ref #9607 replacing class local factory methods and heplers in CdmImageInfo by servic...
authorAndreas Kohlbecker <a.kohlbecker@bgbm.org>
Thu, 6 May 2021 12:25:46 +0000 (14:25 +0200)
committerAndreas Kohlbecker <a.kohlbecker@bgbm.org>
Thu, 6 May 2021 12:53:44 +0000 (14:53 +0200)
16 files changed:
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/UriUtils.java
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/media/CdmImageInfo.java
cdmlib-ext/src/main/java/eu/etaxonomy/cdm/ext/occurrence/gbif/GbifJsonOccurrenceParser.java
cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/common/CdmImportBase.java
cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/media/in/MediaExcelImport.java
cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/sdd/in/SDDImport.java
cdmlib-remote/src/main/java/eu/etaxonomy/cdm/remote/controller/MediaController.java
cdmlib-remote/src/main/java/eu/etaxonomy/cdm/remote/controller/iiif/ManifestComposer.java
cdmlib-remote/src/main/java/eu/etaxonomy/cdm/remote/controller/iiif/ManifestController.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/application/CdmApplicationController.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/application/CdmRepository.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/application/ICdmRepository.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/MediaServiceImpl.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/media/MediaInfoFactory.java [new file with mode: 0644]
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/media/MediaMedadataFileReader.java [new file with mode: 0644]
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/media/SearchReplace.java

index fa33a4277ca1386c2793b4f8bc36eb1ab18ecd4e..949567b31c530c15427d1e5615dd5725da3d54c2 100644 (file)
@@ -118,7 +118,7 @@ public class UriUtils {
         if(! uri.isAbsolute()){
                throw new IOException(URI_IS_NOT_ABSOLUTE);
         }else if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())){
-            HttpResponse response = UriUtils.getResponse(uri, requestHeaders);
+            HttpResponse response = UriUtils.getResponse(uri, requestHeaders); // FIXME must use HTTP HEAD!
             if(UriUtils.isOk(response)){
                 Header[] contentLengths = response.getHeaders("Content-Length");
 
index f952917ddf8d954a6b456cf1ad7de79dd6b3de84..c6a6a69d80903dd1e57f94632f757a6b177be5f3 100644 (file)
@@ -8,25 +8,11 @@
 */
 package eu.etaxonomy.cdm.common.media;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.commons.imaging.ImageInfo;
-import org.apache.commons.imaging.ImageReadException;
-import org.apache.commons.imaging.Imaging;
-import org.apache.commons.imaging.common.GenericImageMetadata.GenericImageMetadataItem;
-import org.apache.commons.imaging.common.ImageMetadata;
-import org.apache.commons.imaging.common.ImageMetadata.ImageMetadataItem;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.http.HttpException;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.log4j.Logger;
-
-import eu.etaxonomy.cdm.common.CdmUtils;
 import eu.etaxonomy.cdm.common.URI;
-import eu.etaxonomy.cdm.common.UriUtils;
+
 
 /**
  * @author k.luther
@@ -34,36 +20,18 @@ import eu.etaxonomy.cdm.common.UriUtils;
  * @since 27.11.2009
  */
 public  class CdmImageInfo extends MediaInfo {
-       private static Logger logger = Logger.getLogger(CdmImageInfo.class);
 
        private int width;
        private int height;
        private int bitPerPixel;
        private final URI imageUri;
 
-       private Map<String, String> metaData;
-
-//********************** Factory Methods ******************************/
-
-       public static CdmImageInfo NewInstance(URI imageUri, Integer timeOut) throws IOException, HttpException {
-               CdmImageInfo instance = new CdmImageInfo(imageUri);
-               instance.readSuffix();
-               instance.readImageLength();
-               instance.readImageInfo(timeOut);
-               return instance;
-       }
+       private Map<String, String> metaData = new HashMap<>();
 
-       public static CdmImageInfo NewInstanceWithMetaData(URI imageUri, Integer timeOut) throws IOException, HttpException {
-               CdmImageInfo instance = NewInstance(imageUri, timeOut);
-
-               instance.readMetaData(timeOut);
-
-               return instance;
-       }
 
 //*********************** CONSTRUCTOR **************************************/
 
-       private CdmImageInfo(URI imageUri){
+       public CdmImageInfo(URI imageUri){
                this.imageUri = imageUri;
        }
 
@@ -77,7 +45,19 @@ public  class CdmImageInfo extends MediaInfo {
                return width;
        }
 
-       public int getHeight() {
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+       public void setHeight(int height) {
+        this.height = height;
+    }
+
+    public void setBitPerPixel(int bitPerPixel) {
+        this.bitPerPixel = bitPerPixel;
+    }
+
+    public int getHeight() {
                return height;
        }
 
@@ -89,124 +69,6 @@ public  class CdmImageInfo extends MediaInfo {
                return metaData;
        }
 
-//**************************** METHODS *****************************/
-
-       private void readSuffix(){
-               String path = imageUri.getPath();
-
-               String suffix = path.substring(StringUtils.lastIndexOf(path, '.') + 1);
-               setSuffix(suffix);
-       }
-
-       private void readImageLength() throws ClientProtocolException, IOException, HttpException{
-               try {
-            long length = UriUtils.getResourceLength(imageUri, null);
-            setLength(length);
-        } catch (HttpException e) {
-            if (e.getMessage().equals("Could not retrieve Content-Length")){
-                InputStream inputStream = UriUtils.getInputStream(imageUri);
-                int n = 0;
-                while(inputStream.read() != -1){
-                    n++;
-                }
-                inputStream.close();
-                logger.info("Content-Length not available in http header. Image size computed via input stream size: " + imageUri);
-                setLength(n);
-            }else{
-                throw e;
-            }
-        }
-       }
-
-       /**
-        * Reads the image infos (width, height, bitPerPixel, metadata, format, mime type)
-        * @param timeOut
-        * @throws IOException
-        * @throws HttpException
-        */
-       private void readImageInfo(Integer timeOut) throws IOException, HttpException{
-
-               InputStream inputStream;
-               try {
-                       inputStream = UriUtils.getInputStream(imageUri);
-                       ImageInfo imageInfo = Imaging.getImageInfo(inputStream, null);
-
-                       setFormatName(imageInfo.getFormatName());
-                       setMimeType(imageInfo.getMimeType());
-                       width = imageInfo.getWidth();
-                       height = imageInfo.getHeight();
-                       bitPerPixel = imageInfo.getBitsPerPixel();
-                       inputStream.close();
-
-               } catch (ImageReadException e) {
-                       logger.error("Could not read: " + imageUri + ". " + e.getMessage());
-                       throw new IOException(e);
-               }
-       }
-
-
-       /**
-        * @param timeOut TODO is not yet used
-        * @return
-        * @throws IOException
-        * @throws HttpException
-        */
-       public Map<String, String> readMetaData(Integer timeOut) throws IOException, HttpException {
-
-               try {
-                       InputStream inputStream = UriUtils.getInputStream(imageUri);
-
-                       ImageMetadata mediaData = Imaging.getMetadata(inputStream, null);
-
-                       if (mediaData != null){
-                               metaData = new HashMap<>();
-                               for (ImageMetadataItem imItem : mediaData.getItems()){
-                                       if (imItem instanceof GenericImageMetadataItem){
-                                           GenericImageMetadataItem item = (GenericImageMetadataItem)imItem;
-                                           if ("Keywords".equals(item.getKeyword())){
-                                               String value = text(item);
-                                               String[] splits = value.split(":");
-                               if (splits.length == 2){
-                                   //convention used e.g. for Flora of cyprus (#9137)
-                                   metaData.put(splits[0].trim(), splits[1].trim());
-                               }else{
-                                   metaData.put(item.getKeyword(), CdmUtils.concat("; ", metaData.get(item.getKeyword()), value));
-                               }
-                                           }else if (item.getKeyword().contains("/")){
-                               //TODO: not sure where this syntax is used originally
-                                               String key = item.getKeyword();
-                                               //key.replace("/", "");
-                                               int index = key.indexOf("/");
-                                               key = key.substring(0, index);
-                                               metaData.put(key, text(item));
-                                           }else{
-                                               metaData.put(item.getKeyword(), text(item));
-                                           }
-                                       }
-                               }
-                       }
-               }catch (ImageReadException e) {
-                       logger.error("Could not read: " + imageUri + ". " + e.getMessage());
-                       //throw new IOException(e);
-               }
-               return metaData;
-       }
-
-    /**
-     * Wrapper for the Item.getText() method which applies cleaning of the text representation.
-     *
-     * <ol>
-     * <li>Strings are surrounded by single quotes, these must be removed</li>
-     * </ol>
-     * @param item
-     */
-    private String text(GenericImageMetadataItem item) {
-        String  text = item.getText();
-        if(text.startsWith("'") && text.endsWith("'")) {
-            text = text.substring(1 , text.length() - 1);
-        }
-        return text;
-    }
 
 // ******************* TO STRING **********************************/
 
index 95ef7284598317ed78adaa79e429d65b16436602..a5cc630a5686bc960dbf2a7438f18fa7460180df 100644 (file)
@@ -24,6 +24,7 @@ import org.apache.http.HttpException;
 import org.apache.log4j.Logger;
 
 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
+import eu.etaxonomy.cdm.api.service.media.MediaMedadataFileReader;
 import eu.etaxonomy.cdm.common.URI;
 import eu.etaxonomy.cdm.common.UriUtils;
 import eu.etaxonomy.cdm.common.media.CdmImageInfo;
@@ -426,7 +427,11 @@ public class GbifJsonOccurrenceParser {
                             if (mediaRecord.has("identifier")){
                                 try {
                                     uri = new URI(mediaRecord.getString("identifier"));
-                                    imageInf = CdmImageInfo.NewInstance(uri, 0);
+                                    imageInf = new MediaMedadataFileReader(uri)
+                                        .readSuffix()
+                                        .readImageLength()
+                                        .readImageInfo()
+                                        .getCdmImageInfo();
                                 } catch (URISyntaxException |IOException | HttpException e) {
                                     e.printStackTrace();
                                 }
@@ -454,7 +459,7 @@ public class GbifJsonOccurrenceParser {
                         derivedUnitFacade.addDerivedUnitMedia(media);
                     }
                     //identifier=http://ww2.bgbm.org/herbarium/images/B/-W/08/53/B_-W_08537%20-00%201__3.jpg
-                   //references=http://ww2.bgbm.org/herbarium/view_biocase.cfm?SpecimenPK=136628
+                    //references=http://ww2.bgbm.org/herbarium/view_biocase.cfm?SpecimenPK=136628
                     //format=image/jpeg
                     //type=StillImage
                 }
index 6b3820a8ebde584dab24cc75dd4873cac16b73a1..b58e6f0dc3cf5c0cd530b7cbd0ba02278b2d0008 100644 (file)
@@ -1430,7 +1430,7 @@ public abstract class CdmImportBase<CONFIG extends IImportConfigurator, STATE ex
         try {
                if (readMediaData){
                        logger.info("Read media data from: " + uri);
-                       cdmImageInfo = CdmImageInfo.NewInstance(uri, 0);
+                       cdmImageInfo = getMediaInfoFactory().cdmImageInfo(uri);
                }
         } catch (Exception e) {
                String message = "An error occurred when trying to read image meta data for " + uri.toString() + ": " +  e.getMessage();
index 48418639c739f29a90565da9d0fafe9dd35f8d44..60d8a9a605e9267d71cc5c131e96358c4f117544 100644 (file)
@@ -8,7 +8,6 @@
 */
 package eu.etaxonomy.cdm.io.media.in;
 
-import eu.etaxonomy.cdm.common.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -22,6 +21,7 @@ import org.joda.time.DateTimeFieldType;
 import org.joda.time.Partial;
 import org.springframework.stereotype.Component;
 
+import eu.etaxonomy.cdm.common.URI;
 import eu.etaxonomy.cdm.common.media.CdmImageInfo;
 import eu.etaxonomy.cdm.io.common.utils.ImportDeduplicationHelper;
 import eu.etaxonomy.cdm.io.excel.common.ExcelImportBase;
@@ -226,7 +226,7 @@ public class MediaExcelImport
             CdmImageInfo cdmImageInfo = null;
             try {
                 if (state.getConfig().isReadMediaData()){
-                    cdmImageInfo = CdmImageInfo.NewInstance(uri, 0);
+                    cdmImageInfo = getMediaInfoFactory().cdmImageInfo(uri);
                 }
             } catch (Exception e) {
                 String message = "An error occurred when trying to read image meta data for %s. Image was created but without metadata.";
index a0587e815de3102a3abd0dbf90208a9c3662c971..299819e1c80e03be706acb2546912d0ec7a0bc2e 100644 (file)
@@ -1599,7 +1599,7 @@ public class SDDImport extends XmlImportBase<SDDImportConfigurator, SDDImportSta
                                                        try{\r
                                                                URL url = new URL(href);\r
 \r
-                                                               imageMetaData = CdmImageInfo.NewInstance(URI.fromUrl(url), 0);\r
+                                                               imageMetaData = getMediaInfoFactory().cdmImageInfo(URI.fromUrl(url));\r
                                                                image = ImageFile.NewInstance(URI.fromUrl(url), null, imageMetaData);\r
                                                        } catch (MalformedURLException e) {\r
                                                                logger.error("Malformed URL", e);\r
@@ -1612,7 +1612,7 @@ public class SDDImport extends XmlImportBase<SDDImportConfigurator, SDDImportSta
                                                        File parent = f.getParentFile();\r
                                                        String fi = parent.toString() + File.separator + href;\r
                                                        File file = new File(fi);\r
-                                                       imageMetaData = CdmImageInfo.NewInstance(new URI(fi), 0); //file\r
+                                                       imageMetaData = getMediaInfoFactory().cdmImageInfo(new URI(fi)); //file\r
                                                        image = ImageFile.NewInstance(URI.fromFile(file), null, imageMetaData);\r
                                                }\r
                                                MediaRepresentation representation = MediaRepresentation.NewInstance(imageMetaData.getMimeType(), null);\r
index 94e049ab7643d2b6ae41fda18d1ab005dba6b18d..add8c8d53f0e3e588d476f97c16e649ae9ecf434 100644 (file)
@@ -29,7 +29,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;\r
 \r
 import eu.etaxonomy.cdm.api.service.IMediaService;\r
-import eu.etaxonomy.cdm.api.service.MediaServiceImpl;\r
+import eu.etaxonomy.cdm.api.service.media.MediaInfoFactory;\r
 import eu.etaxonomy.cdm.common.URI;\r
 import eu.etaxonomy.cdm.common.media.CdmImageInfo;\r
 import eu.etaxonomy.cdm.model.media.Media;\r
@@ -50,6 +50,10 @@ public class MediaController extends AbstractIdentifiableController<Media, IMedi
 \r
     private static final Logger logger = Logger.getLogger(MediaController.class);\r
 \r
+\r
+    @Autowired\r
+    private MediaInfoFactory mediaInfoFactory;\r
+\r
     @Autowired\r
     @Override\r
     public void setService(IMediaService service) {\r
@@ -85,7 +89,7 @@ public class MediaController extends AbstractIdentifiableController<Media, IMedi
 \r
                 } else {\r
                     uri = mediaRepresentation.getParts().get(0).getUri();\r
-                    CdmImageInfo cdmImageInfo = CdmImageInfo.NewInstanceWithMetaData(uri, MediaServiceImpl.IMAGE_READ_TIMEOUT);\r
+                    CdmImageInfo cdmImageInfo = mediaInfoFactory.cdmImageInfoWithMetaData(uri);\r
                     result = cdmImageInfo.getMetaData();\r
                 }\r
             } catch (IOException | HttpException e) {\r
index fb6e99312d4cd21b957660fa80181bdf5655eee8..a55c513896ae2cd51d7776924004f39c6adec73b 100644 (file)
@@ -30,8 +30,8 @@ import de.digitalcollections.iiif.model.sharedcanvas.Manifest;
 import de.digitalcollections.iiif.model.sharedcanvas.Resource;
 import de.digitalcollections.iiif.model.sharedcanvas.Sequence;
 import eu.etaxonomy.cdm.api.service.IMediaService;
-import eu.etaxonomy.cdm.api.service.MediaServiceImpl;
 import eu.etaxonomy.cdm.api.service.l10n.LocaleContext;
+import eu.etaxonomy.cdm.api.service.media.MediaInfoFactory;
 import eu.etaxonomy.cdm.common.media.CdmImageInfo;
 import eu.etaxonomy.cdm.model.common.Credit;
 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
@@ -70,6 +70,8 @@ public class ManifestComposer {
 
     private IMediaService mediaService;
 
+    private MediaInfoFactory mediaInfoFactory; // FIXME define and use interface
+
     private String iiifIdPrefix;
 
     private String[] thumbnailMimetypes = new String[] {"image/.*", ".*"};
@@ -126,10 +128,11 @@ public class ManifestComposer {
     }
 
 
-    public ManifestComposer(String iiifIdPrefix, IMediaToolbox mediaTools, IMediaService mediaService) {
+    public ManifestComposer(String iiifIdPrefix, IMediaToolbox mediaTools, IMediaService mediaService, MediaInfoFactory mediaInfoFactory) {
         this.mediaTools = mediaTools;
         this.iiifIdPrefix = iiifIdPrefix;
         this.mediaService = mediaService;
+        this.mediaInfoFactory = mediaInfoFactory;
     }
 
     <T extends IdentifiableEntity> Manifest manifestFor(EntityMediaContext<T> entityMediaContext, String onEntitiyType, String onEntityUuid) throws IOException {
@@ -376,7 +379,8 @@ public class ManifestComposer {
     }
 
     /**
-     * @deprecated unused as media metadata is now read via the mediaService, see
+     * @deprecated unused as media metadata is now read via the mediaService,
+     *  see {@link IMediaService#readResourceMetadataFiltered(MediaRepresentation)}
      */
     @Deprecated
     private List<MetadataEntry> mediaRepresentationMetaData(MediaRepresentation representation) {
@@ -392,7 +396,7 @@ public class ManifestComposer {
             }
             if (part.getUri() != null) {
                 try {
-                    CdmImageInfo cdmImageInfo = CdmImageInfo.NewInstanceWithMetaData(part.getUri(), MediaServiceImpl.IMAGE_READ_TIMEOUT);
+                    CdmImageInfo cdmImageInfo = mediaInfoFactory.cdmImageInfoWithMetaData(part.getUri());
                     Map<String, String> result = cdmImageInfo.getMetaData();
                     if(result != null){
                         for (String key : result.keySet()) {
index 05fdebc884656778552e12a14fcd77e4e66be3df..89c9c4972daa4b5ac08cbfe4217c2c9f7dfb6fb3 100644 (file)
@@ -32,6 +32,7 @@ import de.digitalcollections.iiif.model.jackson.IiifObjectMapper;
 import de.digitalcollections.iiif.model.sharedcanvas.Manifest;
 import eu.etaxonomy.cdm.api.service.IMediaService;
 import eu.etaxonomy.cdm.api.service.ITermService;
+import eu.etaxonomy.cdm.api.service.media.MediaInfoFactory;
 import eu.etaxonomy.cdm.api.util.TaxonRelationshipEdge;
 import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
 import eu.etaxonomy.cdm.model.taxon.Taxon;
@@ -86,6 +87,9 @@ public class ManifestController {
     @Autowired
     private IMediaService mediaService;
 
+    @Autowired
+    private MediaInfoFactory mediaInfoFactory;
+
     @RequestMapping(
             value = {"taxon/{uuid}/manifest"},
             method = RequestMethod.GET)
@@ -116,7 +120,7 @@ public class ManifestController {
                         taxonPortalController.addTaxonomicChildrenMedia(includeTaxonDescriptions, includeOccurrences, includeTaxonNameDescriptions, entityMediaContext.getEntity(), includeRelationships, entityMediaContext.getMedia())
                                 );
             }
-            ManifestComposer manifestFactory = new ManifestComposer(HTTP_IIIF_CYBERTAXONOMY_ORG, mediaTools, mediaService);
+            ManifestComposer manifestFactory = new ManifestComposer(HTTP_IIIF_CYBERTAXONOMY_ORG, mediaTools, mediaService, mediaInfoFactory);
             manifestFactory.setDoJoinAttributions(true);
             manifestFactory.setUseThumbnailDimensionsForCanvas(true);
             return serializeManifest(manifestFactory.manifestFor(entityMediaContext, "taxon", uuid.toString()));
index 6c1421fe2b74c1f3c8c9452fb50193e9592e51e6..a8512e6e493aa4409fba5eb3214210935502ef00 100644 (file)
@@ -68,6 +68,7 @@ import eu.etaxonomy.cdm.api.service.ITermTreeService;
 import eu.etaxonomy.cdm.api.service.IUserService;\r
 import eu.etaxonomy.cdm.api.service.IVocabularyService;\r
 import eu.etaxonomy.cdm.api.service.longrunningService.ILongRunningTasksService;\r
+import eu.etaxonomy.cdm.api.service.media.MediaInfoFactory;\r
 import eu.etaxonomy.cdm.api.service.molecular.IAmplificationService;\r
 import eu.etaxonomy.cdm.api.service.molecular.IPrimerService;\r
 import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;\r
@@ -768,23 +769,20 @@ public class CdmApplicationController implements ICdmRepository {
         return;\r
     }\r
 \r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
     @Override\r
     public IRightsService getRightsService() {\r
         return configuration.getRightsService();\r
     }\r
 \r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
     @Override\r
     public IRegistrationService getRegistrationService() {\r
         return configuration.getRegistrationService();\r
     }\r
 \r
+    @Override\r
+    public MediaInfoFactory getMediaInfoFactory() {\r
+        return configuration.getMediaInfoFactory();\r
+    }\r
+\r
 }\r
 \r
index 135de30c8b18ab5c5929a6b37a9211f409fde4fc..1fd6f0e03b2efcf726d6b7171ba68c37d2a4ac75 100644 (file)
@@ -69,6 +69,7 @@ import eu.etaxonomy.cdm.api.service.ITermTreeService;
 import eu.etaxonomy.cdm.api.service.IUserService;
 import eu.etaxonomy.cdm.api.service.IVocabularyService;
 import eu.etaxonomy.cdm.api.service.longrunningService.ILongRunningTasksService;
+import eu.etaxonomy.cdm.api.service.media.MediaInfoFactory;
 import eu.etaxonomy.cdm.api.service.molecular.IAmplificationService;
 import eu.etaxonomy.cdm.api.service.molecular.IPrimerService;
 import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
@@ -194,6 +195,8 @@ public class CdmRepository implements ICdmRepository, ApplicationContextAware {
        private IEntityConstraintViolationService entityConstraintViolationService;
        @Autowired
        private ICdmPermissionEvaluator permissionEvaluator;
+    @Autowired
+    private MediaInfoFactory mediaInfoFactory; // FIXME define and use interface
        @Autowired
     private SessionFactory factory;
 
@@ -438,6 +441,10 @@ public class CdmRepository implements ICdmRepository, ApplicationContextAware {
                return permissionEvaluator;
        }
 
+    public MediaInfoFactory getMediaInfoFactory() { // FIXME define and use interface
+        return mediaInfoFactory;
+    }
+
        @Override
        public TransactionStatus startTransaction(){
                return startTransaction(false);
@@ -530,4 +537,5 @@ public class CdmRepository implements ICdmRepository, ApplicationContextAware {
             // no current session: nothing to clear!
         }
     }
+
 }
index 05de2fdd4f27e4fb149812b1bffb6dab76ab1f01..415b9d78282516ddd00892b9bdbfcb27249006a5 100644 (file)
@@ -50,6 +50,7 @@ import eu.etaxonomy.cdm.api.service.ITermTreeService;
 import eu.etaxonomy.cdm.api.service.IUserService;
 import eu.etaxonomy.cdm.api.service.IVocabularyService;
 import eu.etaxonomy.cdm.api.service.longrunningService.ILongRunningTasksService;
+import eu.etaxonomy.cdm.api.service.media.MediaInfoFactory;
 import eu.etaxonomy.cdm.api.service.molecular.IAmplificationService;
 import eu.etaxonomy.cdm.api.service.molecular.IPrimerService;
 import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
@@ -177,6 +178,8 @@ public interface ICdmRepository{
         */
        public ICdmPermissionEvaluator getPermissionEvaluator();
 
+       public MediaInfoFactory getMediaInfoFactory(); // FIXME define and user interface
+
        void authenticate(String username, String password);
 
 }
index 042d8f71092eec6551ff5e6419c5719f18199b9e..a19c42155e751581d072f9962b3836bf258d2fe8 100644 (file)
@@ -27,6 +27,7 @@ import org.springframework.transaction.annotation.Transactional;
 import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;\r
 import eu.etaxonomy.cdm.api.service.config.MediaDeletionConfigurator;\r
 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;\r
+import eu.etaxonomy.cdm.api.service.media.MediaInfoFactory;\r
 import eu.etaxonomy.cdm.api.service.pager.Pager;\r
 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;\r
 import eu.etaxonomy.cdm.common.media.CdmImageInfo;\r
@@ -60,8 +61,6 @@ import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
 @Transactional(readOnly=true)\r
 public class MediaServiceImpl extends IdentifiableServiceBase<Media,IMediaDao> implements IMediaService {\r
 \r
-    public static final Integer IMAGE_READ_TIMEOUT = 3000;\r
-\r
     @Override\r
     @Autowired\r
        protected void setDao(IMediaDao dao) {\r
@@ -76,6 +75,8 @@ public class MediaServiceImpl extends IdentifiableServiceBase<Media,IMediaDao> i
     private INameService nameService;\r
        @Autowired\r
        private IPreferenceService prefsService;\r
+    @Autowired\r
+    private MediaInfoFactory mediaInfoFactory; // FIXME define and use interface\r
 \r
 \r
        @Override\r
@@ -294,7 +295,7 @@ public class MediaServiceImpl extends IdentifiableServiceBase<Media,IMediaDao> i
         Map<String, String> metadata = new HashMap<>();\r
 \r
         for(MediaRepresentationPart part : representation.getParts()) {\r
-            CdmImageInfo iInfo = CdmImageInfo.NewInstanceWithMetaData(part.getUri(), IMAGE_READ_TIMEOUT);\r
+            CdmImageInfo iInfo =  mediaInfoFactory.cdmImageInfoWithMetaData(part.getUri());\r
             if(iInfo.getMetaData() != null) {\r
                 metadata.putAll(iInfo.getMetaData());\r
             }\r
diff --git a/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/media/MediaInfoFactory.java b/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/media/MediaInfoFactory.java
new file mode 100644 (file)
index 0000000..cc6e3c1
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+* Copyright (C) 2021 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.cdm.api.service.media;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.HttpException;
+import org.springframework.stereotype.Component;
+
+import eu.etaxonomy.cdm.common.URI;
+import eu.etaxonomy.cdm.common.media.CdmImageInfo;
+
+/**
+ * @author a.kohlbecker
+ * @since May 5, 2021
+ */
+@Component
+public class MediaInfoFactory {
+
+    /**
+     * TODO needs to be managed in CDM PREFERENCES
+     */
+    private List<MediaUriTransformation> uriTransformations = new ArrayList<>();
+
+    /**
+     * TODO needs to be managed in CDM PREFERENCES
+     *
+     * Transformation from
+     * https://pictures.bgbm.org/digilib/Scaler?fn=Cyprus/Sisymbrium_aegyptiacum_C1.jpg&mo=file
+     *  to
+     * https://image.bgbm.org/metadata/info?file=Cyprus/Sisymbrium_aegyptiacum_C1.jpg
+     */
+    private MediaUriTransformation bgbmMediaMetadataService() {
+        MediaUriTransformation mut = new MediaUriTransformation();
+        mut.setHost(new SearchReplace("pictures.bgbm.org", "image.bgbm.org"));
+        mut.setPathQueryFragment(new SearchReplace("(digilib\\/Scaler\\?fn=)([^&]+)(&mo=file)", "file=$2"));
+        return mut;
+    }
+
+    public MediaInfoFactory() {
+        uriTransformations.add(bgbmMediaMetadataService());
+    }
+
+    public CdmImageInfo cdmImageInfoWithMetaData(URI imageUri) throws IOException, HttpException {
+        return new MediaMedadataFileReader(imageUri)
+               .readSuffix()
+               .readImageLength()
+               .readImageInfo()
+               .readMetaData()
+               .getCdmImageInfo();
+
+    }
+
+    public CdmImageInfo cdmImageInfo(URI imageUri) throws IOException, HttpException {
+        return new MediaMedadataFileReader(imageUri)
+                .readSuffix()
+                .readImageLength()
+                .readImageInfo()
+                .getCdmImageInfo();
+
+    }
+
+
+
+
+
+}
diff --git a/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/media/MediaMedadataFileReader.java b/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/media/MediaMedadataFileReader.java
new file mode 100644 (file)
index 0000000..1693158
--- /dev/null
@@ -0,0 +1,176 @@
+/**
+* Copyright (C) 2021 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.cdm.api.service.media;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.imaging.ImageInfo;
+import org.apache.commons.imaging.ImageReadException;
+import org.apache.commons.imaging.Imaging;
+import org.apache.commons.imaging.common.GenericImageMetadata.GenericImageMetadataItem;
+import org.apache.commons.imaging.common.ImageMetadata;
+import org.apache.commons.imaging.common.ImageMetadata.ImageMetadataItem;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpException;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.log4j.Logger;
+
+import eu.etaxonomy.cdm.common.CdmUtils;
+import eu.etaxonomy.cdm.common.URI;
+import eu.etaxonomy.cdm.common.UriUtils;
+import eu.etaxonomy.cdm.common.media.CdmImageInfo;
+
+/**
+ *  TODO make use of timeOut ?
+ *
+ * @author a.kohlbecker
+ * @since May 6, 2021
+ */
+public class MediaMedadataFileReader {
+
+
+    private static Logger logger = Logger.getLogger(MediaMedadataFileReader.class);
+
+    private CdmImageInfo cdmImageInfo;
+
+    public static final Integer IMAGE_READ_TIMEOUT = 3000; // ms
+
+    private Integer timeOut = IMAGE_READ_TIMEOUT; // setting default
+
+    public MediaMedadataFileReader(CdmImageInfo cdmImageInfo) {
+        this.cdmImageInfo = cdmImageInfo;
+    }
+
+    public MediaMedadataFileReader(URI uri) {
+        this.cdmImageInfo = new CdmImageInfo(uri);
+    }
+
+    /**
+     * Reads the image info (width, height, bitPerPixel, metadata, format, mime type)
+     */
+    public MediaMedadataFileReader readImageInfo() throws IOException, HttpException{
+
+        InputStream inputStream;
+        try {
+            inputStream = UriUtils.getInputStream(cdmImageInfo.getUri());
+            ImageInfo imageInfo = Imaging.getImageInfo(inputStream, null);
+
+            cdmImageInfo.setFormatName(imageInfo.getFormatName());
+            cdmImageInfo.setMimeType(imageInfo.getMimeType());
+            cdmImageInfo.setWidth(imageInfo.getWidth());
+            cdmImageInfo.setHeight(imageInfo.getHeight());
+            cdmImageInfo.setBitPerPixel(imageInfo.getBitsPerPixel());
+            inputStream.close();
+
+        } catch (ImageReadException e) {
+            logger.error("Could not read: " + cdmImageInfo.getUri() + ". " + e.getMessage());
+            throw new IOException(e);
+        }
+
+        return this;
+    }
+
+    public MediaMedadataFileReader readMetaData() throws IOException, HttpException {
+
+        ImageMetadata mediaData = null;
+        try {
+            InputStream inputStream = UriUtils.getInputStream(cdmImageInfo.getUri());
+            mediaData = Imaging.getMetadata(inputStream, null);
+        }catch (ImageReadException e) {
+            logger.error("Could not read: " + cdmImageInfo.getUri() + ". " + e.getMessage());
+            //throw new IOException(e);
+        }
+
+        if(mediaData != null) {
+            for (ImageMetadataItem imItem : mediaData.getItems()){
+                if (imItem instanceof GenericImageMetadataItem){
+                    GenericImageMetadataItem item = (GenericImageMetadataItem)imItem;
+                    if ("Keywords".equals(item.getKeyword())){
+                        String value = text(item);
+                        String[] splits = value.split(":");
+                        if (splits.length == 2){
+                            //convention used e.g. for Flora of cyprus (#9137)
+                            cdmImageInfo.getMetaData().put(splits[0].trim(), splits[1].trim());
+                        }else{
+                            cdmImageInfo.getMetaData().put(
+                                    item.getKeyword(),
+                                    CdmUtils.concat("; ", cdmImageInfo.getMetaData().get(item.getKeyword()), value)
+                                    );
+                        }
+                    }else if (item.getKeyword().contains("/")){
+                        //TODO: not sure where this syntax is used originally
+                        String key = item.getKeyword();
+                        //key.replace("/", "");
+                        int index = key.indexOf("/");
+                        key = key.substring(0, index);
+                        cdmImageInfo.getMetaData().put(key, text(item));
+                    }else{
+                        cdmImageInfo.getMetaData().put(item.getKeyword(), text(item));
+                    }
+                }
+            }
+        }
+
+        return this;
+    }
+
+    /**
+     * Reads the size of the image defined by the {@link #imageUri} in bytes
+     */
+    public MediaMedadataFileReader readImageLength() throws ClientProtocolException, IOException, HttpException{
+        try {
+            long length = UriUtils.getResourceLength(cdmImageInfo.getUri(), null);
+            cdmImageInfo.setLength(length);
+        } catch (HttpException e) {
+            if (e.getMessage().equals("Could not retrieve Content-Length")){
+                InputStream inputStream = UriUtils.getInputStream(cdmImageInfo.getUri());
+                int n = 0;
+                while(inputStream.read() != -1){
+                    n++;
+                }
+                inputStream.close();
+                logger.info("Content-Length not available in http header. Image size computed via input stream size: " + cdmImageInfo.getUri());
+                cdmImageInfo.setLength(n);
+            }else{
+                throw e;
+            }
+        }
+        return this;
+    }
+
+    public MediaMedadataFileReader readSuffix(){
+        String path = cdmImageInfo.getUri().getPath();
+        String suffix = path.substring(StringUtils.lastIndexOf(path, '.') + 1);
+        cdmImageInfo.setSuffix(suffix);
+        return this;
+    }
+
+    public CdmImageInfo getCdmImageInfo() {
+        return cdmImageInfo;
+    }
+
+    /**
+     * Wrapper for the Item.getText() method which applies cleaning of the text representation.
+     * <ol>
+     * <li>Strings are surrounded by single quotes, these must be removed</li>
+     * </ol>
+     * @param item
+     */
+    private String text(GenericImageMetadataItem item) {
+        String  text = item.getText();
+        if(text.startsWith("'") && text.endsWith("'")) {
+            text = text.substring(1 , text.length() - 1);
+        }
+        return text;
+    }
+
+
+
+}
index 1bf5d9b1474dbd31f16cf5422444ca0b4a6d27cd..97bdb63391b7739bfb4875a4cdbab33689889f0a 100644 (file)
@@ -13,8 +13,8 @@ import java.util.regex.Pattern;
 /**
  * Defines a search and replace operation for the
  * {@link MediaUriTransformation}. The search pattern is a regular expression.
- * Internally the search regex is compiled into a caches regex pattern when it
- * is requested by calling {@link #searchPattern}.
+ * Internally the search regex is compiled into a cached regex pattern when it
+ * is requested by calling {@link #searchPattern}.
  * <p>
  * <b>CHANGING THIS CLASS MAY BREAK DESERIALIZATION OF EXISTING CDM PREFERENCES.</b>
  *
@@ -49,7 +49,6 @@ public class SearchReplace {
     }
 
     /**
-     *
      * @return The replacement string
      */
     public String getReplace() {