source: ComponentRegistry/trunk/ComponentRegistry/src/main/java/clarin/cmdi/componentregistry/impl/database/ComponentRegistryDbImpl.java @ 5553

Last change on this file since 5553 was 5553, checked in by olhsha@mpi.nl, 10 years ago

fixing serialisation issues for List<String>. Adding unit tests for goup service

File size: 42.1 KB
Line 
1package clarin.cmdi.componentregistry.impl.database;
2
3import clarin.cmdi.componentregistry.CMDComponentSpecExpander;
4import clarin.cmdi.componentregistry.ComponentRegistry;
5import clarin.cmdi.componentregistry.ComponentRegistryException;
6import clarin.cmdi.componentregistry.Configuration;
7import clarin.cmdi.componentregistry.DeleteFailedException;
8import clarin.cmdi.componentregistry.ItemNotFoundException;
9import clarin.cmdi.componentregistry.MDMarshaller;
10import clarin.cmdi.componentregistry.Owner;
11import clarin.cmdi.componentregistry.OwnerUser;
12import clarin.cmdi.componentregistry.RegistrySpace;
13import clarin.cmdi.componentregistry.UserUnauthorizedException;
14import clarin.cmdi.componentregistry.components.CMDComponentSpec;
15import clarin.cmdi.componentregistry.components.CMDComponentType;
16import clarin.cmdi.componentregistry.impl.ComponentRegistryImplBase;
17import clarin.cmdi.componentregistry.impl.ComponentUtils;
18import clarin.cmdi.componentregistry.model.BaseDescription;
19import clarin.cmdi.componentregistry.model.Comment;
20import clarin.cmdi.componentregistry.model.ComponentDescription;
21import clarin.cmdi.componentregistry.model.Group;
22import clarin.cmdi.componentregistry.model.ProfileDescription;
23import clarin.cmdi.componentregistry.model.RegistryUser;
24import clarin.cmdi.componentregistry.persistence.ComponentDao;
25import clarin.cmdi.componentregistry.persistence.jpa.CommentsDao;
26import clarin.cmdi.componentregistry.persistence.jpa.UserDao;
27
28import java.io.ByteArrayInputStream;
29import java.io.ByteArrayOutputStream;
30import java.io.IOException;
31import java.io.InputStream;
32import java.io.OutputStream;
33import java.io.UnsupportedEncodingException;
34import java.security.Principal;
35import java.util.ArrayList;
36import java.util.Calendar;
37import java.util.Collection;
38import java.util.Collections;
39import java.util.Date;
40import java.util.List;
41
42import javax.xml.bind.JAXBException;
43import javax.xml.transform.TransformerException;
44
45import org.slf4j.Logger;
46import org.slf4j.LoggerFactory;
47import org.springframework.beans.factory.annotation.Autowired;
48import org.springframework.beans.factory.annotation.Qualifier;
49import org.springframework.dao.DataAccessException;
50import org.springframework.orm.jpa.vendor.Database;
51import org.springframework.transaction.annotation.Transactional;
52
53/**
54 * Implementation of ComponentRegistry that uses Database Acces Objects for
55 * accessing the registry (ergo: a database implementation)
56 *
57 * @author Twan Goosen <twan.goosen@mpi.nl>
58 * @author George.Georgovassilis@mpi.nl
59 */
60@Transactional
61public class ComponentRegistryDbImpl extends ComponentRegistryImplBase implements ComponentRegistry {
62
63    private final static Logger LOG = LoggerFactory.getLogger(ComponentRegistryDbImpl.class);
64    private Owner registryOwner;
65    private RegistrySpace registrySpace;
66    private Number groupId;
67    @Autowired
68    private Configuration configuration;
69    @Autowired
70    @Qualifier("componentsCache")
71    private CMDComponentSpecCache componentsCache;
72    @Autowired
73    @Qualifier("profilesCache")
74    private CMDComponentSpecCache profilesCache;
75    // DAO's
76    @Autowired
77    private ComponentDao componentDao;
78    @Autowired
79    private UserDao userDao;
80    @Autowired
81    private CommentsDao commentsDao;
82    @Autowired
83    private MDMarshaller marshaller;
84    @Autowired
85    private GroupService groupService;
86
87    //
88    /**
89     * Default constructor, to use this as a (spring) bean. The public registry
90     * by default. Use setRegistryStatus(), setRegistryOwner(), setGroupId() to
91     * make it another kind of registry.
92     *
93     *
94     */
95    public ComponentRegistryDbImpl() throws TransformerException {
96        this.registrySpace = RegistrySpace.PUBLISHED;
97    }
98
99    @Override
100    public Owner getRegistryOwner() {
101        return this.registryOwner;
102    }
103
104    @Override
105    public void setRegistryOwner(Owner registryOwner) {
106        this.registryOwner = registryOwner;
107    }
108
109    @Override
110    public RegistrySpace getRegistrySpace() {
111        return this.registrySpace;
112    }
113
114    @Override
115    public void setRegistrySpace(RegistrySpace registrySpace) {
116        this.registrySpace = registrySpace;
117    }
118
119    @Override
120    public Number getGroupId() {
121        return this.groupId;
122    }
123
124    @Override
125    public void setGroupId(Number groupId) {
126        this.groupId = groupId;
127    }
128
129    @Override
130    public Number getBaseDescriptionOwnerId(String cmdId) {
131        BaseDescription bd = componentDao.getByCmdId(cmdId);
132        return bd.getDbUserId();
133    }
134
135    @Override
136    public List<Number> getItemGroups(String cmdId) {
137        List<Group> groups = groupService.getGroupsTheItemIsAMemberOf(cmdId);
138        List<Number> result = new ArrayList<Number>();
139        if (groups != null) {
140            for (Group group : groups) {
141                result.add(group.getId());
142            }
143        }
144        return result;
145    }
146
147    /**
148     * @return The user id, or null if there is no owner or it is not a user.
149     */
150    private Number getUserId() {
151        if (registryOwner instanceof OwnerUser) {
152            return registryOwner.getId();
153        } else {
154            return null;
155        }
156    }
157   
158    @Override
159    public Number makeGroupMember(String principalName, String groupName) throws  UserUnauthorizedException, ItemNotFoundException{
160        RegistryUser regOwner = userDao.getPrincipalNameById(registryOwner.getId());
161        if  (groupService.isUserOwnerOfGroup(groupName, regOwner.getPrincipalName()) || 
162                configuration.isAdminUser(regOwner.getPrincipalName())) {
163        return groupService.makeMember(principalName, groupName);
164        } else {
165            throw new UserUnauthorizedException("The registry owner is not the admin or not the owner of the group and cannot add users to the group");
166        }
167    }
168   
169//    @Override
170//    public long removeGroupMember(String principalName, String groupName) throws  UserUnauthorizedException, ItemNotFoundException{
171//        RegistryUser regOwner = userDao.getPrincipalNameById(registryOwner.getId());
172//        if  (groupService.isUserOwnerOfGroup(groupName, regOwner.getPrincipalName()) ||
173//                configuration.isAdminUser(regOwner.getPrincipalName())) {
174//        return groupService.removeMember(principalName, groupName);
175//        } else {
176//            throw new UserUnauthorizedException("The registry owner is not the admin or not the owner of the group and cannot add users to the group");
177//        }
178//    }
179   
180   
181    @Override
182    public List<ProfileDescription> getProfileDescriptions() throws ComponentRegistryException, UserUnauthorizedException {
183        try {
184            switch (registrySpace) {
185                case PRIVATE:
186                    if (registryOwner == null) {
187                        throw new ComponentRegistryException("Private workspace without owner!");
188                    }
189                    return ComponentUtils.toProfiles(componentDao.getPrivateBaseDescriptions(registryOwner.getId(), ProfileDescription.PROFILE_PREFIX));
190                case GROUP:
191                    return this.getProfileDescriptionsInGroup(groupId);
192                case PUBLISHED:
193                    return ComponentUtils.toProfiles(componentDao.getPublicBaseDescriptions(ProfileDescription.PROFILE_PREFIX));
194                default:
195                    throw new ComponentRegistryException("Unsupported status type" + registrySpace);
196            }
197        } catch (DataAccessException ex) {
198            throw new ComponentRegistryException("Database access error while trying to get profile descriptions", ex);
199        }
200    }
201
202    @Override
203    public Boolean isItemPublic(String id) throws ItemNotFoundException {
204        BaseDescription desc = componentDao.getByCmdId(id);
205        if (desc == null) {
206            String idS = (id == null) ? "null" : id;
207            throw new ItemNotFoundException("The component with the id " + idS + "is not found in the database.");
208        }
209        return desc.isPublic();
210    }
211
212    @Override
213    public ProfileDescription getProfileDescriptionAccessControlled(String id) throws ItemNotFoundException, UserUnauthorizedException, ComponentRegistryException {
214        boolean hasAccess = this.canCurrentUserAccessDescription(id);
215        if (hasAccess) {
216            try {
217                return ComponentUtils.toProfile(componentDao.getByCmdId(id));
218            } catch (DataAccessException ex) {
219                throw new ComponentRegistryException("Database access error while trying to get profile description", ex);
220            }
221        } else {
222            throw new UserUnauthorizedException("The logged-in user (aka registry owner) does not have access the the given profile");
223        }
224    }
225
226    private ProfileDescription getProfileDescription(String id) throws ComponentRegistryException {
227        try {
228            return ComponentUtils.toProfile(componentDao.getByCmdId(id));
229        } catch (DataAccessException ex) {
230            throw new ComponentRegistryException("Database access error while trying to get profile description", ex);
231        }
232
233    }
234
235    @Override
236    public List<ComponentDescription> getComponentDescriptions() throws ComponentRegistryException, UserUnauthorizedException {
237        try {
238            switch (registrySpace) {
239                case PRIVATE:
240                    if (registryOwner == null) {
241                        throw new ComponentRegistryException("Private workspace without owner!");
242                    }
243                    return ComponentUtils.toComponents(componentDao.getPrivateBaseDescriptions(registryOwner.getId(), ComponentDescription.COMPONENT_PREFIX));
244                case GROUP:
245                    return this.getComponentDescriptionsInGroup(groupId);
246                case PUBLISHED:
247                    return ComponentUtils.toComponents(componentDao.getPublicBaseDescriptions(ComponentDescription.COMPONENT_PREFIX));
248                default:
249                    throw new ComponentRegistryException("Unsupported status type" + registrySpace);
250            }
251        } catch (DataAccessException ex) {
252            throw new ComponentRegistryException("Database access error while trying to get profile descriptions", ex);
253        }
254    }
255
256    @Override
257    public ComponentDescription getComponentDescriptionAccessControlled(String id) throws ComponentRegistryException, UserUnauthorizedException, ItemNotFoundException {
258        boolean hasAccess = this.canCurrentUserAccessDescription(id);
259        if (hasAccess) {
260            try {
261                return ComponentUtils.toComponent(componentDao.getByCmdId(id));
262            } catch (DataAccessException ex) {
263                throw new ComponentRegistryException("Database access error while trying to get component description", ex);
264            }
265        } else {
266            throw new UserUnauthorizedException("The logged-in user (aka registry owner) does not have access the the given component");
267        }
268    }
269
270    private ComponentDescription getComponentDescription(String id) throws ComponentRegistryException {
271        try {
272            return ComponentUtils.toComponent(componentDao.getByCmdId(id));
273        } catch (DataAccessException ex) {
274            throw new ComponentRegistryException("Database access error while trying to get component description", ex);
275        }
276
277    }
278
279    @Override
280    public List<Comment> getCommentsInProfile(String profileId) throws ComponentRegistryException, UserUnauthorizedException, ItemNotFoundException {
281        try {
282            //Olha was here
283            //if (componentDao.isInRegistry(profileId, getUserId())) {
284            if (this.canCurrentUserAccessDescription(profileId)) {
285                final List<Comment> commentsFromProfile = commentsDao.getCommentsFromItem(profileId);
286                this.setCanDeleteInComments(commentsFromProfile);
287                return commentsFromProfile;
288            } else {
289                throw new UserUnauthorizedException("The logged-in user (aka registry owner) does not have access the the given profile");
290            }
291        } catch (DataAccessException ex) {
292            throw new ComponentRegistryException(
293                    "Database access error while trying to get list of comments from profile", ex);
294        }
295    }
296
297    @Override
298    public Comment getSpecifiedCommentInProfile(String profileId, String commentId)
299            throws ComponentRegistryException, UserUnauthorizedException, ItemNotFoundException {
300        if (this.canCurrentUserAccessDescription(profileId)) {
301            try {
302                Comment comment = commentsDao.findOne(Long.parseLong(commentId));
303                if (comment != null && profileId.equals(comment.getComponentId())) {
304                    this.setCanDeleteInComments(Collections.singleton(comment));
305                    return comment;
306                } else {
307                    throw new ItemNotFoundException("Comment " + commentId + " for the profile " + profileId + " is not found.");
308                }
309            } catch (DataAccessException ex) {
310                throw new ComponentRegistryException("Database access error while trying to get comment from profile", ex);
311            }
312        } else {
313            throw new UserUnauthorizedException("The logged-in user (aka registry owner) does not have access the the given profile");
314        }
315    }
316
317    @Override
318    public List<Comment> getCommentsInComponent(String componentId)
319            throws ComponentRegistryException, UserUnauthorizedException, ItemNotFoundException {
320        if (this.canCurrentUserAccessDescription(componentId)) {
321            try {
322                final List<Comment> commentsFromComponent = commentsDao.getCommentsFromItem(componentId);
323                this.setCanDeleteInComments(commentsFromComponent);
324                return commentsFromComponent;
325
326            } catch (DataAccessException ex) {
327                throw new ComponentRegistryException(
328                        "Database access error while trying to get list of comments from component", ex);
329            }
330        } else {
331            throw new UserUnauthorizedException("The logged-in user (aka registry owner) does not have access the the given component");
332        }
333    }
334
335    @Override
336    public Comment getSpecifiedCommentInComponent(String componentId, String commentId)
337            throws ComponentRegistryException, UserUnauthorizedException, ItemNotFoundException {
338        if (this.canCurrentUserAccessDescription(componentId)) {
339            try {
340                Comment comment = commentsDao.findOne(Long.parseLong(commentId));
341                if (comment != null && componentId.equals(comment.getComponentId().toString())) {
342                    this.setCanDeleteInComments(Collections.singleton(comment));
343                    return comment;
344                } else {
345                    throw new ItemNotFoundException("Comment " + commentId + " for the component " + componentId + " is not found.");
346                }
347            } catch (DataAccessException ex) {
348                throw new ComponentRegistryException("Database access error while trying to get comment from component", ex);
349            }
350        } else {
351            throw new UserUnauthorizedException("The logged-in user (aka registry owner) does not have access the the given component");
352        }
353    }
354
355    /**
356     * Sets the {@link Comment#setCanDelete(boolean) canDelete} property on all
357     * comments in the provided collection for the perspective of the specified
358     * principal. Comment owners (determined by {@link Comment#getUserId() })
359     * and admins can delete, others cannot.
360     *
361     * @param comments comments to configure
362     * @param principal user to configure for
363     * @see Comment#isCanDelete()
364     */
365    private void setCanDeleteInComments(Collection<Comment> comments) {
366        String principalName = (userDao.getPrincipalNameById(registryOwner.getId())).getPrincipalName();
367        final boolean isAdmin = configuration.isAdminUser(principalName);
368        for (Comment comment : comments) {
369            comment.setCanDelete(isAdmin || comment.getUserId() == registryOwner.getId().longValue());
370        }
371
372    }
373
374    @Override
375    public CMDComponentSpec getMDProfileAccessControled(String id) throws ComponentRegistryException, UserUnauthorizedException, ItemNotFoundException {
376        if (this.canCurrentUserAccessDescription(id)) {
377            return this.getMDProfile(id);
378        } else {
379            throw new UserUnauthorizedException("The logged-in user (aka registry owner) does not have access the the given profile or the profile is not found.");
380        }
381    }
382
383    private CMDComponentSpec getMDProfile(String id) throws ComponentRegistryException {
384        if (id.startsWith(ProfileDescription.PROFILE_PREFIX)) {
385            CMDComponentSpec result = profilesCache.get(id);
386            if (result == null && !profilesCache.containsKey(id)) {
387                result = this.getUncachedMDProfile(id);
388                profilesCache.put(id, result);
389            }
390            return result;
391        } else {
392            throw new ComponentRegistryException("the id " + id + " is not a profile id.");
393        }
394    }
395
396    public CMDComponentSpec getUncachedMDProfile(String id) throws ComponentRegistryException {
397        try {
398            return this.getUncachedMDComponent(id, componentDao);
399        } catch (DataAccessException ex) {
400            throw new ComponentRegistryException("Database access error while trying to get profile", ex);
401        }
402    }
403
404    @Override
405    public CMDComponentSpec getMDComponentAccessControlled(String id) throws ComponentRegistryException, UserUnauthorizedException, ItemNotFoundException {
406        if (this.canCurrentUserAccessDescription(id)) {
407            return this.getMDComponent(id);
408        } else {
409            throw new UserUnauthorizedException("The logged-in user (aka registry owner) does not have access the the given component or the component is not found.");
410        }
411    }
412
413    @Override
414    public CMDComponentSpec getMDComponent(String id) throws ComponentRegistryException {
415        if (id.startsWith(ComponentDescription.COMPONENT_PREFIX)) {
416            CMDComponentSpec result = componentsCache.get(id);
417            if (result == null && !componentsCache.containsKey(id)) {
418                result = getUncachedMDComponent(id);
419                componentsCache.put(id, result);
420            }
421            return result;
422        } else {
423            throw new ComponentRegistryException("The id " + id + " is not a component id");
424        }
425    }
426
427    public CMDComponentSpec getUncachedMDComponent(String id) throws ComponentRegistryException {
428        try {
429            return this.getUncachedMDComponent(id, componentDao);
430        } catch (DataAccessException ex) {
431            throw new ComponentRegistryException("Database access error while trying to get component", ex);
432        }
433    }
434
435    @Override
436    public int register(BaseDescription description, CMDComponentSpec spec) {
437        enrichSpecHeader(spec, description);
438        try {
439            String xml = componentSpecToString(spec);
440            // Convert principal name to user record id
441            Number uid = convertUserInDescription(description);
442            componentDao.insertDescription(description, xml, description.isPublic(), uid);
443            invalidateCache(description);
444            return 0;
445        } catch (DataAccessException ex) {
446            LOG.error("Database error while registering component", ex);
447            return -1;
448        } catch (JAXBException ex) {
449            LOG.error("Error while registering component", ex);
450            return -2;
451        } catch (UnsupportedEncodingException ex) {
452            LOG.error("Error while registering component", ex);
453            return -3;
454        }
455    }
456
457    @Override
458    public int registerComment(Comment comment, String principalName) throws ComponentRegistryException, ItemNotFoundException, UserUnauthorizedException {
459        try {
460            if (comment.getComponentId() != null) {
461                if (this.canCurrentUserAccessDescription(comment.getComponentId())) {
462                    // Convert principal name to user record id
463                    Number uid = convertUserIdInComment(comment, principalName);
464                    // Set date to current date
465                    comment.setCommentDate(new Date());
466                    comment.setUserId(uid.longValue());
467                    commentsDao.saveAndFlush(comment);
468
469                } else {
470                    throw new UserUnauthorizedException("The logged-in user cannot access the component/profile with id " + comment.getComponentId());
471                }
472            } else {
473                throw new ComponentRegistryException("The component/profile id for this comment is null.");
474            }
475            return 0;
476        } catch (DataAccessException ex) {
477            LOG.error("Database error while registering component", ex);
478            return -1;
479        }
480    }
481
482    /**
483     * Calling service sets user id to principle. Our task is to convert this to
484     * an id for later reference. If none is set and this is a user's workspace,
485     * set from that user's id.
486     *
487     * It also sets the name in the description according to the display name in
488     * the database.
489     *
490     * @param description Description containing principle name as userId
491     * @return Id (from database)
492     * @throws DataAccessException
493     */
494    private Number convertUserInDescription(BaseDescription description) throws DataAccessException {
495        Number uid = null;
496        String name = null;
497        if (description.getUserId() != null) {
498            RegistryUser user = userDao.getByPrincipalName(description.getUserId());
499            if (user != null) {
500                uid = user.getId();
501                name = user.getName();
502            }
503        } else {
504            uid = getUserId(); // this can be null as well
505        }
506        if (uid != null) {
507            description.setUserId(uid.toString());
508        }
509        if (name != null) {
510            description.setCreatorName(name);
511        }
512        return uid;
513    }
514
515    /**
516     * Calling service sets user id to principle. Our task is to convert this to
517     * an id for later reference. If none is set and this is a user's workspace,
518     * set from that user's id.
519     *
520     * @param comment Comment containing principle name as userId
521     * @return Id (from database)
522     * @throws DataAccessException
523     */
524    private Number convertUserIdInComment(Comment comment, String principalName) throws DataAccessException,
525            ComponentRegistryException {
526        if (principalName != null) {
527            RegistryUser user = userDao.getByPrincipalName(principalName);
528            if (user != null) {
529                Long id = user.getId();
530                if (id != null) {
531                    // Set user id in comment for convenience of calling method
532                    comment.setUserId(id);
533                    // Set name to user's preferred display name
534                    comment.setUserName(user.getName());
535                    return id;
536                } else {
537                    throw new ComponentRegistryException("Cannot find user with principal name: " + principalName);
538                }
539            }
540        }
541        return null;
542    }
543
544    @Override
545    public int update(BaseDescription description, CMDComponentSpec spec, boolean forceUpdate) throws UserUnauthorizedException, ItemNotFoundException {
546        try {
547            this.checkAuthorisation(description);
548            this.checkAge(description);
549            // For public components, check if used in other components or
550            // profiles (unless forced)
551            if (!forceUpdate && description.isPublic() && !description.isProfile()) {
552                this.checkStillUsed(description.getId());
553            }
554            componentDao.updateDescription(getIdForDescription(description), description, componentSpecToString(spec));
555            invalidateCache(description);
556            return 0;
557        } catch (JAXBException ex) {
558            LOG.error("Error while updating component", ex);
559            return -1;
560        } catch (UnsupportedEncodingException ex) {
561            LOG.error("Error while updating component", ex);
562            return -1;
563        } catch (IllegalArgumentException ex) {
564            LOG.error("Error while updating component", ex);
565            return -1;
566        } catch (DeleteFailedException e) {
567            LOG.error("Error while updating component", e);
568            return -1;
569        } catch (ComponentRegistryException e) {
570            LOG.error("Error while updating component", e);
571            return -1;
572        }
573    }
574
575    @Override
576    public int publish(BaseDescription desc, CMDComponentSpec spec, Principal principal) throws UserUnauthorizedException, ItemNotFoundException {
577        int result = 0;
578        this.checkAuthorisation(desc);
579        if (desc.isPublic()) { // if already in published
580            // todo
581            desc.setHref(ComponentUtils.createPublicHref(desc.getHref()));
582            Number id = getIdForDescription(desc);
583            try {
584                // Update description & content
585                componentDao.updateDescription(id, desc, componentSpecToString(spec));
586                // Set to public
587                componentDao.setPublished(id, true);
588            } catch (DataAccessException ex) {
589                LOG.error("Database error while updating component", ex);
590                return -1;
591            } catch (JAXBException ex) {
592                LOG.error("Error while updating component", ex);
593                return -2;
594            } catch (UnsupportedEncodingException ex) {
595                LOG.error("Error while updating component", ex);
596                return -3;
597            }
598        }
599        return result;
600    }
601
602    @Override
603    public void getMDProfileAsXml(String profileId, OutputStream output) throws ComponentRegistryException {
604        CMDComponentSpec expandedSpec = CMDComponentSpecExpanderDbImpl.expandProfile(profileId, this);
605        writeXml(expandedSpec, output);
606    }
607
608    @Override
609    public void getMDProfileAsXsd(String profileId, OutputStream outputStream) throws ComponentRegistryException {
610        CMDComponentSpec expandedSpec = CMDComponentSpecExpanderDbImpl.expandProfile(profileId, this);
611        writeXsd(expandedSpec, outputStream);
612    }
613
614    @Override
615    public void getMDComponentAsXml(String componentId, OutputStream output) throws ComponentRegistryException {
616        CMDComponentSpec expandedSpec = CMDComponentSpecExpanderDbImpl.expandComponent(componentId, this);
617        writeXml(expandedSpec, output);
618    }
619
620    @Override
621    public void getMDComponentAsXsd(String componentId, OutputStream outputStream) throws ComponentRegistryException {
622        CMDComponentSpec expandedSpec = CMDComponentSpecExpanderDbImpl.expandComponent(componentId, this);
623        writeXsd(expandedSpec, outputStream);
624    }
625
626    @Override
627    public void deleteMDProfile(String profileId) throws UserUnauthorizedException,
628            DeleteFailedException, ComponentRegistryException, ItemNotFoundException {
629        ProfileDescription desc = getProfileDescriptionAccessControlled(profileId);
630        if (desc != null) {
631            try {
632                this.checkAuthorisation(desc);
633                this.checkAge(desc);
634                componentDao.setDeleted(desc, true);
635                invalidateCache(desc);
636            } catch (DataAccessException ex) {
637                throw new DeleteFailedException("Database access error while trying to delete profile", ex);
638            }
639        }
640    }
641
642    @Override
643    public void deleteMDComponent(String componentId, boolean forceDelete)
644            throws UserUnauthorizedException, DeleteFailedException, ComponentRegistryException, ItemNotFoundException {
645        BaseDescription desc = getComponentDescriptionAccessControlled(componentId);
646        if (desc != null) {
647            try {
648                this.checkAuthorisation(desc);
649                this.checkAge(desc);
650
651                if (!forceDelete) {
652                    checkStillUsed(componentId);
653                }
654                componentDao.setDeleted(desc, true);
655                invalidateCache(desc);
656            } catch (DataAccessException ex) {
657                throw new DeleteFailedException("Database access error while trying to delete component", ex);
658            }
659        }
660    }
661
662    private void invalidateCache(BaseDescription description) {
663        if (description.isProfile()) {
664            profilesCache.remove(description.getId());
665        } else {
666            componentsCache.remove(description.getId());
667        }
668    }
669
670    /**
671     * Looks up description on basis of CMD Id. This will also check if such a
672     * record even exists.
673     *
674     * @param description Description to look up
675     * @return Database id for description
676     * @throws IllegalArgumentException If description with non-existing id is
677     * passed
678     */
679    private Number getIdForDescription(BaseDescription description) throws IllegalArgumentException {
680        Number dbId = null;
681        try {
682            dbId = componentDao.getDbId(description.getId());
683        } catch (DataAccessException ex) {
684            LOG.error("Error getting dbId for component with id " + description.getId(), ex);
685        }
686        if (dbId == null) {
687            throw new IllegalArgumentException("Could not get database Id for description");
688        } else {
689            return dbId;
690        }
691    }
692
693    private String componentSpecToString(CMDComponentSpec spec) throws UnsupportedEncodingException, JAXBException {
694        ByteArrayOutputStream os = new ByteArrayOutputStream();
695        getMarshaller().marshal(spec, os);
696        String xml = os.toString("UTF-8");
697        return xml;
698    }
699
700    private CMDComponentSpec getUncachedMDComponent(String id, ComponentDao dao) {
701        String xml = dao.getContent(false, id);
702        if (xml != null) {
703            try {
704                InputStream is = new ByteArrayInputStream(xml.getBytes("UTF-8"));
705                CMDComponentSpec result = getMarshaller().unmarshal(CMDComponentSpec.class, is, null);
706                try {
707                    is.close();
708                    return result;
709                } catch (IOException ex) {
710                    LOG.error("Cannot close the stream", ex);
711                    return result;
712                }
713
714            } catch (JAXBException ex) {
715                LOG.error("Error while unmarshalling", ex);
716            } catch (UnsupportedEncodingException ex) {
717                LOG.error("Exception while reading XML from database", ex);
718            }
719        }
720        return null;
721    }
722
723    private void checkAuthorisation(BaseDescription desc) throws UserUnauthorizedException, ItemNotFoundException {
724        if (!this.canCurrentUserAccessDescription(desc.getId())) {
725            String principalName = (registryOwner != null) ? userDao.getPrincipalNameById(registryOwner.getId()).getPrincipalName() : "null";
726            throw new UserUnauthorizedException("Unauthorized operation user '" + principalName
727                    + "' is not the creator (nor a member of the group, nor an administrator) of the "
728                    + (desc.isProfile() ? "profile" : "component") + "(" + desc + ").");
729        }
730    }
731
732    private void checkAuthorisationComment(Comment desc) throws UserUnauthorizedException {
733        String principalName = userDao.getPrincipalNameById(registryOwner.getId()).getPrincipalName();
734        if (!(this.isOwnerOfComment(desc, principalName) || configuration.isAdminUser(principalName))) {
735            throw new UserUnauthorizedException("Unauthorized operation: user '" + principalName
736                    + "' is not the creator (nor the Administrator) of the comment " + (desc.getId()));
737        }
738    }
739
740//    private boolean isOwnerOfDescription(BaseDescription desc, String principalName) {
741//        String owner = componentDao.getOwnerPrincipalName(getIdForDescription(desc));
742//        return owner != null // If owner is null, no one can be owner
743//                && principalName.equals(owner);
744//    }
745    private boolean isOwnerOfComment(Comment com, String principalName) {
746        long id = Long.parseLong(com.getId());
747        RegistryUser owner = commentsDao.getOwnerOfComment(id);
748        return owner != null // If owner is null, no one can be owner
749                && principalName.equals(owner.getPrincipalName());
750    }
751
752    private void checkAge(BaseDescription desc) throws DeleteFailedException {
753        String principalName = userDao.getPrincipalNameById(registryOwner.getId()).getPrincipalName();
754        if (desc.isPublic() && !configuration.isAdminUser(principalName)) {
755            Date regDate = desc.getRegistrationDate();
756            Calendar calendar = Calendar.getInstance();
757            calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) - 1);
758            if (regDate.before(calendar.getTime())) { // More then month old
759                throw new DeleteFailedException(
760                        "The "
761                        + (desc.isProfile() ? "profile" : "Ccomponent")
762                        + " is more than a month old and cannot be deleted anymore. It might have been used to create metadata, deleting it would invalidate that metadata.");
763            }
764        }
765    }
766
767    @Override
768    public String getName() {
769        if (this.getRegistrySpace() != null) {
770            if (this.getRegistrySpace().equals(RegistrySpace.PUBLISHED)) {
771                return ComponentRegistry.PUBLIC_NAME;
772            } else {
773                if (this.getRegistrySpace().equals(RegistrySpace.GROUP)) {
774                    if (groupId != null) {
775                        return "Registry of group" + groupId.toString();
776                    } else {
777                        return "Error: Registry of group null.";
778                    }
779                }
780            }
781        };
782        RegistryUser u = userDao.findOne(getUserId().longValue());
783        return "Registry of " + u.getName();
784    }
785
786    private boolean canCurrentUserAccessDescription(String cmdId) throws ItemNotFoundException {
787        if (cmdId == null) {
788            throw new ItemNotFoundException("Item with the null cmdIdentifier.");
789        }
790
791        BaseDescription description = componentDao.getByCmdId(cmdId);
792        if (description == null) {
793            throw new ItemNotFoundException("Item with the id " + cmdId + " is not found.");
794        }
795
796
797        Number userId = getUserId();
798        if (userId == null) {
799            return false;
800        }
801        RegistryUser user = userDao.findOne(userId.longValue());
802        if (user == null) {
803            return false;
804        }
805
806
807        if (configuration.isAdminUser(user.getPrincipalName())) {
808            return true;
809        }
810
811        return groupService.canUserAccessComponentEitherOnHisOwnOrThroughGroupMembership(user, description);
812    }
813
814    @Override
815    public List<ProfileDescription> getDeletedProfileDescriptions() throws ComponentRegistryException {
816        return ComponentUtils.toProfiles(componentDao.getDeletedDescriptions(getUserId()));
817    }
818
819    @Override
820    public List<ComponentDescription> getDeletedComponentDescriptions() throws ComponentRegistryException {
821        return ComponentUtils.toComponents(componentDao.getDeletedDescriptions(getUserId()));
822    }
823
824    @Override
825    public void deleteComment(String commentId) throws IOException,
826            UserUnauthorizedException, DeleteFailedException, ItemNotFoundException {
827        try {
828            Comment comment = commentsDao.findOne(Long.parseLong(commentId));
829            if (comment != null
830                    // Comment must have an existing (in this registry)
831                    // componentId or profileId
832                    && comment.getComponentId() != null
833                    && this.canCurrentUserAccessDescription(comment.getComponentId())) {
834                this.checkAuthorisationComment(comment);
835                commentsDao.delete(comment);
836            } else {
837                // Comment exists in DB, but component is not in this registry
838                throw new ItemNotFoundException("Comment " + commentId + " cannot be found in specified registry");
839            }
840        } catch (DataAccessException ex) {
841            throw new DeleteFailedException("Database access error while trying to delete component", ex);
842        } catch (NumberFormatException ex) {
843            throw new DeleteFailedException("Illegal comment ID, cannot parse integer", ex);
844        }
845    }
846
847    @Override
848    public CMDComponentSpecExpander getExpander() {
849        return new CMDComponentSpecExpanderDbImpl(this);
850    }
851
852    @Override
853    protected MDMarshaller getMarshaller() {
854        return marshaller;
855    }
856
857    @Override
858    public String toString() {
859        return getName();
860    }
861
862    private List<ComponentDescription> getComponentDescriptionsInGroup(Number groupId)
863            throws ComponentRegistryException, UserUnauthorizedException {
864
865        String principalName = userDao.getPrincipalNameById(registryOwner.getId()).getPrincipalName();
866
867        if (!groupService.userGroupMember(principalName, groupId.toString())) {
868            throw new UserUnauthorizedException("The user \'" + principalName + "\' does not have access to components of the group " + groupId);
869        }
870
871        List<String> componentIds = componentDao.getAllItemIdsInGroup(ComponentDescription.COMPONENT_PREFIX, groupId.longValue());
872        List<ComponentDescription> components = new ArrayList<ComponentDescription>();
873        for (String id : componentIds) {
874            BaseDescription description = componentDao.getByCmdId(id);
875            if (description != null) {
876                components.add(ComponentUtils.toComponent(description));
877            }
878        }
879        return components;
880    }
881
882    private List<ProfileDescription> getProfileDescriptionsInGroup(Number groupId) throws ComponentRegistryException, UserUnauthorizedException {
883
884        String principalName = userDao.getPrincipalNameById(registryOwner.getId()).getPrincipalName();
885
886        if (!groupService.userGroupMember(principalName, groupId.toString())) {
887            throw new UserUnauthorizedException("The user \'" + principalName + "\' does not have access to profiles of the group " + groupId);
888        }
889
890        List<String> profileIds = componentDao.getAllItemIdsInGroup(ProfileDescription.PROFILE_PREFIX, groupId.longValue());
891        List<ProfileDescription> profiles = new ArrayList<ProfileDescription>();
892        for (String id : profileIds) {
893            BaseDescription description = componentDao.getByCmdId(id);
894            if (description != null) {
895                profiles.add(ComponentUtils.toProfile(description));
896            }
897        }
898        return profiles;
899    }
900
901    @Override
902    public List<ProfileDescription> getProfileDescriptionsForMetadaEditor(Number groupId) throws UserUnauthorizedException,
903            ComponentRegistryException {
904        return this.getProfileDescriptionsInGroup(groupId.longValue());
905    }
906
907    @Override
908    public List<String> getAllNonDeletedProfileIds() {
909        return componentDao.getAllNonDeletedProfileIds();
910    }
911
912    @Override
913    public List<String> getAllNonDeletedComponentIds() {
914        return componentDao.getAllNonDeletedComponentIds();
915    }
916
917    @Override
918    public List<ComponentDescription> getUsageInComponents(String componentId) throws ComponentRegistryException {
919        LOG.debug("Checking usage of component {} in components", componentId);
920        List<ComponentDescription> result = new ArrayList<ComponentDescription>();
921        List<String> ids = getAllNonDeletedComponentIds();
922        for (String id : ids) {
923            CMDComponentSpec spec = getMDComponent(id);
924            if (spec != null && hasComponentId(componentId, spec.getCMDComponent())) {
925                LOG.debug("Component {} used in component {}", componentId, spec.getHeader().getID());
926                result.add(getComponentDescription(id));
927            }
928        }
929        return result;
930    }
931
932    @Override
933    public List<ProfileDescription> getUsageInProfiles(String componentId) throws ComponentRegistryException {
934        LOG.debug("Checking usage of component {} in profiles", componentId);
935        List<ProfileDescription> result = new ArrayList<ProfileDescription>();
936        for (String id : getAllNonDeletedProfileIds()) {
937            CMDComponentSpec profile = getMDProfile(id);
938            if (profile != null && hasComponentId(componentId, profile.getCMDComponent())) {
939                LOG.debug("Component {} used in profile {}", componentId, profile.getHeader().getID());
940                result.add(getProfileDescription(id));
941            }
942        }
943        return result;
944    }
945
946    protected static boolean findComponentId(String componentId, List<CMDComponentType> componentReferences) {
947        for (CMDComponentType cmdComponent : componentReferences) {
948            if (hasComponentId(componentId, cmdComponent)) {
949                return true;
950            }
951        }
952        return false;
953    }
954
955    private static boolean hasComponentId(String componentId, CMDComponentType cmdComponent) {
956        if (componentId.equals(cmdComponent.getComponentId())) {
957            return true;
958        } else if (findComponentId(componentId, cmdComponent.getCMDComponent())) {
959            return true;
960        } else {
961            return false;
962        }
963    }
964
965    protected void checkStillUsed(String componentId) throws DeleteFailedException, ComponentRegistryException {
966        for (String id : getAllNonDeletedProfileIds()) {
967            CMDComponentSpec spec = getMDProfile(id);
968            if (spec != null && hasComponentId(componentId, spec.getCMDComponent())) {
969                LOG.warn("Cannot delete component {}, still used in profile {} and possibly other profiles and/or components", componentId, spec.getHeader().getID());
970                // Profile match - throw
971                throw new DeleteFailedException("Component is still in use by other components or profiles. Request component usage for details.");
972            }
973        }
974
975        LOG.debug("Component {} is not used in any profiles", componentId);
976
977        for (String id : getAllNonDeletedComponentIds()) {
978            CMDComponentSpec spec = getMDComponent(id);
979            if (spec != null && hasComponentId(componentId, spec.getCMDComponent())) {
980                LOG.warn("Cannot delete component {}, still used in component {} and possibly other components", componentId, spec.getHeader().getID());
981                // Component match -> throw
982                throw new DeleteFailedException("Component is still in use by one or more other components. Request component usage for details.");
983            }
984        }
985    }
986   
987   
988}
Note: See TracBrowser for help on using the repository browser.