Project

General

Profile

Download (16.5 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2007 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9

    
10
package eu.etaxonomy.cdm.api.service;
11

    
12
import java.io.IOException;
13
import java.util.ArrayList;
14
import java.util.Arrays;
15
import java.util.HashMap;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.Set;
19
import java.util.UUID;
20
import java.util.stream.Collectors;
21

    
22
import org.apache.http.HttpException;
23
import org.springframework.beans.factory.annotation.Autowired;
24
import org.springframework.stereotype.Service;
25
import org.springframework.transaction.annotation.Transactional;
26

    
27
import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
28
import eu.etaxonomy.cdm.api.service.config.MediaDeletionConfigurator;
29
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
30
import eu.etaxonomy.cdm.api.service.media.MediaInfoFactory;
31
import eu.etaxonomy.cdm.api.service.pager.Pager;
32
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
33
import eu.etaxonomy.cdm.common.media.CdmImageInfo;
34
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
35
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
36
import eu.etaxonomy.cdm.model.common.CdmBase;
37
import eu.etaxonomy.cdm.model.common.ICdmBase;
38
import eu.etaxonomy.cdm.model.description.DescriptionBase;
39
import eu.etaxonomy.cdm.model.description.IDescribable;
40
import eu.etaxonomy.cdm.model.description.MediaKey;
41
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
42
import eu.etaxonomy.cdm.model.description.TaxonDescription;
43
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
44
import eu.etaxonomy.cdm.model.description.TextData;
45
import eu.etaxonomy.cdm.model.location.NamedArea;
46
import eu.etaxonomy.cdm.model.media.Media;
47
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
48
import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
49
import eu.etaxonomy.cdm.model.media.Rights;
50
import eu.etaxonomy.cdm.model.metadata.CdmPreference;
51
import eu.etaxonomy.cdm.model.metadata.PreferencePredicate;
52
import eu.etaxonomy.cdm.model.metadata.PreferenceSubject;
53
import eu.etaxonomy.cdm.model.name.TaxonName;
54
import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;
55
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
56
import eu.etaxonomy.cdm.model.taxon.Taxon;
57
import eu.etaxonomy.cdm.persistence.dao.media.IMediaDao;
58
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
59

    
60
@Service
61
@Transactional(readOnly=true)
62
public class MediaServiceImpl extends IdentifiableServiceBase<Media,IMediaDao> implements IMediaService {
63

    
64
    @Override
65
    @Autowired
66
	protected void setDao(IMediaDao dao) {
67
		this.dao = dao;
68
	}
69

    
70
	@Autowired
71
    private IOccurrenceService specimenService;
72
	@Autowired
73
    private ITaxonService taxonService;
74
	@Autowired
75
    private INameService nameService;
76
	@Autowired
77
	private IPreferenceService prefsService;
78
    @Autowired
79
    private MediaInfoFactory mediaInfoFactory; // FIXME define and use interface
80

    
81

    
82
	@Override
83
    public Pager<MediaKey> getMediaKeys(Set<Taxon> taxonomicScope, Set<NamedArea> geoScopes, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
84
        long numberOfResults = dao.countMediaKeys(taxonomicScope, geoScopes);
85

    
86
		List<MediaKey> results = new ArrayList<>();
87
		if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
88
			results = dao.getMediaKeys(taxonomicScope, geoScopes, pageSize, pageNumber, propertyPaths);
89
		}
90

    
91
		return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
92
	}
93

    
94
	@Override
95
    public Pager<Rights> getRights(Media t, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
96
        long numberOfResults = dao.countRights(t);
97

    
98
		List<Rights> results = new ArrayList<>();
99
		if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
100
			results = dao.getRights(t, pageSize, pageNumber,propertyPaths);
101
		}
102

    
103
		return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
104
	}
105

    
106
	@Override
107
	@Transactional(readOnly = false)
108
    public UpdateResult updateCaches(Class<? extends Media> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Media> cacheStrategy, IProgressMonitor monitor) {
109
		if (clazz == null){
110
			clazz = Media.class;
111
		}
112
		return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
113
	}
114

    
115
	 @Override
116
	 @Transactional(readOnly=false)
117
	 public DeleteResult delete(Set<UUID> mediaUuids, MediaDeletionConfigurator config) {
118
	     DeleteResult result = new DeleteResult();
119
	     for (UUID uuid:mediaUuids){
120
	         result.includeResult(delete(uuid, config));
121
	     }
122
	     return result;
123

    
124
	 }
125

    
126
    @Override
127
    @Transactional(readOnly=false)
128
    public DeleteResult delete(UUID mediaUuid, MediaDeletionConfigurator config) {
129
        DeleteResult result = new DeleteResult();
130
        Media media = this.load(mediaUuid);
131
        if (media == null){
132
            return result;
133
        }
134
        result = isDeletable(mediaUuid, config);
135
        if (result.isOk()){
136
            Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(media);
137
            for (CdmBase ref: references){
138

    
139
                IDescribable<?> updatedObject = null;
140
                IService<ICdmBase> service = null;
141
                if (ref instanceof TextData){
142

    
143
                    TextData textData = HibernateProxyHelper.deproxy(ref, TextData.class);
144
                    DescriptionBase<?> description = HibernateProxyHelper.deproxy(textData.getInDescription(), DescriptionBase.class);
145

    
146
                    IDescribable<?> objectToUpdate = null;
147
                    boolean deleteIsMatchingInstance = false;
148
                    if (description instanceof TaxonDescription){
149
                        objectToUpdate = ((TaxonDescription)description).getTaxon();
150
                        deleteIsMatchingInstance = config.getDeleteFrom() instanceof Taxon;
151
                        service = (IService)taxonService;
152
                    }else if (description instanceof SpecimenDescription){
153
                        objectToUpdate = ((SpecimenDescription)description).getDescribedSpecimenOrObservation();
154
                        deleteIsMatchingInstance = config.getDeleteFrom() instanceof SpecimenOrObservationBase;
155
                        service = (IService)specimenService;
156
                    }else if (description instanceof TaxonNameDescription){
157
                        objectToUpdate = ((TaxonNameDescription)description).getTaxonName();
158
                        deleteIsMatchingInstance = config.getDeleteFrom() instanceof TaxonName;
159
                        service = (IService)nameService;
160
                    }else{
161
                        throw new RuntimeException("Unsupported DescriptionBase class");
162
                    }
163

    
164
                    if (objectToUpdate == null ){
165
                        continue;
166
                    } else if ( (config.isDeleteFromDescription() && deleteIsMatchingInstance  &&
167
                                   config.getDeleteFrom().getId() == objectToUpdate.getId())
168
                                || config.isDeleteFromEveryWhere()){
169
                        updatedObject = handleDeleteMedia(media, textData, description,
170
                                (IDescribable)objectToUpdate);
171
                    } else {
172
                        // this should not be happen, because it is not deletable. see isDeletable
173
                        result.setAbort();
174
                    }
175

    
176
//                } else if (ref instanceof MediaSpecimen && config.getDeleteFrom().getId() == ref.getId() && config.getDeleteFrom() instanceof MediaSpecimen){
177
//                        MediaSpecimen mediaSpecimen = HibernateProxyHelper.deproxy(ref, MediaSpecimen.class);
178
//                        mediaSpecimen.setMediaSpecimen(null);
179
//                        updatedObject = mediaSpecimen;
180
//                        service = (IService)specimenService;
181
                }else if (ref instanceof MediaRepresentation){
182
                    continue;
183
                }else {
184
                    result.setAbort();
185
                }
186

    
187
                if (updatedObject != null){
188
                    service.update(updatedObject); //service should always be != null if updatedObject != null
189
                    result.addUpdatedObject((CdmBase)updatedObject);
190
                }
191
            }
192
            if (result.isOk()){
193
                dao.delete(media);
194
                result.addDeletedObject(media);
195
            }
196

    
197
        }
198
        return result;
199
    }
200

    
201
    /**
202
     * @param media
203
     * @param textData
204
     * @param desc
205
     * @param taxon
206
     */
207
    private IDescribable<DescriptionBase<?>> handleDeleteMedia(Media media, TextData textData,
208
            DescriptionBase<?> desc, IDescribable<DescriptionBase<?>> describable) {
209
        while(textData.getMedia().contains(media)){
210
            textData.removeMedia(media);
211
        }
212

    
213
        return describable;
214
    }
215

    
216

    
217
    @Override
218
    public DeleteResult isDeletable(UUID mediaUuid, DeleteConfiguratorBase config){
219
        DeleteResult result = new DeleteResult();
220
        Media media = this.load(mediaUuid);
221
        Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(media);
222
        MediaDeletionConfigurator mediaConfig = (MediaDeletionConfigurator)config;
223
        CdmBase deleteFrom = mediaConfig.getDeleteFrom();
224

    
225
        if (mediaConfig.isDeleteFromEveryWhere()){
226
           return result;
227
        }
228
        for (CdmBase ref: references){
229
            String message = null;
230
            if (ref instanceof MediaRepresentation){
231
                continue;
232
            }else if (ref instanceof TextData){
233
                TextData textData = HibernateProxyHelper.deproxy(ref, TextData.class);
234
                DescriptionBase<?> description = HibernateProxyHelper.deproxy(textData.getInDescription(), DescriptionBase.class);
235

    
236
                if (description instanceof TaxonDescription){
237
                    TaxonDescription desc = HibernateProxyHelper.deproxy(description, TaxonDescription.class);
238
                    if (desc.getTaxon() == null || (mediaConfig.isDeleteFromDescription() && (deleteFrom instanceof Taxon && ((Taxon)deleteFrom).getId() == desc.getTaxon().getId()))){
239
                        continue;
240
                    } else{
241
                        message = "The media can't be deleted from the database because it is referenced by a taxon. ("+desc.getTaxon().getTitleCache()+")";
242
                        result.setAbort();
243
                    }
244

    
245
                } else if (description instanceof SpecimenDescription){
246
                    SpecimenDescription desc = HibernateProxyHelper.deproxy(description, SpecimenDescription.class);
247
                    if (desc.getDescribedSpecimenOrObservation() == null || (mediaConfig.isDeleteFromDescription() && (deleteFrom instanceof SpecimenOrObservationBase && ((SpecimenOrObservationBase)deleteFrom).getId() == desc.getDescribedSpecimenOrObservation().getId()))){
248
                        continue;
249
                    } else{
250
                        message = "The media can't be deleted from the database because it is referenced by a specimen or observation. ("+desc.getDescribedSpecimenOrObservation().getTitleCache()+")";
251
                        result.setAbort();
252
                    }
253
                } else if (description instanceof TaxonNameDescription){
254
                    TaxonNameDescription desc = HibernateProxyHelper.deproxy(description, TaxonNameDescription.class);
255
                    if (desc.getTaxonName() == null || (mediaConfig.isDeleteFromDescription() && (deleteFrom instanceof TaxonName && ((TaxonName)deleteFrom).getId() == desc.getTaxonName().getId()))){
256
                        continue;
257
                    } else{
258
                        message = "The media can't be deleted from the database because it is referenced by a scientific name. ("+desc.getTaxonName().getTitleCache()+")";
259
                        result.setAbort();
260
                    }
261
                }
262
            }else if (ref instanceof MediaSpecimen){
263
               message = "The media can't be deleted from the database because it is referenced by a mediaspecimen. ("+((MediaSpecimen)ref).getTitleCache()+")";
264
               result.setAbort();
265
            }else {
266
                message = "The media can't be completely deleted because it is referenced by another " + ref.getUserFriendlyTypeName() ;
267
                result.setAbort();
268
            }
269
            if (message != null){
270
                result.addException(new ReferencedObjectUndeletableException(message));
271
                result.addRelatedObject(ref);
272
            }
273
        }
274

    
275
        return result;
276
    }
277

    
278
    /**
279
     * Reads the metadata as stored in the file or web resource and filters the data by the include and exclude lists of key names
280
     * as stored in the data base properties {@link PreferencePredicate#MediaMetadataKeynameExcludes} and {@link PreferencePredicate#MediaMetadataKeynameExcludes}
281
     * <p>
282
     * Metadata of multiple parts is merged into one common metadata map whereas the later part being read may overwrite data from previous parts.
283
     * The consequences of this can be neglected since we don't expect that multiple parts are actually being used.
284
     *
285
     * @param representation
286
     * @return
287
     * @throws IOException
288
     * @throws HttpException
289
     */
290
    @Override
291
    public Map<String, String> readResourceMetadataFiltered(MediaRepresentation representation) throws IOException, HttpException {
292

    
293
        List<String> includes = mediaMetadataKeyIncludes();
294
        List<String> excludes = mediaMetadataKeyExludes();
295
        Map<String, String> metadata = new HashMap<>();
296

    
297
        for(MediaRepresentationPart part : representation.getParts()) {
298
            CdmImageInfo iInfo =  mediaInfoFactory.cdmImageInfo(part.getUri(), true);
299
            if(iInfo.getMetaData() != null) {
300
                metadata.putAll(iInfo.getMetaData());
301
            }
302
        }
303
        if(logger.isDebugEnabled()) {
304
            logger.debug("meta data as read from all parts: " + metadata.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", ", "{", "}")));
305
        }
306

    
307
        if(!includes.isEmpty()) {
308
            metadata = metadata.entrySet()
309
                    .stream()
310
                    .filter( e -> containsCaseInsensitive(e.getKey(), includes))
311
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
312
            if(logger.isDebugEnabled()) {
313
                logger.debug("meta filtered by includes: " + metadata.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", ", "{", "}")));
314
            }
315
        }
316
        if(!excludes.isEmpty()) {
317
            metadata = metadata.entrySet()
318
                    .stream()
319
                    .filter( e -> !containsCaseInsensitive(e.getKey(), excludes))
320
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
321
            if(logger.isDebugEnabled()) {
322
                logger.debug("meta filtered by excludes: " + metadata.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", ", "{", "}")));
323
            }
324
        }
325

    
326
        if(metadata == null) {
327
            metadata = new HashMap<>();
328
        }
329
        return metadata;
330
    }
331

    
332
    private boolean containsCaseInsensitive(String s, List<String> l){
333
        return l.stream().anyMatch(x -> x.equalsIgnoreCase(s));
334
    }
335

    
336
    protected List<String> mediaMetadataKeyExludes(){
337
        CdmPreference pref = prefsService.findExact(CdmPreference.NewKey(PreferenceSubject.NewDatabaseInstance(), PreferencePredicate.MediaMetadataKeynameExcludes));
338
        if(pref == null) {
339
            return new ArrayList<>();
340
        }
341
        return pref.splitStringListValue();
342
    }
343

    
344
    protected List<String> mediaMetadataKeyIncludes(){
345
        CdmPreference pref = prefsService.findExact(CdmPreference.NewKey(PreferenceSubject.NewDatabaseInstance(), PreferencePredicate.MediaMetadataKeynameIncludes));
346
        if(pref == null) {
347
            return Arrays.asList(PreferencePredicate.MediaMetadataKeynameIncludes.getDefaultValue().toString().split(","));
348
        }
349
        return pref.splitStringListValue();
350
    }
351
}
(68-68/95)