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

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

    
63
    public static final Integer IMAGE_READ_TIMEOUT = 3000;
64

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

    
71
	@Autowired
72
    private IOccurrenceService specimenService;
73
	@Autowired
74
    private ITaxonService taxonService;
75
	@Autowired
76
    private INameService nameService;
77
	@Autowired
78
	private IPreferenceService prefsService;
79

    
80

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

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

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

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

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

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

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

    
114
    @Override
115
    @Transactional(readOnly=false)
116
    public DeleteResult delete(UUID mediaUuid, MediaDeletionConfigurator config) {
117
        DeleteResult result = new DeleteResult();
118
        Media media = this.load(mediaUuid);
119
        if (media == null){
120
            return result;
121
        }
122
        result = isDeletable(mediaUuid, config);
123
        if (result.isOk()){
124
            Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(media);
125
            for (CdmBase ref: references){
126

    
127
                IDescribable<?> updatedObject = null;
128
                IService<ICdmBase> service = null;
129
                if (ref instanceof TextData){
130

    
131
                    TextData textData = HibernateProxyHelper.deproxy(ref, TextData.class);
132
                    DescriptionBase<?> description = HibernateProxyHelper.deproxy(textData.getInDescription(), DescriptionBase.class);
133

    
134
                    IDescribable<?> objectToUpdate = null;
135
                    boolean deleteIsMatchingInstance = false;
136
                    if (description instanceof TaxonDescription){
137
                        objectToUpdate = ((TaxonDescription)description).getTaxon();
138
                        deleteIsMatchingInstance = config.getDeleteFrom() instanceof Taxon;
139
                        service = (IService)taxonService;
140
                    }else if (description instanceof SpecimenDescription){
141
                        objectToUpdate = ((SpecimenDescription)description).getDescribedSpecimenOrObservation();
142
                        deleteIsMatchingInstance = config.getDeleteFrom() instanceof SpecimenOrObservationBase;
143
                        service = (IService)specimenService;
144
                    }else if (description instanceof TaxonNameDescription){
145
                        objectToUpdate = ((TaxonNameDescription)description).getTaxonName();
146
                        deleteIsMatchingInstance = config.getDeleteFrom() instanceof TaxonName;
147
                        service = (IService)nameService;
148
                    }else{
149
                        throw new RuntimeException("Unsupported DescriptionBase class");
150
                    }
151

    
152
                    if (objectToUpdate == null ){
153
                        continue;
154
                    } else if ( (config.isDeleteFromDescription() && deleteIsMatchingInstance  &&
155
                                   config.getDeleteFrom().getId() == objectToUpdate.getId())
156
                                || config.isDeleteFromEveryWhere()){
157
                        updatedObject = handleDeleteMedia(media, textData, description,
158
                                (IDescribable)objectToUpdate);
159
                    } else {
160
                        // this should not be happen, because it is not deletable. see isDeletable
161
                        result.setAbort();
162
                    }
163

    
164
//                } else if (ref instanceof MediaSpecimen && config.getDeleteFrom().getId() == ref.getId() && config.getDeleteFrom() instanceof MediaSpecimen){
165
//                        MediaSpecimen mediaSpecimen = HibernateProxyHelper.deproxy(ref, MediaSpecimen.class);
166
//                        mediaSpecimen.setMediaSpecimen(null);
167
//                        updatedObject = mediaSpecimen;
168
//                        service = (IService)specimenService;
169
                }else if (ref instanceof MediaRepresentation){
170
                    continue;
171
                }else {
172
                    result.setAbort();
173
                }
174

    
175
                if (updatedObject != null){
176
                    service.update(updatedObject); //service should always be != null if updatedObject != null
177
                    result.addUpdatedObject((CdmBase)updatedObject);
178
                }
179
            }
180
            if (result.isOk()){
181
                dao.delete(media);
182
                result.addDeletedObject(media);
183
            }
184

    
185
        }
186
        return result;
187
    }
188

    
189
    /**
190
     * @param media
191
     * @param textData
192
     * @param desc
193
     * @param taxon
194
     */
195
    private IDescribable<DescriptionBase<?>> handleDeleteMedia(Media media, TextData textData,
196
            DescriptionBase<?> desc, IDescribable<DescriptionBase<?>> describable) {
197
        while(textData.getMedia().contains(media)){
198
            textData.removeMedia(media);
199
        }
200
        //if the textData contains text it should not be deleted
201
        if (textData.getMedia().isEmpty() && textData.getMultilanguageText().isEmpty()){
202
            desc.removeElement(textData);
203
        }
204
        if (desc.getElements().isEmpty()){
205
            describable.removeDescription(desc);
206
        }
207
        return describable;
208
    }
209

    
210

    
211
    @Override
212
    public DeleteResult isDeletable(UUID mediaUuid, DeleteConfiguratorBase config){
213
        DeleteResult result = new DeleteResult();
214
        Media media = this.load(mediaUuid);
215
        Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(media);
216
        MediaDeletionConfigurator mediaConfig = (MediaDeletionConfigurator)config;
217
        CdmBase deleteFrom = mediaConfig.getDeleteFrom();
218

    
219
        if (mediaConfig.isDeleteFromEveryWhere()){
220
           return result;
221
        }
222
        for (CdmBase ref: references){
223
            String message = null;
224
            if (ref instanceof MediaRepresentation){
225
                continue;
226
            }else if (ref instanceof TextData){
227
                TextData textData = HibernateProxyHelper.deproxy(ref, TextData.class);
228
                DescriptionBase<?> description = HibernateProxyHelper.deproxy(textData.getInDescription(), DescriptionBase.class);
229

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

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

    
269
        return result;
270
    }
271

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

    
287
        List<String> includes = mediaMetadataKeyIncludes();
288
        List<String> excludes = mediaMetadataKeyExludes();
289
        Map<String, String> metadata = new HashMap<>();
290

    
291
        for(MediaRepresentationPart part : representation.getParts()) {
292
            CdmImageInfo iInfo = CdmImageInfo.NewInstanceWithMetaData(part.getUri(), IMAGE_READ_TIMEOUT);
293
            if(iInfo.getMetaData() != null) {
294
                metadata.putAll(iInfo.getMetaData());
295
            }
296
        }
297

    
298
        if(logger.isDebugEnabled()) {
299
            logger.debug("meta data as read from all parts: " + metadata.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", ", "{", "}")));
300
        }
301

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

    
321
        if(metadata == null) {
322
            metadata = new HashMap<>();
323
        }
324
        return metadata;
325
    }
326

    
327
    private boolean containsCaseInsensitive(String s, List<String> l){
328
        return l.stream().anyMatch(x -> x.equalsIgnoreCase(s));
329
    }
330

    
331
    protected List<String> mediaMetadataKeyExludes(){
332
        CdmPreference pref = prefsService.findExact(CdmPreference.NewKey(PreferenceSubject.NewDatabaseInstance(), PreferencePredicate.MediaMetadataKeynameExcludes));
333
        if(pref == null) {
334
            return new ArrayList<>();
335
        }
336
        return pref.splitStringListValue();
337
    }
338

    
339
    protected List<String> mediaMetadataKeyIncludes(){
340
        CdmPreference pref = prefsService.findExact(CdmPreference.NewKey(PreferenceSubject.NewDatabaseInstance(), PreferencePredicate.MediaMetadataKeynameIncludes));
341
        if(pref == null) {
342
            return Arrays.asList(PreferencePredicate.MediaMetadataKeynameIncludes.getDefaultValue().toString().split(","));
343
        }
344
        return pref.splitStringListValue();
345
    }
346
}
(72-72/100)