/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/citations/trunk/citations-servlet/servlet/src/java/org/sakaiproject/citation/servlet/CitationServlet.java $ * $Id: CitationServlet.java 111745 2012-08-22 18:51:08Z jimeng@umich.edu $ *********************************************************************************** * * Copyright (c) 2006, 2007, 2008 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.citation.servlet; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.HashMap; import java.util.logging.Logger; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.cheftool.VmServlet; import org.sakaiproject.citation.api.Citation; import org.sakaiproject.citation.api.CitationCollection; import org.sakaiproject.citation.api.CitationService; import org.sakaiproject.citation.api.Schema; import org.sakaiproject.citation.api.ConfigurationService; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.content.api.ContentHostingService; import org.sakaiproject.content.api.ContentResource; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.cover.EntityManager; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.exception.ServerOverloadException; import org.sakaiproject.exception.TypeException; import org.sakaiproject.tool.api.ActiveTool; import org.sakaiproject.tool.api.Session; import org.sakaiproject.tool.api.Tool; import org.sakaiproject.tool.api.ToolException; import org.sakaiproject.tool.cover.ActiveToolManager; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.user.cover.UserDirectoryService; import org.sakaiproject.util.BasicAuth; import org.sakaiproject.util.ParameterParser; import org.sakaiproject.util.ResourceLoader; import org.sakaiproject.util.Validator; import org.sakaiproject.util.Web; /** * * */ //public class CitationServlet extends VelocityPortletPaneledAction public class CitationServlet extends VmServlet { /** * */ public static final String SERVLET_TEMPLATE = "/vm/servlet.vm"; public static final String COMPACT_TEMPLATE = "/vm/compact.vm"; // private String collectionTitle = null; /** Our log (commons). */ private static Log M_log = LogFactory.getLog(CitationServlet.class); /** Resource bundle using current language locale */ protected static ResourceLoader rb = new ResourceLoader("citations"); /** set to true when init'ed. */ // protected boolean m_ready = false; protected BasicAuth basicAuth = null; protected ContentHostingService contentService; protected CitationService citationService; protected ConfigurationService configurationService; protected enum Status { SUCCESS, ERROR; } // /** init thread - so we don't wait in the actual init() call */ // public class CitationServletInit extends Thread // { // protected CitationService m_citationService; // public void setCitationService(CitationService service) // { // this.m_citationService = service; // } // /** // * construct and start the init activity // */ // public CitationServletInit() // { // m_ready = false; // start(); // } // /** // * run the init // */ // public void run() // { // m_ready = true; // } // } /** * initialize the AccessServlet servlet * * @param config * the servlet config parameter * @exception ServletException * in case of difficulties */ public void init( ServletConfig config ) throws ServletException { super.init(config); // startInit(); basicAuth = new BasicAuth(); basicAuth.init(); // get services from ComponentManager contentService = (ContentHostingService) ComponentManager.get("org.sakaiproject.content.api.ContentHostingService"); citationService = (CitationService) ComponentManager.get("org.sakaiproject.citation.api.CitationService"); configurationService = (ConfigurationService) ComponentManager.get("org.sakaiproject.citation.api.ConfigurationService"); } // /** // * Start the initialization process // */ // public void startInit() // { // new CitationServletInit(); // } /** * respond to an HTTP GET request * * @param req * HttpServletRequest object with the client request * @param res * HttpServletResponse object back to the client * @exception ServletException * in case of difficulties * @exception IOException * in case of difficulties */ public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { if(M_log.isDebugEnabled()) { M_log.debug("doGet() " + req.getMethod()); } // process any login that might be present basicAuth.doLogin(req); // catch the login helper requests String option = req.getPathInfo(); String[] parts = option.split("/"); if ((parts.length == 2) && ((parts[1].equals("login")))) { doLogin( req, res, null ); } else { // Setup velocity. setupResponse(req, res); ContentResource resource = null; try { ParameterParser paramParser = (ParameterParser) req .getAttribute(ATTR_PARAMS); resource = findResource(paramParser, option); String resourceUuid = this.contentService.getUuid(resource.getId()); setVmReference("resourceId", resourceUuid, req); boolean fromGoogle = false; Citation citation = findOpenURLVersion01(paramParser); if (citation == null) { citation = findOpenUrlCitation(req); } // set the success flag setVmReference("success", citation != null, req); if (citation != null) { addCitation(resource, citation); setVmReference( "citation", citation, req ); setVmReference("topRefresh", Boolean.TRUE, req ); // TODO } else { // return failure setVmReference("error", rb.getString("error.notfound"), req); } } catch (IdUnusedException iue) { setVmReference("error", rb.getString("error.noid"), req); } catch (ServerOverloadException e) { setVmReference("error", rb.getString("error.unavailable"), req); } catch (PermissionException e) { setVmReference("error", rb.getString("error.permission"), req); } setVmReference("openUrlLabel", configurationService.getSiteConfigOpenUrlLabel(), req); setVmReference("titleProperty", Schema.TITLE, req); // validator setVmReference("xilator", new Validator(), req); // Set near end so we always have something setVmReference( "titleArgs", new String[]{ getCollectionTitle(resource) }, req ); // return the servlet template includeVm( COMPACT_TEMPLATE, req, res ); } } /** * Looks for an OpenURL citation in the request. * @param req * @return */ protected Citation findOpenUrlCitation(HttpServletRequest req) { Citation citation = citationService.addCitation(req); return citation; } /** * * @param req * HttpServletRequest object with the client request * @param res * HttpServletResponse object back to the client * @exception ServletException * in case of difficulties * @exception IOException * in case of difficulties */ public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // process any login that might be present basicAuth.doLogin(req); // catch the login helper posts String option = req.getPathInfo(); String[] parts = option.split("/"); if ((parts.length == 2) && ((parts[1].equals("login")))) { doLogin(req, res, null); } else { // don't handle POSTs sendError(res, HttpServletResponse.SC_NOT_FOUND); } } public void doDelete(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { if(M_log.isDebugEnabled()) { M_log.debug("doDelete() " + req.getMethod()); } // process any login that might be present basicAuth.doLogin(req); // catch the login helper posts String option = req.getPathInfo(); String[] parts = option.split("/"); if ((parts.length == 2) && ((parts[1].equals("login")))) { doLogin(req, res, null); } else if(parts.length < 3) { res.sendError(HttpServletResponse.SC_BAD_REQUEST, rb.getString("savesite.delete.missing_params")); return; } else { String citationId = parts[2]; String resourceUuid = parts[1]; if(M_log.isDebugEnabled()) { M_log.debug("doDelete() citationId == " + citationId + " resourceUuid == " + resourceUuid); } if(resourceUuid == null || resourceUuid.trim().equals("") || citationId == null || citationId.trim().equals("")) { res.sendError(HttpServletResponse.SC_BAD_REQUEST, rb.getString("savesite.delete.missing_params")); return; } String resourceId = contentService.resolveUuid(resourceUuid); if(resourceId == null) { res.sendError(HttpServletResponse.SC_NOT_FOUND, rb.getString("savecite.delete.invalid_uuid")); return; } if(! citationService.allowReviseCitationList(resourceId)) { res.sendError(HttpServletResponse.SC_FORBIDDEN, "savecite.delete.not_permitted"); return; } try { ContentResource resource = this.contentService.getResource(resourceId); String citationCollectionId = new String(resource.getContent()); if(citationCollectionId == null || citationCollectionId.trim().equals("")) { res.sendError(HttpServletResponse.SC_CONFLICT, rb.getString("savecite.delete.invalid_uuid")); return; } CitationCollection collection = this.citationService.getCollection(citationCollectionId ); Citation item = collection.getCitation(citationId); collection.remove(item); this.citationService.save(collection); M_log.debug("doDelete() SUCCESS"); } catch (IdUnusedException e) { res.sendError(HttpServletResponse.SC_NOT_FOUND, rb.getString("savecite.delete.invalid_uuid")); } catch (TypeException e) { res.sendError(HttpServletResponse.SC_CONFLICT, rb.getString("savecite.delete.invalid_uuid")); } catch (PermissionException e) { res.sendError(HttpServletResponse.SC_FORBIDDEN, "savecite.delete.not_permitted"); } catch (ServerOverloadException e) { res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rb.getString("savecite.delete.internal")); } } } public ContentResource findResource(ParameterParser params, String option) throws PermissionException, IdUnusedException { // get the path info String path = params.getPath(); if (path == null) path = ""; // if (!m_ready) // { // sendError( res, HttpServletResponse.SC_SERVICE_UNAVAILABLE ); // } // parse the request path String[] parts = option.split("/"); String resourceUuid = parts[1]; String resourceId = contentService.resolveUuid(resourceUuid); if (resourceId == null) { throw new IdUnusedException(resourceUuid); } // revise permission granted if (!citationService.allowReviseCitationList(resourceId)) { // revise permission denied throw new PermissionException(null, null, null); } ContentResource resource = null; try { resource = contentService.getResource(resourceId); } catch (TypeException e) { // Ignore. if(M_log.isDebugEnabled()) { M_log.debug("TypeException in findResource() " + e.getMessage()); } } return resource; } public void addCitation(ContentResource resource, Citation citation) throws IdUnusedException, ServerOverloadException { String citationCollectionId = new String(resource.getContent()); CitationCollection collection = citationService.getCollection(citationCollectionId); collection.add(citation); citationService.save(collection); } public String getCollectionTitle(ContentResource resource) { String collectionTitle = null; if (resource != null) { //String refStr = resource.getReference(); //Reference ref = EntityManager.newReference(refStr); //collectionTitle = ref.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME); collectionTitle = resource.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME); } return collectionTitle == null || "".equals(collectionTitle)? "your current citation list": collectionTitle; //TODO i18n } public Citation findOpenURLVersion01(ParameterParser params) { String genre = params.getString("genre"); String[] authors = params.getStrings("au"); String title = params.getString("title"); String atitle = params.getString("atitle"); String volume = params.getString("volume"); String issue = params.getString("issue"); String pages = params.getString("pages"); String publisher = params.getString("publisher"); String date = params.getString("date"); String id = params.getString("id"); HashMap map = new HashMap(); map.put("genre", genre); map.put("au", authors); map.put("title", title); map.put("atitle", atitle); map.put("volume", volume); map.put("issue", issue); map.put("pages", pages); map.put("publisher", publisher); map.put("date", date); map.put("id", id); return findOpenURLVersion01(map); } /** * Try and extract a Citation from the request. * * @param req HttpServletRequest object with the client request * @param res HttpServletResponse object back to the client */ public Citation findOpenURLVersion01(Map params) { // http://localhost:8080/savecite/71b84348-5962-4e0e-aa49-4ce5824ed84f // ?sakai.session.key=nada&sid=google&genre=book&au=Siever,+E.&au=Figgins,+S.&au=Love,+R.&au=Robbins,+A. // &title=Linux+in+a+Nutshell&date=2009&publisher=Oreilly+%26+Associates+Inc // Google is't passing much information with the Import into WebLearn // link as can be seen in the difference between the OpenURL version and // our version. // http://localhost:8080/savecite/71b84348-5962-4e0e-aa49-4ce5824ed84f?sakai.session.key=nada&sid=google&genre=article&au=Elsworth,+JD&au=Glover,+V.&au=Reynolds,+GP&au=Sandler,+M.&au=Lees,+AJ&au=Phuapradit,+P.&au=Shaw,+KM&au=Stern,+GM&au=Kumar,+P.&atitle=Deprenyl+administration+in+man:+a+selective+monoamine+oxidase+B+inhibitor+without+the+%E2%80%98cheese+effect%E2%80%99&title=Psychopharmacology&volume=57&issue=1&pages=33-38&date=1978&publisher=Springer // http://oxfordsfx.hosted.exlibrisgroup.com/oxford?sid=google&auinit=JD&aulast=Elsworth&atitle=Deprenyl+administration+in+man:+a+selective+monoamine+oxidase+B+inhibitor+without+the+%E2%80%98cheese+effect%E2%80%99&id=doi:10.1007/BF00426954&title=Psychopharmacology&volume=57&issue=1&date=1978&spage=33&issn=0033-3158 String genre = (String) params.get("genre"); String[] authors = (String[]) params.get("au"); String title = (String) params.get("title"); String atitle = (String) params.get("atitle"); String volume = (String) params.get("volume"); String issue = (String) params.get("issue"); String pages = (String) params.get("pages"); String publisher = (String) params.get("publisher"); String date = (String) params.get("date"); String id = (String) params.get("id"); // do we have enough info for a meaningful citation? if ((title == null || title.trim().equals("")) && (atitle == null || atitle.trim().equals(""))) { // both title AND atitle are null return null; } // force a generic genre if we don't know any better if (genre == null || genre.trim().equals("")) { genre = CitationService.UNKNOWN_TYPE; } Citation citation = citationService.addCitation(genre); String info = "New citation:\n\t genre:\t\t" + genre; // Generally, only books have a title that's the actual title of the // piece. // We'll check to see if there's an atitle; if not, use the title as the // work's title. Otherwise, use the title as the source. if (title != null) { if (atitle != null) { info += "\n\t source title:\t\t" + title; citation.setCitationProperty(Schema.SOURCE_TITLE, title); } else { info += "\n\t title:\t\t" + title; citation.setCitationProperty(Schema.TITLE, title); } } if (atitle != null) { info += "\n\t title:\t\t" + atitle; citation.setCitationProperty(Schema.TITLE, atitle); } if (authors != null && authors.length > 0) { for (int i = 0; i < authors.length; i++) { info += "\n\t au:\t\t" + authors[i]; citation.setCitationProperty(Schema.CREATOR, authors[i]); } } if (volume != null) { info += "\n\t volume:\t\t" + volume; citation.setCitationProperty(Schema.VOLUME, volume); } if (issue != null) { info += "\n\t issue:\t\t" + issue; citation.setCitationProperty(Schema.ISSUE, issue); } if (pages != null) { info += "\n\t pages:\t\t" + pages; citation.setCitationProperty(Schema.PAGES, pages); } if (publisher != null) { info += "\n\t publisher:\t\t" + publisher; citation.setCitationProperty(Schema.PUBLISHER, publisher); } if (date != null) { info += "\n\t date:\t\t" + date; citation.setCitationProperty(Schema.YEAR, date); } if (id != null) { info += "\n\t id:\t\t" + id; citation.setCitationProperty(Schema.ISN, id); } info += "\n"; // M_log.info(info); return citation; } /** * Setup the request/response ready for Velocity. */ protected void setupResponse(HttpServletRequest req, HttpServletResponse res) { // the context wraps our real vm attribute set ResourceProperties props = new org.sakaiproject.util.BaseResourceProperties(); setVmReference("props", props, req); setVmReference("validator", new Validator(), req); setVmReference("tlang", rb, req); res.setContentType("text/html; charset=UTF-8"); String client = req.getParameter("client"); if(client != null && ! client.trim().equals("")) { Locale locale = rb.getLocale(); List<Map<String,String>> clientMaps = configurationService.getSaveciteClientsForLocale(locale); for(Map<String,String> clientMap : clientMaps) { String clientId = clientMap.get("id"); if(clientId == null || clientId.trim().equals("")) { continue; } if(client.trim().equalsIgnoreCase(clientId)) { setVmReference("client", clientMap,req); break; } } } } /** * Make a redirect to the login url. * * @param req * HttpServletRequest object with the client request. * @param res * HttpServletResponse object back to the client. * @param path * The current request path, set ONLY if we want this to be where to redirect the user after successfull login * @throws IOException */ protected void doLogin(HttpServletRequest req, HttpServletResponse res, String path) throws ToolException, IOException { // if basic auth is valid do that if ( basicAuth.doAuth(req,res) ) { //System.err.println("BASIC Auth Request Sent to the Browser "); return; } // get the Sakai session Session session = SessionManager.getCurrentSession(); // set the return path for after login if needed (Note: in session, not tool session, special for Login helper) if (path != null) { // where to go after session.setAttribute(Tool.HELPER_DONE_URL, Web.returnUrl(req, path)); } // check that we have a return path set; might have been done earlier if (session.getAttribute(Tool.HELPER_DONE_URL) == null) { M_log.warn("doLogin - proceeding with null HELPER_DONE_URL"); } // map the request to the helper, leaving the path after ".../options" for the helper ActiveTool tool = ActiveToolManager.getActiveTool("sakai.login"); String context = req.getContextPath() + req.getServletPath() + "/login"; tool.help(req, res, context, "/login"); } /** * Utility method to return errors as the response * * @param res response associated with this request * @param code error code */ protected void sendError(HttpServletResponse res, int code) { try { res.sendError(code); } catch (Throwable t) { M_log.warn("sendError: " + t); } } }