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.client; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.net.URI; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.xml.bind.JAXBElement; import net.opengeospatial.ows.CodeType; import net.opengeospatial.wps.DataInputsType; import net.opengeospatial.wps.Execute; import net.opengeospatial.wps.ExecuteResponseType; import net.opengeospatial.wps.IOValueType; import net.opengeospatial.wps.InputDescriptionType; import net.opengeospatial.wps.LiteralInputType; import net.opengeospatial.wps.LiteralValueType; 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.DataInputs; import net.opengeospatial.wps.ProcessDescriptionType.ProcessOutputs; import net.opengeospatial.wps.ProcessFailedType; import net.opengeospatial.wps.StatusType; import net.opengeospatial.wps.SupportedComplexDataType; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.vfs2.FileContent; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileSystemManager; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.kalypso.commons.io.VFSUtilities; import org.kalypso.contribs.eclipse.core.runtime.StatusUtilities; import org.kalypso.gmlschema.IGMLSchema; import org.kalypso.ogc.gml.serialize.GmlSerializeException; import org.kalypso.ogc.gml.serialize.GmlSerializer; import org.kalypso.service.ogc.exception.OWSException; import org.kalypso.service.wps.Activator; import org.kalypso.service.wps.client.exceptions.WPSException; import org.kalypso.service.wps.i18n.Messages; import org.kalypso.service.wps.internal.KalypsoServiceWPSDebug; import org.kalypso.service.wps.utils.MarshallUtilities; import org.kalypso.service.wps.utils.WPSUtilities; import org.kalypso.service.wps.utils.WPSUtilities.WPS_VERSION; import org.kalypso.service.wps.utils.ogc.ExecuteMediator; import org.kalypso.service.wps.utils.ogc.ProcessDescriptionMediator; import org.kalypso.service.wps.utils.ogc.WPS040ObjectFactoryUtilities; import org.kalypso.service.wps.utils.simulation.WPSSimulationDataProvider; import org.kalypso.service.wps.utils.simulation.WPSSimulationInfo; import org.kalypso.service.wps.utils.simulation.WPSSimulationManager; import org.kalypso.simulation.core.SimulationException; import org.kalypsodeegree.model.feature.GMLWorkspace; /** * This class manages the connect between the client and the server.<br> * It polls regularly and checks the status of the calculation, that can be retrieved from it then.<br> * Furthermore it has the ability to be canceled.<br> * * @author Holger Albert * @deprecated currently working on a refactoring of the wps service see * {@link org.kalypso.service.wps.refactoring.IWPSProcess} */ @Deprecated public class NonBlockingWPSRequest { /** * The identifier of the service to be called. */ private final String m_identifier; private String m_jobId = ""; //$NON-NLS-1$ /** * The address of the service. */ private final String m_serviceEndpoint; private DataInputsType m_dataInputs; private OutputDefinitionsType m_outputDefinitions; private String m_statusLocation; private ProcessDescriptionType m_processDescription; /** * The constructor. * * @param identifier * The identifier of the service to be called. * @param serviceEndpoint * The address of the service. * @param timeout * After this period of time, the job gives up, waiting for a result of the service. */ public NonBlockingWPSRequest(final String identifier, final String serviceEndpoint) { /* Initializing of the given variables. */ m_identifier = identifier; m_serviceEndpoint = serviceEndpoint; } /** * Initializes the WPS by getting the process description */ public ProcessDescriptionType getProcessDescription(final IProgressMonitor monitor) throws CoreException { if (m_processDescription == null) { initProcessDescription(monitor); } return m_processDescription; } /** * Use {@link #getProcessDescription(IProgressMonitor)} instead. */ @Deprecated public ProcessDescriptionType getProcessDescription() throws CoreException { if (m_processDescription == null) { initProcessDescription(null); } return m_processDescription; } /** * This function initializes the request.<br> * TODO: let it throw an CoreException instead of returning the status; makes error handling much easier. * * @param monitor * A progress monitor. * @return A status, indicating the success of the function. */ public IStatus init(final Map<String, Object> inputs, final List<String> outputs, final IProgressMonitor monitor) { try { /* Get the process description. */ final ProcessDescriptionType processDescription = getProcessDescription(monitor); /* Get the input data. */ m_dataInputs = createDataInputs(processDescription, inputs); /* Get the output data. */ m_outputDefinitions = createOutputDefinitions(processDescription, outputs); } catch (final CoreException e) { return e.getStatus(); } return Status.OK_STATUS; } private void initProcessDescription(IProgressMonitor monitor) throws CoreException { KalypsoServiceWPSDebug.DEBUG.printf("Initializing ...\n"); //$NON-NLS-1$ /* Monitor. */ monitor = SubMonitor.convert(monitor, Messages.getString("org.kalypso.service.wps.client.NonBlockingWPSRequest.0"), 300); //$NON-NLS-1$ KalypsoServiceWPSDebug.DEBUG.printf("Asking for a process description ...\n"); //$NON-NLS-1$ // decide between local and remote invocation if (WPSRequest.SERVICE_LOCAL.equals(m_serviceEndpoint)) { final ProcessDescriptionMediator processDescriptionMediator = new ProcessDescriptionMediator( WPS_VERSION.V040); m_processDescription = processDescriptionMediator.getProcessDescription(m_identifier); } else { final List<ProcessDescriptionType> processDescriptionList = WPSUtilities .callDescribeProcess(m_serviceEndpoint, m_identifier); if (processDescriptionList.size() != 1) { throw new CoreException(StatusUtilities.createStatus(IStatus.ERROR, Messages.getString("org.kalypso.service.wps.client.NonBlockingWPSRequest.1"), null)); //$NON-NLS-1$ } /* Monitor. */ monitor.worked(300); final ProcessDescriptionType processDescription = processDescriptionList.get(0); final String receivedIdentifier = processDescription.getIdentifier().getValue(); if (!ObjectUtils.equals(m_identifier, receivedIdentifier)) { final String msg = String.format( "DescribeProcess returned wrong process-identifier. Received '%s', asked for '%s'.", receivedIdentifier, m_identifier); throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, -1, msg, null)); } /* We will always take the first one. */ m_processDescription = processDescriptionList.get(0); } } /** * this function forwards the functionality of cancel of active job from the member wpsRequest fixes the bug #242, in * actual situation works only with local jobs and was tested only on windows machine. this class is already signed as * deprecated, so complete functionality test will not be done */ public IStatus cancelJob() { if (WPSRequest.SERVICE_LOCAL.equals(m_serviceEndpoint)) { final WPSSimulationManager instance = WPSSimulationManager.getInstance(); try { final WPSSimulationInfo job = instance.getJob(m_jobId); job.cancel(); return Status.CANCEL_STATUS; } catch (final SimulationException e) { return StatusUtilities.statusFromThrowable(e, "Simulation could not be cancelled."); } } else { return StatusUtilities.createErrorStatus("Canceling only possible for local simulations."); } } /** * Starts the simulation. * * @param monitor * The progress monitor. */ public IStatus run(IProgressMonitor monitor) { // TODO: clear old results: No not here! We have to introduce additional operation to the service // So the client can tell the server to release any resources. /* Monitor. */ monitor = SubMonitor.convert(monitor, Messages.getString("org.kalypso.service.wps.client.NonBlockingWPSRequest.4"), 200); //$NON-NLS-1$ KalypsoServiceWPSDebug.DEBUG.printf("Checking for service URL ...\n"); //$NON-NLS-1$ /* Check, if we have a service endpoint. */ if (m_serviceEndpoint == null) { KalypsoServiceWPSDebug.DEBUG.printf("No URL to the service is given.\n"); //$NON-NLS-1$ return StatusUtilities.statusFromThrowable( new WPSException(Messages.getString("org.kalypso.service.wps.client.NonBlockingWPSRequest.3"))); //$NON-NLS-1$ } /* Send the request. */ ExecuteResponseType executeResponse; final CodeType simulationIdentifier = WPS040ObjectFactoryUtilities.buildCodeType("", m_identifier); //$NON-NLS-1$ try { // decide between local and remote invocation if (WPSRequest.SERVICE_LOCAL.equals(m_serviceEndpoint)) { FileObject resultFile = null; try { /* Execute the simulation via a manager, so that more than one simulation can be run at the same time. */ final Execute execute = WPS040ObjectFactoryUtilities.buildExecute(simulationIdentifier, m_dataInputs, m_outputDefinitions, true, true); final WPSSimulationManager manager = WPSSimulationManager.getInstance(); final ExecuteMediator executeMediator = new ExecuteMediator(execute); final WPSSimulationInfo info = manager.startSimulation(executeMediator); m_jobId = info.getId(); /* Prepare the execute response. */ final FileObject resultDir = manager.getResultDir(info.getId()); resultFile = resultDir.resolveFile("executeResponse.xml"); //$NON-NLS-1$ final String statusLocation = WPSUtilities .convertInternalToClient(resultFile.getURL().toExternalForm()); final StatusType status = WPS040ObjectFactoryUtilities.buildStatusType("Process accepted.", //$NON-NLS-1$ true); executeResponse = WPS040ObjectFactoryUtilities.buildExecuteResponseType(simulationIdentifier, status, m_dataInputs, m_outputDefinitions, null, statusLocation, WPSUtilities.WPS_VERSION.V040.toString()); } catch (final IOException e) { throw new CoreException(StatusUtilities.statusFromThrowable(e)); } catch (final SimulationException e) { throw new CoreException(StatusUtilities.statusFromThrowable(e)); } catch (final OWSException e) { throw new CoreException(StatusUtilities.statusFromThrowable(e)); } finally { if (resultFile != null) try { resultFile.close(); } catch (final FileSystemException e) { // gobble } } } else { executeResponse = WPSUtilities.callExecute(m_serviceEndpoint, m_identifier, m_dataInputs, m_outputDefinitions); } } catch (final CoreException e) { return e.getStatus(); } final StatusType status = executeResponse.getStatus(); final ProcessFailedType processFailed = status.getProcessFailed(); if (processFailed != null) { final String errorString = WPSUtilities.createErrorString(processFailed.getExceptionReport()); return StatusUtilities.createErrorStatus(errorString); } /* If the user aborted the job. */ monitor.worked(100); if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } /* Retrieve the path to the status file of the process. */ m_statusLocation = executeResponse.getStatusLocation(); /* Finish. */ monitor.worked(100); return Status.OK_STATUS; } private DataInputsType createDataInputs(final ProcessDescriptionType description, final Map<String, Object> inputs) throws CoreException { /* The storage for the input values. */ final List<IOValueType> inputValues = new LinkedList<>(); /* Get the input list. */ final DataInputs dataInputs = description.getDataInputs(); final List<InputDescriptionType> inputDescriptions = dataInputs.getInput(); /* Iterate over all inputs and build the data inputs for the execute request. */ for (final InputDescriptionType inputDescription : inputDescriptions) { final CodeType identifier = inputDescription.getIdentifier(); /* Check if the input is in our model data, too. */ final String inputId = identifier.getValue(); if (inputs.containsKey(inputId)) { /* Input is here. */ final Object inputValue = inputs.get(inputId); inputValues.add(createDataInput(inputDescription, inputValue)); continue; } /* Check, if it is an optional one. */ if (inputDescription.getMinimumOccurs().intValue() == 1) { /* Ooops, it is a mandatory one, but it is missing in our model data. */ throw new CoreException(StatusUtilities.createErrorStatus( Messages.getString("org.kalypso.service.wps.client.NonBlockingWPSRequest.5", inputId))); //$NON-NLS-1$ } } return WPS040ObjectFactoryUtilities.buildDataInputsType(inputValues); } private IOValueType createDataInput(final InputDescriptionType inputDescription, final Object inputValue) throws CoreException { final CodeType identifier = inputDescription.getIdentifier(); final String inputId = identifier.getValue(); final String title = inputDescription.getTitle(); final String abstrakt = inputDescription.getAbstract(); /* Supported complex data type. */ final SupportedComplexDataType complexData = inputDescription.getComplexData(); final LiteralInputType literalInput = inputDescription.getLiteralData(); // final SupportedCRSsType boundingBoxInput = inputDescription.getBoundingBoxData(); if (complexData != null) { // TODO: we ignore this information at the time, but it should be checked if we can actually send this kind of // data // final String defaultEncoding = complexData.getDefaultEncoding(); // final String defaultFormat = complexData.getDefaultFormat(); // final String defaultSchema = complexData.getDefaultSchema(); // final List<ComplexDataType> supportedComplexData = complexData.getSupportedComplexData(); // for( ComplexDataType complexDataType : supportedComplexData ) // { // final String encoding = complexDataType.getEncoding(); // final String format = complexDataType.getFormat(); // final String schema = complexDataType.getSchema(); // } final IOValueType ioValue = getInputValue(inputValue, inputId, title, abstrakt); /* Add the input. */ return ioValue; } if (literalInput != null) { /* Build the literal value type. */ final CodeType code = WPS040ObjectFactoryUtilities.buildCodeType(null, inputId); final String inputType = literalInput.getDataType().getValue(); final String value = marshalLiteral(inputValue, inputType); final LiteralValueType literalValue = WPS040ObjectFactoryUtilities.buildLiteralValueType(value, inputType, null); final IOValueType ioValue = WPS040ObjectFactoryUtilities.buildIOValueType(code, title, abstrakt, literalValue); /* Add the input. */ return ioValue; } /* Supported CRSs type. */ // TODO: support SupportedCRSsType throw new UnsupportedOperationException(); } private IOValueType getInputValue(final Object inputValue, final String inputId, final String title, final String abstrakt) throws CoreException { final Object valueType; final String format; final String encoding; final String schema; final Object valueString; /* Build the complex value reference. */ if (inputValue instanceof URI) { // this way we can probably not check the format, encoding and schema of the input final URI uri = (URI) inputValue; format = null; encoding = null; schema = null; valueType = WPS040ObjectFactoryUtilities.buildComplexValueReference(uri.toASCIIString(), format, encoding, schema); } /* Build the complex value. */ else { // TODO: support other complex types, e.g. regular XML, geometries, ... if (inputValue instanceof GMLWorkspace) { final GMLWorkspace gmlWorkspace = (GMLWorkspace) inputValue; final IGMLSchema gmlSchema = gmlWorkspace.getGMLSchema(); final String schemaLocationString = gmlSchema.getContext().toString(); format = WPSSimulationDataProvider.TYPE_GML; encoding = "UTF-8"; //$NON-NLS-1$ // TODO: copy the schema to a place where the server can find it // REMARK: makes no sense to give platform: or bundleresource: urls; they do not exist on theother side if (schemaLocationString != null && !schemaLocationString.startsWith("bundleresource:") //$NON-NLS-1$ && !schemaLocationString.startsWith("platform:")) //$NON-NLS-1$ schema = schemaLocationString; else schema = null; // enforce the schemaLocation // TODO: copy the schema to a place where the server can find it gmlWorkspace.setSchemaLocation(schemaLocationString); valueString = getGML(gmlWorkspace); } else { // do not know, maybe it can be marshalled by the binding framework? // TODO: this will probably throw an exception later, maybe throw it now? format = null; encoding = null; schema = null; valueString = inputValue; } // REMARK: hack/convention: the input must now be the raw input for the anyType element final List<Object> value = new ArrayList<>(1); value.add(valueString); valueType = WPS040ObjectFactoryUtilities.buildComplexValueType(format, encoding, schema, value); } final CodeType code = WPS040ObjectFactoryUtilities.buildCodeType(null, inputId); final IOValueType ioValue = WPS040ObjectFactoryUtilities.buildIOValueType(code, title, abstrakt, valueType); return ioValue; } private Object getGML(final GMLWorkspace gmlWorkspace) throws CoreException { final Object valueString; // 0.5 MB text file default buffer final StringWriter stringWriter = new StringWriter(512 * 1024); try { GmlSerializer.serializeWorkspace(stringWriter, gmlWorkspace, "UTF-8", true); //$NON-NLS-1$ } catch (final GmlSerializeException e) { throw new CoreException(StatusUtilities.statusFromThrowable(e)); } valueString = stringWriter.toString(); return valueString; } private String marshalLiteral(final Object literalValue, @SuppressWarnings("unused") final String inputType) { // REMARK: (crude) Hack: if it is already a String, we always return this value. if (literalValue instanceof String) { return (String) literalValue; } // TODO: handle null throw new UnsupportedOperationException(); } /** * This function creates the data outputs the clients expects for the wps. * * @param description * The description of the process. * @param data * The modeldata. * @return The output of the model spec in wps format. */ private OutputDefinitionsType createOutputDefinitions(final ProcessDescriptionType description, final List<String> outputs) { /* The storage for the output values. */ final List<OutputDefinitionType> outputValues = new LinkedList<>(); /* Get the output list. */ final ProcessOutputs processOutputs = description.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 (!outputs.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 String getStatusLocation() { return m_statusLocation; } @SuppressWarnings("unchecked") public ExecuteResponseType getExecuteResponse(final FileSystemManager manager) throws Exception, InterruptedException { final FileObject statusFile = VFSUtilities.checkProxyFor(m_statusLocation, manager); if (statusFile.exists()) { /* Some variables for handling the errors. */ boolean success = false; int cnt = 0; /* Try to read the status at least 3 times, before exiting. */ JAXBElement<ExecuteResponseType> executeState = null; while (success == false) { final FileContent content = statusFile.getContent(); InputStream inputStream = null; try { inputStream = content.getInputStream(); final String xml = IOUtils.toString(inputStream); if (xml != null && !"".equals(xml)) //$NON-NLS-1$ { final Object object = MarshallUtilities.unmarshall(xml); executeState = (JAXBElement<ExecuteResponseType>) object; success = true; } } catch (final Exception e) { /* An error has occured while copying the file. */ KalypsoServiceWPSDebug.DEBUG .printf("An error has occured with the message: " + e.getLocalizedMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ /* If a certain amount (here 2) of retries was reached before, rethrow the error. */ if (cnt >= 2) { KalypsoServiceWPSDebug.DEBUG .printf("The second retry has failed, rethrowing the error ...\n"); //$NON-NLS-1$ throw e; } /* Retry the copying of the file. */ cnt++; KalypsoServiceWPSDebug.DEBUG.printf("Retry: " + String.valueOf(cnt) + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ success = false; /* Wait for some milliseconds. */ Thread.sleep(1000); } finally { IOUtils.closeQuietly(inputStream); statusFile.close(); } } return executeState.getValue(); } else return null; } }