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

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

Fixed issued arising after testing on localhost tomcat (completed)

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