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

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

Unit test for getting profiles and components from groups. A little bug is fixed

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