/* 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.arcims; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import com.esri.gpt.catalog.arcims.ImsCatalog; import com.esri.gpt.catalog.arcims.ImsPermissionDao; import com.esri.gpt.catalog.context.CatalogConfiguration; import com.esri.gpt.catalog.discovery.DiscoveryException; import com.esri.gpt.catalog.management.MmdEnums; import com.esri.gpt.catalog.publication.ProcessingContext; import com.esri.gpt.catalog.publication.ProcessorFactory; import com.esri.gpt.catalog.publication.PublicationRequest; import com.esri.gpt.catalog.publication.ResourceProcessor; import com.esri.gpt.catalog.schema.ValidationException; import com.esri.gpt.catalog.search.SearchException; import com.esri.gpt.framework.context.BaseServlet; import com.esri.gpt.framework.context.RequestContext; import com.esri.gpt.framework.http.HttpClientRequest; import com.esri.gpt.framework.jsf.MessageBroker; import com.esri.gpt.framework.security.credentials.CredentialsDeniedException; import com.esri.gpt.framework.security.identity.AuthenticationStatus; import com.esri.gpt.framework.security.identity.IdentityException; import com.esri.gpt.framework.security.principal.Publisher; import com.esri.gpt.framework.util.Val; import com.esri.gpt.framework.xml.XsltTemplate; import com.esri.gpt.server.csw.provider.components.OperationResponse; import com.esri.gpt.server.csw.provider.components.RequestHandler; import com.esri.gpt.server.csw.provider.local.ProviderFactory; /** * Servlet Connector for an AtcIMS MetadataServer */ public class ServletConnector extends BaseServlet { // class variables ============================================================= private static final Logger LOGGER = Logger.getLogger(ServletConnector.class .getName()); // instance variables ========================================================== /** The GPT TO CSW XSLT template *. */ private XsltTemplate axlToCswXsltTemplate; /** The GPT TO CSW XSLT template *. */ private XsltTemplate cswToAxlXsltTemplate; // constructors ================================================================ // properties ================================================================== // methods ===================================================================== /** * Handles a GET request. * * @param request * the servlet request * @param response * the servlet response */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { getLogger().finer("Query string=" + request.getQueryString()); if ("ping".equalsIgnoreCase(request.getParameter("Cmd"))) { response.getWriter().write("IMS v9.3.0"); } else if ("ping".equalsIgnoreCase(request.getParameter("cmd"))) { response.getWriter().write("IMS v9.3.0"); } else if ("getVersion".equalsIgnoreCase(request.getParameter("Cmd"))) { response.getWriter().write("Version=9.3.0\nBuild_Number=514.2159"); } else { response.sendError(HttpServletResponse.SC_NOT_FOUND); } // String axlRequest = readInputCharacters(request); // getLogger().finer("axlRequest:\n"+axlRequest); } /** * Executes a POST request. * * @param request * the servlet request * @param response * the servlet response * @param context * the request context */ protected void execute(HttpServletRequest request, HttpServletResponse response, RequestContext context) throws Exception { // initialize CatalogConfiguration catConfig = context.getCatalogConfiguration(); String sService = getServiceName(request); String axlRequest = readInputCharacters(request); String axlResponse = ""; String sSourceUri = ""; String sServiceNameParam = sService; if (sService.indexOf(":EB:") != -1) { String s[] = sService.split(":EB:"); sService = s[0]; sSourceUri = s[1]; } // ensure that a login request was authenticated if (sService.toLowerCase().equals("login")) { if (!context.getUser().getAuthenticationStatus().getWasAuthenticated()) { throw new CredentialsDeniedException("Invalid credentials."); } } boolean proxyMode = true; // process the request getLogger().finest("axlRequest:\n" + axlRequest); if (axlRequest.length() > 0) { if ((axlRequest.indexOf("<GETCLIENTSERVICES") != -1)) { axlResponse = getClientServices(context); } else if ((sService.length() > 0) && !sService.toLowerCase().equals("login")) { if (sServiceNameParam.indexOf(":EB:") != -1 && axlRequest.indexOf("<PUT_METADATA") != -1) { String parts[] = axlRequest.split("<!--"); String s[] = parts[1].split("-->"); String xml = s[0]; if (axlRequest.indexOf("<THUMBNAIL>") != -1 && xml.indexOf("<Thumbnail/>") != -1) { xml = setThumbnail(xml, axlRequest); axlResponse = publish(context, xml, sSourceUri); } else if (xml.indexOf("<gptAgsUrl>") != -1) { axlResponse = handleAgsUrl(context, xml, sSourceUri); } else { axlResponse = publish(context, xml, sSourceUri); } }else if (proxyMode && sServiceNameParam.indexOf(":EB:") == -1){ axlResponse = makeCswRequestFromAxl(context,request, axlRequest); } else { throw new Exception("Configuration issue: No ArcXML socket client was created."); } } } // write the response getLogger().finest("axlResponse:\n" + axlResponse); if (axlResponse.length() > 0) { writeXmlResponse(response, axlResponse); } } /** * Makes csw requests from axl request and return axl response * @param context the RequestContext * @param request the HttpServletRequest * @param axlRequest the axl request * @return axlResponse the axl response * @throws SearchException * @throws TransformerConfigurationException * @throws TransformerException * @throws DiscoveryException */ public String makeCswRequestFromAxl(RequestContext context, HttpServletRequest request, String axlRequest) throws SearchException, TransformerConfigurationException, TransformerException, DiscoveryException { String axlResponse = ""; if (axlRequest.indexOf("<GET_METADATA><GET_METADATA_DOCUMENT") != -1 || axlRequest.indexOf("<SEARCH_METADATA") != -1) { XsltTemplate template = getAxlToCswXsltTemplate(); Map<String, String> params = new HashMap<String, String>(); if (axlRequest.contains("{thisHHHH-isHH-aHHH-dumm-ydocidHHHhhh}")) { params.put("all", "all"); } int start = axlRequest.indexOf("startresult=\""); int end = 0; String parts = null; if(start != -1){ end = axlRequest.indexOf("\"",start+13); parts = axlRequest.substring(start+13,end); } start = axlRequest.indexOf("maxresults=\""); String max = null; if(start != -1){ end = axlRequest.indexOf("\"",start+12); max = axlRequest.substring(start+12,end); } String cswRequest = template.transform(axlRequest, params); getLogger().finest(" AXL2CSW transformed request : " + cswRequest); String cswResponse = ""; try { RequestHandler handler = ProviderFactory.newHandler(context); OperationResponse resp = handler.handleXML(cswRequest); cswResponse = resp.getResponseXml(); } catch (Exception e) { throw new SearchException(e); } getLogger().finest(" CSW response : " + cswResponse); String metadataUrl = "/csw?service=CSW&request=GetRecordById&version=2.0.2&ElementSetName=full&outputSchema=original&ID="; String requestUrl = request.getRequestURL().toString(); String contextPath = request.getContextPath(); String baseUrl = requestUrl.substring(0, requestUrl.indexOf(contextPath)); metadataUrl = baseUrl + contextPath + metadataUrl; params = new HashMap<String, String>(); params.put("partialMetadataUrl", metadataUrl); params.put("partialThumbnailUrl", baseUrl); if (axlRequest.indexOf("<GET_METADATA><GET_METADATA_DOCUMENT") != -1) { start = axlRequest.indexOf("docid=\""); end = axlRequest.indexOf("/></GET_METADATA>"); String docid = axlRequest.substring(start + 7, end).trim(); docid = docid.substring(0, docid.length() - 1); String url = "/csw?service=CSW&request=GetRecordById&version=2.0.2&ElementSetName=full&outputSchema=original&ID=" + docid; params.put("metadataUrl", url); } if(parts != null && parts.length() > 0){ params.put("startResult", parts); } if(max != null && max.length() > 0){ params.put("maxResults", max); } template = getCswToAxlXsltTemplate(); axlResponse = template.transform(cswResponse, params); } else if (axlRequest.indexOf("<GET_METADATA><GET_ROOT_DATASET") != -1) { StringBuffer sb = new StringBuffer(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") .append( "<ARCXML version=\"1.1\"><RESPONSE><METADATA numresults=\"17\" startresult=\"0\" total=\"17\">") .append( "<METADATA_DATASET name=\"Geoportal\" docid=\"{thisHHHH-isHH-aHHH-dumm-ydocidHHHhhh}\" children=\"true\" siblings=\"false\" folder=\"true\" private=\"false\">") .append( "<ENVELOPE minx=\"-180\" miny=\"-90\" maxx=\"180\" maxy=\"90.000000083819\" /></METADATA_DATASET></METADATA></RESPONSE></ARCXML>"); axlResponse = sb.toString(); } return axlResponse; } /** * Handle Ags url enpoints to publish catalog services * @param context the RequestContext * @param xml the xml string * @param sSourceUri the source Uri * @return the axl response * @throws Exception */ public String handleAgsUrl(RequestContext context, String xml, String sSourceUri) throws Exception { String axlResponse = ""; String url = xml.substring(xml.indexOf("<gptAgsUrl>") + 11, xml.indexOf("</gptAgsUrl>")).trim(); if (url != null && url.length() > 0){ Publisher publisher = new Publisher(context); HttpClientRequest httpClient = HttpClientRequest.newRequest(); ProcessingContext pContext = new ProcessingContext(context,publisher,httpClient,null,false); ProcessorFactory factory = new ProcessorFactory(); ResourceProcessor processor = factory.interrogate(pContext,url); if (processor == null) { axlResponse = "Unable to process resource."; return axlResponse; } processor.setPublicationMethod(MmdEnums.PublicationMethod.batch.toString()); processor.process(); int numCreated = pContext.getNumberCreated(); int numReplaced = pContext.getNumberReplaced(); int numFailed = pContext.getNumberFailed(); int numDeleted = pContext.getNumberDeleted(); int numUnchanged = pContext.getNumberUnchanged(); StringBuffer sb = new StringBuffer(); sb .append( "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ARCXML version=\"1.1\">") .append("<RESPONSE><METADATA_ACTION>").append( "Publication Summary : ").append(numCreated) .append(" created, ").append(numReplaced).append( " replaced and ").append(numFailed).append( " failed").append( "</METADATA_ACTION></RESPONSE></ARCXML>"); axlResponse = sb.toString(); } else { axlResponse = "Error:ArcGIS Server/Service url is invalid."; } return axlResponse; } /** * Gets the axl to csw xslt template. * * @return the axl to csw xslt template * * @throws SearchException * the search exception * @throws Searchception * xlst template not initialized by configuration */ public XsltTemplate getAxlToCswXsltTemplate() throws SearchException { if(this.axlToCswXsltTemplate != null) { return this.axlToCswXsltTemplate; } String path = "gpt/search/axl2csw.xslt"; Exception tmpltException = null; if(this.axlToCswXsltTemplate != null) { return this.axlToCswXsltTemplate; } try { this.axlToCswXsltTemplate = XsltTemplate.makeTemplate(path); } catch (TransformerConfigurationException e) { tmpltException = e; } if(tmpltException != null) { throw new SearchException("Could not make xslt template from path " + path , tmpltException ); } return this.axlToCswXsltTemplate; } /** * Gets the axl to csw xslt template. * * @return the axl to csw xslt template * * @throws SearchException the search exception * @throws Searchception xlst template not initialized by configuration */ public XsltTemplate getCswToAxlXsltTemplate() throws SearchException { if(this.cswToAxlXsltTemplate != null) { return this.cswToAxlXsltTemplate; } String path = "gpt/search/csw2axl.xslt"; Exception tmpltException = null; if(this.cswToAxlXsltTemplate != null) { return this.cswToAxlXsltTemplate; } try { this.cswToAxlXsltTemplate = XsltTemplate.makeTemplate(path); } catch (TransformerConfigurationException e) { tmpltException = e; } if(tmpltException != null) { throw new SearchException("Could not make xslt template from path " + path , tmpltException ); } return this.cswToAxlXsltTemplate; } /** * Executes a publish request for GPT publish client tool * * @param context * the request context * @param xml * the metadata xml string * @param sourceUri * the source uri for metadata document * @return axlResponse the response message for the publication request */ private String publish(RequestContext context, String xml, String sourceUri) { String axlResponse = ""; getLogger().finer("Intercepting publication request..."); try { // prepare the publisher Publisher publisher = new Publisher(context); // publication request PublicationRequest publishRequest = new PublicationRequest(context, publisher, xml); publishRequest.getPublicationRecord().setSourceFileName(sourceUri); publishRequest.getPublicationRecord().setPublicationMethod( MmdEnums.PublicationMethod.batch.toString()); publishRequest.publish(); boolean bReplaced = publishRequest.getPublicationRecord() .getWasDocumentReplaced(); StringBuffer sb = new StringBuffer(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); sb.append("<ARCXML version=\"1.1\"><RESPONSE><METADATA_ACTION>"); if (bReplaced) { sb.append("REPLACED"); } else { sb.append("OK"); } sb.append("</METADATA_ACTION></RESPONSE></ARCXML>"); axlResponse = sb.toString(); } catch (IdentityException e) { StringBuffer sb = new StringBuffer(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); sb .append("<ARCXML version=\"1.1\"><RESPONSE><ERROR>Error:Authentication failed.</ERROR></RESPONSE></ARCXML>"); axlResponse = sb.toString(); } catch (ValidationException e) { StringBuffer sb = new StringBuffer(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); sb.append("<ARCXML version=\"1.1\"><RESPONSE><ERROR>"); boolean bProcessMessages = true; if (bProcessMessages) { MessageBroker messageBroker = new MessageBroker(); messageBroker.setBundleBaseName("gpt.resources.gpt"); ArrayList<String> messages = new ArrayList<String>(); e.getValidationErrors().buildMessages(messageBroker, messages, true); sb.append("ValidationError:Metadata publication failed."); boolean bFirst = true; for (String error : messages) { sb.append("<VALIDATIONERROR><![CDATA["); if (bFirst) sb.append("(").append(e.getKey()).append(") "); sb.append(error).append("]]></VALIDATIONERROR>"); bFirst = false; } } else { String error = Val.chkStr(e.getMessage()); sb.append("<![CDATA[ValidationError:Metadata publication failed. " + error + "]]>"); } sb.append("</ERROR></RESPONSE></ARCXML>"); axlResponse = sb.toString(); } catch (Throwable e) { String error = Val.chkStr(e.getMessage()); StringBuffer sb = new StringBuffer(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); sb.append("<ARCXML version=\"1.1\"><RESPONSE><ERROR>"); sb.append("<![CDATA[Error:Metadata publication failed. " + error + "]]>"); sb.append("</ERROR></RESPONSE></ARCXML>"); axlResponse = sb.toString(); } return axlResponse; } /** * Executes a publish request for GPT publish client tool * * @param xml * the metadata xml string * @param axlRequest * the ArcXML publish metadata request * @return newXml the modified metadata xml string */ private String setThumbnail(String xml, String axlRequest) { String[] parts = axlRequest.split("<THUMBNAIL>"); String[] s = parts[1].split("</THUMBNAIL>"); String newXml = xml.replace("<Thumbnail/>", "<Thumbnail><Data EsriPropertyType=\"Picture\">" + s[0] + "</Data></Thumbnail>"); return newXml; } /** * Returns the GETCLIENTSERVICES axl. * * @param context * the request context * @return the GETCLIENTSERVICES axl */ private String getClientServices(RequestContext context) { ImsCatalog catalog = context.getCatalogConfiguration().getArcImsCatalog(); AuthenticationStatus authStatus = context.getUser().getAuthenticationStatus(); boolean bIsAdministrator = authStatus.getAuthenticatedRoles().hasRole( "gptAdministrator"); boolean bIsPublisher = authStatus.getAuthenticatedRoles().hasRole( "gptPublisher"); StringBuffer sb = new StringBuffer(); sb.append("<ARCXML version=\"1.0\">\n"); sb.append("<RESPONSE><SERVICES>"); String sService = catalog.getBrowseService().getServiceName(); if (sService.length() > 0) { String sRole = "metadata_browser_all"; sb.append("<SERVICE ACCESS=\"PUBLIC\" DESC=\"\""); sb.append(" NAME=\"").append(sService).append("\""); sb.append(" SERVICEGROUP=\"MetadataServer1\" STATUS=\"ENABLED\""); sb.append(" roles=\"").append(sRole).append("\""); sb.append(" TYPE=\"MetadataServer\" VERSION=\"\" group=\"*\">"); sb.append("<IMAGE TYPE=\"xml,gnd,jpg\"/>"); sb .append("<ENVIRONMENT><LOCALE country=\"US\" language=\"en\" variant=\"\"/><UIFONT name=\"Arial\"/></ENVIRONMENT>"); sb.append("<CLEANUP INTERVAL=\"10\"/>"); sb.append("</SERVICE>"); } sService = catalog.getPublishService().getServiceName(); if ((sService.length() > 0) && (bIsAdministrator || bIsPublisher)) { String sRole = ImsPermissionDao.ROLE_PUBLISHER; if (bIsAdministrator) sRole = ImsPermissionDao.ROLE_ADMINISTRATOR; sb.append("<SERVICE ACCESS=\"PUBLIC\" DESC=\"\""); sb.append(" NAME=\"").append(sService).append("\""); sb.append(" SERVICEGROUP=\"MetadataServer1\" STATUS=\"ENABLED\""); sb.append(" roles=\"").append(sRole).append("\""); sb.append(" TYPE=\"MetadataServer\" VERSION=\"\" group=\"*\">"); sb.append("<IMAGE TYPE=\"xml,gnd,jpg\"/>"); sb .append("<ENVIRONMENT><LOCALE country=\"US\" language=\"en\" variant=\"\"/><UIFONT name=\"Arial\"/></ENVIRONMENT>"); sb.append("<CLEANUP INTERVAL=\"10\"/>"); sb.append("</SERVICE>"); } sb.append("</SERVICES></RESPONSE></ARCXML>"); getLogger().finer("GETCLIENTSERVICES response:\n" + sb.toString()); return sb.toString(); } /** * Gets the logger. * * @return the logger */ @Override protected Logger getLogger() { return LOGGER; } /** * Gets the service name from the servlet request. * * @param request * the servlet request * @return the service name */ protected String getServiceName(HttpServletRequest request) { String sServiceName = ""; String sQuery = Val.chkStr(request.getQueryString()); int nIdx = sQuery.toLowerCase().indexOf("servicename="); if (nIdx != -1) { String sTmp = Val.chkStr(sQuery.substring(nIdx + 12)); nIdx = sTmp.indexOf("&"); if (nIdx == -1) { sServiceName = sTmp; } else if (nIdx > 0) { sServiceName = Val.chkStr(sTmp.substring(0, nIdx)); } } return sServiceName; } }