/* * Copyright (C) 2011 - 2012 Interactive Media Management * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package dk.i2m.converge.ws.soap; import dk.i2m.converge.core.workflow.WorkflowStateTransitionException; import dk.i2m.converge.core.DataNotFoundException; import dk.i2m.converge.core.content.NewsItem; import dk.i2m.converge.core.content.NewsItemPlacement; import dk.i2m.converge.core.metadata.Concept; import dk.i2m.converge.core.security.UserAccount; import dk.i2m.converge.core.views.InboxView; import dk.i2m.converge.core.workflow.Edition; import dk.i2m.converge.core.workflow.Section; import dk.i2m.converge.ejb.facades.*; import dk.i2m.converge.ws.model.ModelConverter; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Resource; import javax.ejb.EJB; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; import javax.xml.ws.WebServiceContext; /** * {@link WebService} for retrieving news items. * * @author Allan Lykke Christensen */ //@WebService(serviceName = "NewsItemService") public class NewsItemService { private static final Logger LOG = Logger.getLogger(NewsItemService.class. getName()); private static final String USER_IS_NOT_AUTHENTICATED = "User is not authenticated"; @EJB private NewsItemFacadeLocal newsItemFacade; @EJB private OutletFacadeLocal outletFacade; @EJB private UserFacadeLocal userFacade; @EJB private MetaDataFacadeLocal metaDataFacade; @Resource private WebServiceContext context; /** * Starts the workflow of a new * {@link dk.i2m.converge.core.content.NewsItem}. * * @param outletId Unique identifier of the Outlet where to place the news * item * @param title Title of the news item * @return Unique identifier of the new news item * @throws WorkflowStateTransitionException * If the workflow could not be * started */ @WebMethod(operationName = "start") public Long start(@WebParam(name = "outletId") Long outletId, @WebParam(name = "title") String title) throws WorkflowStateTransitionException { if (context.getUserPrincipal() == null) { throw new WorkflowStateTransitionException(USER_IS_NOT_AUTHENTICATED); } String username = context.getUserPrincipal().getName(); UserAccount userAccount = null; try { userAccount = userFacade.findById(username); } catch (DataNotFoundException ex) { throw new WorkflowStateTransitionException(ex); } dk.i2m.converge.core.workflow.Outlet outlet = null; try { outlet = outletFacade.findOutletById(outletId); } catch (DataNotFoundException ex) { throw new WorkflowStateTransitionException(ex); } if (!outlet.isValid()) { throw new WorkflowStateTransitionException("Outlet #" + outletId + " has not been configured properly"); } dk.i2m.converge.core.content.NewsItem newsItem = new dk.i2m.converge.core.content.NewsItem(); dk.i2m.converge.core.workflow.Workflow workflow = outlet.getWorkflow(); dk.i2m.converge.core.content.NewsItemActor nia = new dk.i2m.converge.core.content.NewsItemActor(); nia.setRole(workflow.getStartState().getActorRole()); nia.setUser(userAccount); nia.setNewsItem(newsItem); newsItem.getActors().add(nia); newsItem.setLanguage(outlet.getLanguage()); newsItem.setTitle(title); newsItem.setOutlet(outlet); newsItem = newsItemFacade.start(newsItem); return newsItem.getId(); } /** * Gets all complete {@link NewsItem}s for a given edition. * * @param id Unique identifier of the edition * @return {@link List} of complete {@link NewsItem}s in the given edition */ @WebMethod(operationName = "getNewsItemsForEdition") public List<dk.i2m.converge.ws.model.NewsItem> getNewsItemsForEdition(@WebParam(name = "editionId") Long id) { List<dk.i2m.converge.ws.model.NewsItem> newsItems = new ArrayList<dk.i2m.converge.ws.model.NewsItem>(); try { Edition edition = outletFacade.findEditionById(id); for (NewsItemPlacement placement : edition.getPlacements()) { if (placement.getNewsItem().isEndState()) { newsItems.add(ModelConverter.toNewsItem(placement)); } } } catch (DataNotFoundException ex) { LOG.log(Level.SEVERE, ex.getMessage()); LOG.log(Level.FINEST, null, ex); } return newsItems; } /** * Gets the assignments for the authenticated user. * * @return {@link List} of {@link NewsItem}s representing the current * assignments of the authenticated user */ @WebMethod(operationName = "getAssignments") public List<dk.i2m.converge.ws.model.NewsItem> getAssignments() { List<dk.i2m.converge.ws.model.NewsItem> output = new ArrayList<dk.i2m.converge.ws.model.NewsItem>(); if (context.getUserPrincipal() == null) { LOG.log(Level.WARNING, USER_IS_NOT_AUTHENTICATED); return output; } String username = context.getUserPrincipal().getName(); LOG.log(Level.INFO, "Fetching assignments for {0}", username); List<InboxView> assignments = newsItemFacade.findInbox(username); LOG.log(Level.INFO, "{0} items for {1}", new Object[]{assignments.size(), username}); for (InboxView assignment : assignments) { try { // TODO: Inefficient to check out each item. Create query similar to findInbox that will fetch required fields dk.i2m.converge.core.content.NewsItem newsItem = newsItemFacade. findNewsItemById(assignment.getId()); for (dk.i2m.converge.core.content.NewsItemPlacement nip : newsItem.getPlacements()) { dk.i2m.converge.ws.model.NewsItem ni = ModelConverter. toNewsItem(nip); output.add(ni); } } catch (DataNotFoundException ex) { LOG.log(Level.SEVERE, "NewsItem in InboxView could not be found in database. {0}", ex.getMessage()); LOG.log(Level.FINEST, null, ex); } } return output; } /** * Gets the privileged outlets for the authenticated user. * * @return {@link List} of privileged {@link Outlet}s for the authenticated * user */ @WebMethod(operationName = "getOutlets") public List<dk.i2m.converge.ws.model.Outlet> getOutlets() { List<dk.i2m.converge.ws.model.Outlet> output = new ArrayList<dk.i2m.converge.ws.model.Outlet>(); if (context.getUserPrincipal() == null) { LOG.log(Level.WARNING, USER_IS_NOT_AUTHENTICATED); return output; } String username = context.getUserPrincipal().getName(); UserAccount userAccount; try { userAccount = userFacade.findById(username); } catch (DataNotFoundException ex) { LOG.log(Level.SEVERE, ex.getMessage()); LOG.log(Level.FINEST, null, ex); return output; } for (dk.i2m.converge.core.workflow.Outlet outlet : userAccount. getPrivilegedOutlets()) { output.add(ModelConverter.toOutlet(outlet)); } return output; } @WebMethod(operationName = "addNewsItemToEdition") public void addNewsItemToEdition( @WebParam(name = "newsItemId") Long newsItemId, @WebParam(name = "editionId") Long editionId, @WebParam(name = "sectionId") Long sectionId, @WebParam(name = "start") Integer start, @WebParam(name = "position") Integer position) throws DataNotFoundException { Section section = null; Edition edition = outletFacade.findEditionById(editionId); NewsItem newsItem = newsItemFacade.findNewsItemById(newsItemId); try { section = outletFacade.findSectionById(sectionId); } catch (DataNotFoundException ex) { LOG.log(Level.SEVERE, ex.getMessage()); LOG.log(Level.FINEST, null, ex); } NewsItemPlacement placement = new NewsItemPlacement(); placement.setEdition(edition); placement.setNewsItem(newsItem); placement.setSection(section); placement.setStart(start); placement.setPosition(position); placement.setOutlet(edition.getOutlet()); newsItemFacade.createPlacement(placement); } @WebMethod(operationName = "addConceptToNewsItem") public void addConceptToNewsItem(@WebParam(name = "newsItemId") Long newsItemId, @WebParam(name = "conceptId") Long conceptId) { try { NewsItemHolder newsItemHolder = newsItemFacade.checkout(newsItemId); Concept concept = metaDataFacade.findConceptById(conceptId); newsItemHolder.getNewsItem().getConcepts().add(concept); newsItemFacade.checkin(newsItemHolder.getNewsItem()); } catch (LockingException ex) { LOG.log(Level.SEVERE, ex.getMessage()); LOG.log(Level.FINEST, null, ex); } catch (DataNotFoundException ex) { LOG.log(Level.SEVERE, ex.getMessage()); LOG.log(Level.FINEST, null, ex); } } /** * Checks out a {@link NewsItem} using its unique identifier. Upon checking * out the {@link NewsItem} it becomes locked for editing by other users. * * @param id Unique identifier of the {@link NewsItem} * @return {@link dk.i2m.converge.ws.model.NewsItem} matching the unique * identifier * @throws NewsItemNotFoundException If the requested {@link NewsItem} could * not be found * @throws NewsItemLockingException If the requested {@link NewsItem} is * locked by another user * @throws NewsItemReadOnlyException If the requested {@link NewsItem} is * not in a state for being checked out */ @WebMethod(operationName = "checkout") public dk.i2m.converge.ws.model.NewsItem checkout(Long id) throws NewsItemNotFoundException, NewsItemLockingException, NewsItemReadOnlyException { dk.i2m.converge.ws.model.NewsItem output = null; if (context.getUserPrincipal() == null) { LOG.log(Level.WARNING, USER_IS_NOT_AUTHENTICATED); return output; } try { NewsItemHolder nih = newsItemFacade.checkout(id); if (!nih.isCheckedOut()) { throw new NewsItemLockingException(id + " is checked out by another user"); } if (nih.isReadOnly()) { throw new NewsItemReadOnlyException( id + " cannot be checked out. You do not have permission to edit the story."); } for (NewsItemPlacement nip : nih.getNewsItem().getPlacements()) { // Override the last one - keep the latest output = ModelConverter.toNewsItem(nip); } if (output == null) { output = ModelConverter.toNewsItem(nih.getNewsItem()); } } catch (DataNotFoundException ex) { throw new NewsItemNotFoundException(id + " does not exist in the database"); } return output; } /** * Checks in a {@link NewsItem}. * * @param newsItem {@link dk.i2m.converge.ws.model.NewsItem} News item to * check-in * @throws NewsItemNotFoundException If a corresponding {@link NewsItem} * could not be found * @throws NewsItemLockingException If the corresponding {@link NewsItem} is * not locked, one can only check-in an item that has been checked-out */ @WebMethod(operationName = "checkin") public void checkin(dk.i2m.converge.ws.model.NewsItem newsItem) throws NewsItemLockingException, NewsItemNotFoundException { if (context.getUserPrincipal() == null) { LOG.log(Level.WARNING, USER_IS_NOT_AUTHENTICATED); } try { dk.i2m.converge.core.content.NewsItem ni = newsItemFacade. findNewsItemById(newsItem.getId()); ni.setTitle(newsItem.getTitle()); ni.setByLine(newsItem.getByLine()); ni.setStory(newsItem.getStory()); ni.setBrief(newsItem.getBrief()); try { newsItemFacade.checkin(ni); } catch (LockingException ex) { throw new NewsItemLockingException(ex.getMessage()); } } catch (DataNotFoundException ex) { throw new NewsItemNotFoundException(newsItem.getId() + " does not exist in the database"); } } /** * Workflow step for the given NewsItem. * * @param newsItemId Unique identifier of the {@link NewsItem} to step * @param workflowStep Unique identifier of the workflow step to take * @param comment Comment to attach to the workflow step * @throws NewsItemNotFoundException If a corresponding {@link NewsItem} * could not be found * @throws NewsItemWorkflowException If the workflow step is illegal */ @WebMethod(operationName = "step") public void step(@WebParam(name = "newsItemId") Long newsItemId, @WebParam(name = "workflowStepId") Long workflowStep, @WebParam(name = "comment") String comment) throws NewsItemNotFoundException, NewsItemWorkflowException { if (context.getUserPrincipal() == null) { LOG.log(Level.WARNING, USER_IS_NOT_AUTHENTICATED); } try { NewsItemHolder checkout = newsItemFacade.checkout(newsItemId); try { newsItemFacade.step(checkout.getNewsItem(), workflowStep, comment); } catch (WorkflowStateTransitionException ex) { throw new NewsItemWorkflowException(ex); } } catch (DataNotFoundException ex) { throw new NewsItemNotFoundException(newsItemId + " does not exist in the database"); } } }