Java tutorial
/*---------------- FILE HEADER KALYPSO ------------------------------------------ * * This file is part of kalypso. * Copyright (C) 2004 by: * * Technical University Hamburg-Harburg (TUHH) * Institute of River and coastal engineering * Denickestrae 22 * 21073 Hamburg, Germany * http://www.tuhh.de/wb * * and * * Bjoernsen Consulting Engineers (BCE) * Maria Trost 3 * 56070 Koblenz, Germany * http://www.bjoernsen.de * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Contact: * * E-Mail: * belger@bjoernsen.de * schlienger@bjoernsen.de * v.doemming@tuhh.de * * ---------------------------------------------------------------------------*/ package org.kalypso.service.wps.utils; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.LinkedList; import java.util.List; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.namespace.QName; import net.opengeospatial.ows.CodeType; import net.opengeospatial.ows.ExceptionReport; import net.opengeospatial.ows.ExceptionType; import net.opengeospatial.wps.DataInputsType; import net.opengeospatial.wps.DescribeProcess; import net.opengeospatial.wps.Execute; import net.opengeospatial.wps.ExecuteResponseType; import net.opengeospatial.wps.OutputDefinitionType; import net.opengeospatial.wps.OutputDefinitionsType; import net.opengeospatial.wps.OutputDescriptionType; import net.opengeospatial.wps.ProcessDescriptionType; import net.opengeospatial.wps.ProcessDescriptionType.ProcessOutputs; import net.opengeospatial.wps.ProcessDescriptions; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.StatusLine; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.io.IOUtils; import org.apache.commons.vfs2.FileContent; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSystemManager; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.osgi.framework.internal.core.FrameworkProperties; import org.kalypso.commons.io.VFSUtilities; import org.kalypso.commons.net.ProxyUtilities; import org.kalypso.commons.xml.NS; import org.kalypso.contribs.eclipse.core.runtime.IStatusCollector; import org.kalypso.contribs.eclipse.core.runtime.StatusCollector; import org.kalypso.contribs.eclipse.core.runtime.StatusUtilities; import org.kalypso.contribs.java.util.Arrays; import org.kalypso.service.ogc.exception.OWSException; import org.kalypso.service.wps.Activator; import org.kalypso.service.wps.i18n.Messages; import org.kalypso.service.wps.internal.KalypsoServiceWPSDebug; import org.kalypso.service.wps.utils.ogc.WPS040ObjectFactoryUtilities; import org.kalypso.simulation.core.ISimulation; import org.kalypso.simulation.core.KalypsoSimulationCoreExtensions; /** * This class contains functions for handling requests and responses via XML and other functions. * * @author Holger Albert */ @SuppressWarnings("restriction") public class WPSUtilities { /** * AnyURI QName. */ public static final QName QNAME_ANY_URI = new QName(NS.XSD_SCHEMA, "anyURI"); //$NON-NLS-1$ /** * Service type identifier. */ public static final String SERVICE = "WPS"; //$NON-NLS-1$ public static enum WPS_VERSION { V100("1.0.0"), //$NON-NLS-1$ V040("0.4.0"); //$NON-NLS-1$ private final String m_version; WPS_VERSION(final String version) { m_version = version; } /** * @see java.lang.Enum#toString() */ @Override public String toString() { return m_version; } public static WPS_VERSION getValue(final String value) { for (final WPS_VERSION version : values()) { if (version.toString().equals(value)) return version; } return null; } } /** * The constructor. */ private WPSUtilities() { } /** * This function is responsible for sending a request to a server. * * @param xml * The XML string to be send. * @param url * The address of the server. * @return The response as String. */ public static String send(final String xml, final String url) throws CoreException, HttpException, IOException { /* Send the request. */ KalypsoServiceWPSDebug.DEBUG.printf("Calling '" + url + "'...\n"); //$NON-NLS-1$ //$NON-NLS-2$ /* Create the client. */ final HttpClient client = ProxyUtilities.getConfiguredHttpClient(25000, new URL(url), 0); /* Build the method. */ final PostMethod post = new PostMethod(url); // TODO: this is maybe a bit heavy, if the request is big (got an OutOfMemory once at marshalling the xml string) // Instead we should provide an overloaded method that accepts the stringRequestEntity from outside (allowing for // stream or file-based entities) post.setRequestEntity(new StringRequestEntity(xml, "text/xml", null)); //$NON-NLS-1$ /* Let the method handle the authentication, if any. */ post.setDoAuthentication(true); /* Execute the method. */ final int status = client.executeMethod(post); /* Handle the response. */ KalypsoServiceWPSDebug.DEBUG.printf("Status code: " + String.valueOf(status) + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ if (status != 200) { final IStatusCollector collector = new StatusCollector(Activator.PLUGIN_ID); final StatusLine statusLine = post.getStatusLine(); collector.add(IStatus.ERROR, "%s", null, statusLine); final String responseBodyAsString = post.getResponseBodyAsString(); collector.add(IStatus.INFO, "Response body was '%s'", null, responseBodyAsString); collector.add(IStatus.INFO, "Post content was '%s'", null, xml); final String msg = Messages.getString("org.kalypso.service.wps.utils.WPSUtilities.0", url, statusLine); //$NON-NLS-1$ final IStatus error = collector.asMultiStatusOrOK(msg); throw new CoreException(error); } final InputStream is = post.getResponseBodyAsStream(); if (is == null) return null; try { return IOUtils.toString(is); } finally { is.close(); } } public static List<ProcessDescriptionType> callDescribeProcess(final String serviceEndpoint, final String... processIds) throws CoreException { /* Build the describe process request. */ final List<CodeType> identifiers = new LinkedList<>(); for (final String processId : processIds) { identifiers.add(WPS040ObjectFactoryUtilities.buildCodeType("", processId)); //$NON-NLS-1$ } final DescribeProcess describeProcess = WPS040ObjectFactoryUtilities.buildDescribeProcess(identifiers); /* Send the request. */ Object describeProcessObject = null; try { final String describeProcessMsg = MarshallUtilities.marshall(describeProcess, WPS_VERSION.V040); final String describeProcessResponse = WPSUtilities.send(describeProcessMsg, serviceEndpoint); /* Try to unmarshall. */ describeProcessObject = MarshallUtilities.unmarshall(describeProcessResponse, WPS_VERSION.V040); } catch (final IOException e) { throw new CoreException(StatusUtilities.statusFromThrowable(e)); } catch (final JAXBException e) { throw new CoreException(StatusUtilities.statusFromThrowable(e)); } // TODO: error handling: our WPS service throws other types of exceptions // (http://www.opengis.net/ows}ExceptionReport) than expected // here {http://www.opengeospatial.net/ows}ExceptionReport. We need to fix that... find out what is correct in // respect to WPS specification if (describeProcessObject instanceof ExceptionReport) { final ExceptionReport report = (ExceptionReport) describeProcessObject; throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, createErrorString(report))); } /* Use the process description for building the DataInputs and the OutputDefinitions. */ final ProcessDescriptions processDescriptions = (ProcessDescriptions) describeProcessObject; /* The descriptions of all processes from the process descriptions response. */ final List<ProcessDescriptionType> processDescriptionList = processDescriptions.getProcessDescription(); /* Check describe process. */ if (processDescriptionList == null || processDescriptionList.size() == 0) throw new CoreException(StatusUtilities.createStatus(IStatus.ERROR, Messages.getString("org.kalypso.service.wps.utils.WPSUtilities.1"), null)); //$NON-NLS-1$ return processDescriptionList; } @SuppressWarnings("unchecked") public static ExecuteResponseType callExecute(final String serviceEndpoint, final String typeID, final DataInputsType dataInputs, final OutputDefinitionsType outputDefinitions) throws CoreException { try { /* Build the execute request. */ final Execute execute = WPS040ObjectFactoryUtilities.buildExecute( WPS040ObjectFactoryUtilities.buildCodeType("", typeID), dataInputs, outputDefinitions, true, //$NON-NLS-1$ true); final String executeRequestString = MarshallUtilities.marshall(execute, WPS_VERSION.V040); final String executeResponseString = WPSUtilities.send(executeRequestString, serviceEndpoint); /* Handle the execute response. */ KalypsoServiceWPSDebug.DEBUG.printf("Response:\n" + executeResponseString + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ if (executeResponseString == null || executeResponseString.length() == 0) throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.getString("org.kalypso.service.wps.utils.WPSUtilities.2"))); //$NON-NLS-1$ final Object response = MarshallUtilities.unmarshall(executeResponseString, WPS_VERSION.V040); if (response instanceof ExceptionReport) throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, WPSUtilities.createErrorString((ExceptionReport) response))); final JAXBElement<ExecuteResponseType> elmt = (JAXBElement<ExecuteResponseType>) response; final ExecuteResponseType executeResponse = elmt.getValue(); return executeResponse; } catch (final JAXBException e) { throw new CoreException(StatusUtilities.statusFromThrowable(e)); } catch (final IOException e) { throw new CoreException(StatusUtilities.statusFromThrowable(e)); } } /** * This function will create an error string, containing all error messages. * * @param exceptionReport * The exception report. * @return The error messages as one string. */ public static String createErrorString(final ExceptionReport exceptionReport) { final List<ExceptionType> exceptions = exceptionReport.getException(); String messages = ""; //$NON-NLS-1$ for (final ExceptionType exception : exceptions) { final List<String> exceptionList = exception.getExceptionText(); final String exceptionText = Arrays.toString(exceptionList.toArray(new String[exceptionList.size()]), "\n"); //$NON-NLS-1$ messages = messages + "Code: " + exception.getExceptionCode() + "\nMessage: " + exceptionText //$NON-NLS-1$//$NON-NLS-2$ + "\nLocator: " + exception.getLocator(); //$NON-NLS-1$ } return messages; } /** * Returns the simulation with the given type id. * * @param simulationType * The simulations type id. * @return The simulation. */ public static ISimulation getSimulation(final String simulationType) throws CoreException { /* Get the simulation. */ KalypsoServiceWPSDebug.DEBUG.printf("Searching for simulation \"" + simulationType + "\" ...\n"); //$NON-NLS-1$ //$NON-NLS-2$ final ISimulation simulation = KalypsoSimulationCoreExtensions.createSimulation(simulationType); if (simulation == null) throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.getString("org.kalypso.service.wps.utils.WPSUtilities.3", simulationType))); //$NON-NLS-1$ return simulation; } /** * This function returns all simulations. * * @return All simulations. */ public static List<ISimulation> getSimulations() throws OWSException { try { return KalypsoSimulationCoreExtensions.createSimulations(); } catch (final CoreException e) { KalypsoServiceWPSDebug.DEBUG.printf("Error retrieving the simulations!\n"); //$NON-NLS-1$ throw new OWSException(OWSException.ExceptionCode.NO_APPLICABLE_CODE, e, ""); //$NON-NLS-1$ } } /** * This function converts the URL that, the server has used and converts it to an URL, which the client can use. * * @param serverUrl * The URL, which the server has used internally to copy the results.<br> * Example:<br> * Server URL: file://var/lib/wwwrun/apache/informdss/htdocs/webdav/results<br> * Replacement URL: http://informdss.bafg.de/webdav/results<br> * ----------------------------------------------------------------------------------<br> * serverUrl param : file://var/lib/wwwrun/apache/informdss/htdocs/webdav/results/xxx<br> * ----------------------------------------------------------------------------------<br> * Result: http://informdss.bafg.de/webdav/results/xxx */ public static String convertInternalToClient(final String serverUrl) { /* If no property for the replacement is set, use the server URL and provide it to the client. */ final String clientProperty = FrameworkProperties.getProperty("org.kalypso.service.wps.client.replacement"); //$NON-NLS-1$ if (clientProperty == null) return serverUrl; final String serverProperty = FrameworkProperties.getProperty("org.kalypso.service.wps.results"); //$NON-NLS-1$ final String clientUrl = serverUrl.replace(serverProperty, clientProperty); KalypsoServiceWPSDebug.DEBUG.printf("Converting " + serverUrl + " to " + clientUrl + " ...\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ return clientUrl; } /** * This function converts the URL, that the client has used to copy the input data and converts it to an URL, which * the server can use to retrieve them. * * @param clientUrl * The URL, which the client has used internally to copy the input data.<br> * Example:<br> * Client URL: webdav://informdss.bafg.de/webdav/input<br> * Replacement URL: http://informdss.bafg.de/webdav/input<br> * ----------------------------------------------------------------------------------<br> * clientUrl param : webdav://informdss.bafg.de/webdav/input/xxx<br> * ----------------------------------------------------------------------------------<br> * Result: http://informdss.bafg.de/webdav/input/xxx * @param clientProperty * The client URL. */ public static String convertInternalToServer(final String clientUrl, final String clientProperty) { /* If no property for the replacement is set, use the client URL and provide it to the server. */ final String serverProperty = FrameworkProperties.getProperty("org.kalypso.service.wps.server.replacement"); //$NON-NLS-1$ if (serverProperty == null) return clientUrl; final String serverUrl = clientUrl.replace(clientProperty, serverProperty); KalypsoServiceWPSDebug.DEBUG.printf("Converting " + clientUrl + " to " + serverUrl + " ...\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ return serverUrl; } /** * This function creates the data outputs the clients expects for the WPS.<br> * Only the excepted outputs are created, other output definitions provided by this process are omitted (filtered * out). * * @param processDescription * The description of the process. * @param expectedOutputs * expected outputs * @return The output of the model spec in wps format. */ public static OutputDefinitionsType createOutputDefinitions(final ProcessDescriptionType processDescription, final List<String> expectedOutputs) { /* The storage for the output values. */ final List<OutputDefinitionType> outputValues = new LinkedList<>(); /* Get the output list. */ final ProcessOutputs processOutputs = processDescription.getProcessOutputs(); final List<OutputDescriptionType> outputDescriptions = processOutputs.getOutput(); /* Iterate over all outputs and build the data inputs for the execute request. */ for (final OutputDescriptionType outputDescription : outputDescriptions) { final CodeType identifier = outputDescription.getIdentifier(); /* Check if the output is in our model data, too. */ if (!expectedOutputs.contains(identifier.getValue())) { /* Ooops, it is missing in our model data. */ // throw new CoreException( StatusUtilities.createErrorStatus( "The data output " + identifier.getValue() + // " is missing. Check your model data." ) ); continue; } // TODO: maybe only ask for outputs that are in the list m_outputs? final CodeType code = WPS040ObjectFactoryUtilities.buildCodeType(null, identifier.getValue()); final OutputDefinitionType outputDefinition = WPS040ObjectFactoryUtilities.buildOutputDefinitionType( code, outputDescription.getTitle(), outputDescription.getAbstract(), null, null, null, null); /* Add the output. */ outputValues.add(outputDefinition); } return WPS040ObjectFactoryUtilities.buildOutputDefinitionsType(outputValues); } public static ExecuteResponseType readExecutionResponse(final FileSystemManager manager, final String statusLocation) throws CoreException { try { final FileObject statusFile = VFSUtilities.checkProxyFor(statusLocation, manager); if (!statusFile.exists()) return null; /* Try to read the status at least 3 times, before exiting. */ Exception lastError = new Exception(); // TODO: timeout defined as approximately 3 seconds is in some how not always usable, set to 100. // Better to set it from predefined properties. // Hi Ilya, I think you missunderstood the number here. // It does not represent a timeout, but the number of times to try. // The Thread.sleep( 1000 ) in case of an error is only the time to wait, // before it is retried to read the execution response. // I changed the value back to 3. Holger for (int i = 0; i < 3; i++) { InputStream inputStream = null; try { final FileContent content = statusFile.getContent(); inputStream = content.getInputStream(); final String xml = IOUtils.toString(inputStream); if (xml == null || "".equals(xml)) //$NON-NLS-1$ throw new IOException(Messages.getString("org.kalypso.service.wps.utils.WPSUtilities.4") //$NON-NLS-1$ + statusFile.toString()); final Object object = MarshallUtilities.unmarshall(xml); final JAXBElement<?> executeState = (JAXBElement<?>) object; return (ExecuteResponseType) executeState.getValue(); } catch (final Exception e) { lastError = e; KalypsoServiceWPSDebug.DEBUG .printf("An error has occured with the message: " + e.getLocalizedMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ KalypsoServiceWPSDebug.DEBUG.printf("Retry: " + String.valueOf(i) + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ Thread.sleep(1000); } finally { IOUtils.closeQuietly(inputStream); statusFile.close(); } } KalypsoServiceWPSDebug.DEBUG.printf("The second retry has failed, rethrowing the error ..."); //$NON-NLS-1$ //$NON-NLS-2$ final IStatus status = StatusUtilities.createStatus(IStatus.ERROR, Messages.getString("org.kalypso.service.wps.utils.WPSUtilities.5") //$NON-NLS-1$ + lastError.getLocalizedMessage(), lastError); throw new CoreException(status); } catch (final Exception e) { e.printStackTrace(); final IStatus status = StatusUtilities.createStatus(IStatus.ERROR, Messages.getString("org.kalypso.service.wps.utils.WPSUtilities.6") + e.getLocalizedMessage(), //$NON-NLS-1$ e); throw new CoreException(status); } } }