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

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

Added group service. Tested via the tomcat on loclahots (test URI and postman), old unit tests are adjusted and work well. Todo: retest on localhost tomcat, look at run-time exceptions, add new unit tests, adjust front-end

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