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

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

Fixed exception that have appeared while unit-testing

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