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

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

Unit test for getting comments of profiles and components from groups.

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