/* See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * Esri Inc. licenses this file to You under the Apache 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.apache.org/licenses/LICENSE-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 com.esri.gpt.control.rest; import com.esri.gpt.catalog.schema.MetadataDocument; import com.esri.gpt.catalog.schema.SchemaException; import com.esri.gpt.catalog.search.ASearchEngine; import com.esri.gpt.catalog.search.SearchCriteria; import com.esri.gpt.catalog.search.SearchEngineFactory; import com.esri.gpt.catalog.search.SearchException; import com.esri.gpt.catalog.search.SearchResult; import com.esri.gpt.catalog.search.SearchResultRecord; import com.esri.gpt.catalog.search.SearchResultRecords; import com.esri.gpt.control.georss.JsonFeedWriter; import com.esri.gpt.control.georss.KmlFeedWriter; import com.esri.gpt.control.georss.KmlFeedWriter.KmlSignatureProvider; import com.esri.gpt.control.georss.RecordSnippetWriter; import com.esri.gpt.control.georss.RestQueryServlet; import com.esri.gpt.control.georss.SearchResultRecordAdapter; import com.esri.gpt.control.georss.SearchResultRecordsAdapter; import com.esri.gpt.framework.collection.StringAttributeMap; import com.esri.gpt.framework.context.BaseServlet; import com.esri.gpt.framework.context.RequestContext; import com.esri.gpt.framework.jsf.FacesContextBroker; import com.esri.gpt.framework.jsf.MessageBroker; import com.esri.gpt.framework.util.LogUtil; import com.esri.gpt.framework.util.Val; import java.io.IOException; import java.io.PrintWriter; import java.net.URLDecoder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * REST servlet. * Provides REST functionality. */ public class RestServlet extends BaseServlet { // class variables ============================================================= /** format parameter key ("f")*/ private static final String FORMAT_KEY = "f"; /** style parameter key ("style") */ private static final String STYLE_KEY = "style"; // constructors ================================================================ /** * Creates instance of the servlet. */ public RestServlet() {} // properties ================================================================== // methods ===================================================================== /** * Initializes servlet. * @param config servlet configuration * @throws ServletException if error initializing servlet */ @Override public void init(ServletConfig config) throws ServletException { super.init(config); } /** * Process the HTTP request request. * @param request HTTP request. * @param response HTTP response. * @param context request context * @throws ServletException if error invoking command. * @throws IOException if error writing to the buffer. */ @Override protected void execute(HttpServletRequest request, HttpServletResponse response, RequestContext context) throws ServletException, IOException { MessageBroker msgBroker = new FacesContextBroker(request,response).extractMessageBroker(); Format format = extractFormat(request); setContentType(response,format); PrintWriter writer = response.getWriter(); // extra params Map<String,String> extraMap = new HashMap<String,String>(); extraMap.put(RestQueryServlet.PARAM_KEY_SHOW_THUMBNAIL, request.getParameter(RestQueryServlet.PARAM_KEY_SHOW_THUMBNAIL)); extraMap.put(RestQueryServlet.PARAM_KEY_SHOW_RELATIVE_URLS, request.getParameter(RestQueryServlet.PARAM_KEY_SHOW_RELATIVE_URLS)); extraMap.put(RestQueryServlet.PARAM_KEY_IS_JSFREQUEST, request.getParameter(RestQueryServlet.PARAM_KEY_IS_JSFREQUEST)); context.getObjectMap().put(RestQueryServlet.EXTRA_REST_ARGS_MAP, extraMap); if(request.getScheme().toLowerCase().equals("https") && extraMap.get(RestQueryServlet.PARAM_KEY_SHOW_THUMBNAIL) == null) { String agent = request.getHeader("user-agent"); if (agent != null && agent.toLowerCase().indexOf("msie") > -1) { extraMap.put(RestQueryServlet.PARAM_KEY_SHOW_THUMBNAIL, "false"); } } try { // extract the id parameters String id = Val.chkStr(request.getParameter("id")); String rid = Val.chkStr(request.getParameter("rid")); if (id.length() == 0) { String tmp = request.getPathInfo().replaceAll("^/", ""); id = URLDecoder.decode(tmp,"UTF-8"); } // create search dao SearchCriteria criteria = new SearchCriteria(); SearchResult result = new SearchResult(); ASearchEngine dao = rid.length()==0? SearchEngineFactory.createSearchEngine(criteria, result, context, msgBroker): SearchEngineFactory.createSearchEngine(criteria, result, context, rid, msgBroker); switch (format) { case html: { String [] styleUrl = extractStyle(request); SearchResultRecord record = dao.getMetadataAsSearchResultRecord(id); printHtml(msgBroker, writer, record, styleUrl); } break; case htmlfragment: { SearchResultRecord record = dao.getMetadataAsSearchResultRecord(id); printHtmlFragment(msgBroker, writer, record); } break; case kml: { SearchResultRecord record = dao.getMetadataAsSearchResultRecord(id); printKml(msgBroker, writer, record); } break; case json: case pjson: { SearchResultRecord record = dao.getMetadataAsSearchResultRecord(id); printPjson(msgBroker, writer, record,format); } break; default: case xml: { ASearchEngine.ARecord aRecord = dao.getARecord(id); String sMetadata = aRecord.getMetadataAsText(); Date lastModified = aRecord.getModifiedDate(); if (lastModified!=null) { response.addHeader("Last-Modified", new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", request.getLocale()).format(lastModified)); } printXml(context, writer, sMetadata); } break; } } catch (Exception ex) { response.setContentType("text/plain;charset=UTF-8"); String s = "Unable to return the document associated with the supplied identifier."; writer.println(s); LogUtil.getLogger().log(Level.SEVERE, "Error getting metadata", ex); } finally { writer.flush(); } } /** * Sets content type according to the format. * @param response HTTP response * @param format format */ private void setContentType(HttpServletResponse response, Format format) { switch (format) { case html: case htmlfragment: response.setContentType("text/html;charset=UTF-8"); break; case kml: response.setContentType("application/vnd.google-earth.kml+xml;charset=UTF-8"); response.setHeader("Content-Disposition","attachment; filename=\"document.kml\""); break; case json: response.setContentType("application/json;charset=UTF-8"); response.setHeader("Content-disposition", "attachment; filename=\"document.json\""); break; case pjson: response.setContentType("text/plain;charset=UTF-8"); break; default: case xml: response.setContentType("text/xml;charset=UTF-8"); break; } } /** * Extracts response format. * @param request HTTP request * @return response format */ private Format extractFormat(HttpServletRequest request) { return Format.checkValueOf(getParameterByKey(request,FORMAT_KEY)); } /** * Extracts style url. * @param request HTTP request * @return array of styles URL's */ private String[] extractStyle(HttpServletRequest request) { return getParameterByKey(request,STYLE_KEY).split(","); } /** * Prints document as HTML * @param msgBroker the message broker * @param writer writer * @param record record * @param styleUrl array of styles URL's * @throws SearchException if extracting document failed */ private void printHtml(MessageBroker msgBroker, PrintWriter writer, SearchResultRecord record, String [] styleUrl) throws SearchException { String sLang = msgBroker.getLocale().getLanguage(); writer.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"); writer.println("<html lang=\"" +sLang+ "\">"); writer.println("<head>"); writer.println("<title>" +record.getTitle()+ "</title>"); for (String style : styleUrl) { style = Val.chkStr(style); if (style.length()>0) { writer.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"" +style+ "\"/>"); } } writer.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>"); writer.println("</head>"); writer.println("<body>"); printHtmlFragment(msgBroker, writer, record); writer.println("</body>"); writer.println("</html>"); } /** * Prints document as HTML fragment * @param msgBroker the message broker * @param writer writer * @param record record * @throws SearchException if extracting document failed */ private void printHtmlFragment(MessageBroker msgBroker, PrintWriter writer, SearchResultRecord record) throws SearchException { RecordSnippetWriter snippetWriter = new RecordSnippetWriter(msgBroker, writer); snippetWriter.setShowTitle(true); snippetWriter.setShowIcon(true); snippetWriter.setClipText(true); snippetWriter.write(new SearchResultRecordAdapter(record)); } /** * Prints document as XML * @param writer writer * @param sMetadata metadata as string * @throws SearchException if extracting document failed * @throws SchemaException if parsing document failed */ private void printXml(RequestContext context, PrintWriter writer, String sMetadata) throws SearchException, SchemaException { StringAttributeMap params = context.getCatalogConfiguration().getParameters(); String s = Val.chkStr(params.getValue("RestServlet.printXml.stripStyleSheets")); boolean bStripStyleSheets = s.equalsIgnoreCase("true"); if (bStripStyleSheets) { //sMetadata = sMetadata.replaceAll("<\\?xml\\-stylesheet.*\\?>|<\\!DOCTYPE.*>",""); sMetadata = sMetadata.replaceAll("<\\?xml\\-stylesheet.+?>|<\\!DOCTYPE.+?>",""); } MetadataDocument document = new MetadataDocument(); String sXml = document.prepareForFullViewing(sMetadata); writer.write(sXml); } /** * Gets parameter value. * @param request HTTP request * @param parameterKey parameter key * @return parameter name */ private String getParameterByKey(HttpServletRequest request, String parameterKey) { Map<String, String[]> parMap = request.getParameterMap(); for (Map.Entry<String, String[]> e : parMap.entrySet()) { if (e.getKey().equalsIgnoreCase(parameterKey)) { if (e.getValue().length > 0) { return Val.chkStr(e.getValue()[0]); } else { return ""; } } } return ""; } /** * Prints records as KML. * @param msgBroker message broker * @param writer underlying writer * @param record record to write */ private void printKml(MessageBroker msgBroker, PrintWriter writer, final SearchResultRecord record) { SearchResultRecords records = new SearchResultRecords(); records.add(record); KmlFeedWriter kmlWriter = new KmlFeedWriter(msgBroker, writer); kmlWriter.setKmlSignatureProvider(new KmlSignatureProvider() { public String getTitle() { return record.getTitle(); } public String getDescription() { return record.getAbstract(); } }); kmlWriter.write(new SearchResultRecordsAdapter(records)); } /** * Prints records as KML. * @param msgBroker message broker * @param writer underlying writer * @param record record to write */ private void printPjson(MessageBroker msgBroker, PrintWriter writer, final SearchResultRecord record, Format format) { SearchResultRecords records = new SearchResultRecords(); records.add(record); JsonFeedWriter jsonWriter = new JsonFeedWriter(writer, null, format == Format.pjson ); jsonWriter.setMessageBroker(msgBroker); jsonWriter.write(new SearchResultRecordsAdapter(records)); } // enums ======================================================================= /** * Response format. */ private enum Format { /** XML (full metadata) */ xml, /** HTML */ html, /** HTML FRAGMENT (snippet) */ htmlfragment, /** JSON */ json, /** HTML */ pjson, /** KML */ kml; /** * Checks value. * @param value textual value * @return value. Default: {@link Format#xml} */ public static Format checkValueOf(String value) { value = Val.chkStr(value); for (Format f: values()) { if (f.name().equalsIgnoreCase(value)) { return f; } } return xml; } } }