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.refactoring; import java.io.IOException; import java.io.StringWriter; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.xml.bind.DatatypeConverter; import net.opengeospatial.ows.BoundingBoxType; import net.opengeospatial.ows.CodeType; import net.opengeospatial.wps.ComplexValueType; import net.opengeospatial.wps.DataInputsType; import net.opengeospatial.wps.Execute; import net.opengeospatial.wps.ExecuteResponseType; import net.opengeospatial.wps.IOValueType; import net.opengeospatial.wps.IOValueType.ComplexValueReference; import net.opengeospatial.wps.InputDescriptionType; import net.opengeospatial.wps.LiteralInputType; import net.opengeospatial.wps.LiteralValueType; import net.opengeospatial.wps.OutputDefinitionsType; import net.opengeospatial.wps.ProcessDescriptionType; import net.opengeospatial.wps.ProcessDescriptionType.DataInputs; import net.opengeospatial.wps.ProcessFailedType; import net.opengeospatial.wps.ProcessStartedType; import net.opengeospatial.wps.StatusType; import net.opengeospatial.wps.SupportedComplexDataType; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileSystemManager; import org.eclipse.core.runtime.Assert; 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.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.client.WPSRequest; import org.kalypso.service.wps.i18n.Messages; import org.kalypso.service.wps.internal.KalypsoServiceWPSDebug; 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 */ public class DefaultWPSProcess implements IWPSProcess { /** * The identifier of the service to be called. */ private final String m_identifier; /** * The address of the service. */ private final String m_serviceEndpoint; private final FileSystemManager m_manager; private OutputDefinitionsType m_outputDefinitions; private String m_statusLocation; private ProcessDescriptionType m_processDescription; private ExecuteResponseType m_executionResponse; private Map<String, Object[]> m_output = null; private String m_jobId = ""; /** * The constructor. * * @param identifier * The identifier of the service to be called. * @param serviceEndpoint * The address of the service. */ public DefaultWPSProcess(final String identifier, final String serviceEndpoint, final FileSystemManager manager) { /* Initializing of the given variables. */ m_identifier = identifier; m_serviceEndpoint = serviceEndpoint; m_manager = manager; Assert.isNotNull(m_serviceEndpoint, "No URL to the the service is given."); //$NON-NLS-1$ } /** * Initializes the WPS by getting the process description */ public synchronized ProcessDescriptionType getProcessDescription(final IProgressMonitor monitor) throws CoreException { if (m_processDescription == null) { monitor.setTaskName(Messages.getString("org.kalypso.service.wps.refactoring.DefaultWPSProcess.0")); //$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.refactoring.DefaultWPSProcess.1"), null)); //$NON-NLS-1$ } /* We will always take the first one. */ m_processDescription = processDescriptionList.get(0); } } return m_processDescription; } @Override public synchronized ProcessStatus getProcessStatus() { if (m_executionResponse == null) return ProcessStatus.NONE; final StatusType status = m_executionResponse.getStatus(); if (status.getProcessAccepted() != null) return ProcessStatus.ACCEPED; if (status.getProcessStarted() != null) return ProcessStatus.STARTED; if (status.getProcessSucceeded() != null) return ProcessStatus.SUCEEDED; if (status.getProcessFailed() != null) return ProcessStatus.FAILED; throw new IllegalStateException( Messages.getString("org.kalypso.service.wps.refactoring.DefaultWPSProcess.2") + status.toString()); //$NON-NLS-1$ } @Override public synchronized String getTitle() { if (m_processDescription == null) return "<unknown>"; return m_processDescription.getTitle(); } @Override public synchronized Integer getPercentCompleted() { if (m_executionResponse == null) return null; final StatusType status = m_executionResponse.getStatus(); final String processAccepted = status.getProcessAccepted(); final ProcessStartedType processStarted = status.getProcessStarted(); final ProcessFailedType processFailed = status.getProcessFailed(); final String processSucceeded = status.getProcessSucceeded(); if (processAccepted != null) return 0; if (processStarted != null) return processStarted.getPercentCompleted(); if (processFailed != null) return 100; if (processSucceeded != null) return 100; return 0; } /** * @see org.kalypso.service.wps.client.IWPSProcess#getStatusDescription() */ @Override public synchronized String getStatusDescription() { if (m_executionResponse == null) return null; final StatusType status = m_executionResponse.getStatus(); final String processAccepted = status.getProcessAccepted(); final ProcessStartedType processStarted = status.getProcessStarted(); final ProcessFailedType processFailed = status.getProcessFailed(); final String processSucceeded = status.getProcessSucceeded(); if (processAccepted != null) return processAccepted; if (processStarted != null) return processStarted.getValue(); if (processFailed != null) // TODO: can be done better... return processFailed.getExceptionReport().toString(); if (processSucceeded != null) return processSucceeded; return Messages.getString("org.kalypso.service.wps.refactoring.DefaultWPSProcess.3"); //$NON-NLS-1$ } /** * @see org.kalypso.service.wps.client.IWPSProcess#startProcess(java.util.Map, java.util.List, * org.eclipse.core.runtime.IProgressMonitor) */ @Override public synchronized void startProcess(final Map<String, Object> inputs, final List<String> outputs, IProgressMonitor monitor) throws CoreException { Assert.isTrue(m_executionResponse == null); /* Monitor. */ monitor = SubMonitor.convert(monitor, Messages.getString("org.kalypso.service.wps.refactoring.DefaultWPSProcess.4"), 200); //$NON-NLS-1$ KalypsoServiceWPSDebug.DEBUG.printf("Checking for service URL ...\n"); //$NON-NLS-1$ /* Get the process description. */ final ProcessDescriptionType processDescription = getProcessDescription(monitor); /* Get the input data. */ final DataInputsType dataInputs = createDataInputs(processDescription, inputs); /* Get the output data. */ m_outputDefinitions = WPSUtilities.createOutputDefinitions(processDescription, outputs); /* Loop, until an result is available, a timeout is reached or the user has cancelled the job. */ final String title = processDescription.getTitle(); monitor.setTaskName(Messages.getString("org.kalypso.service.wps.client.WPSRequest.1") + title); //$NON-NLS-1$ final CodeType simulationIdentifier = WPS040ObjectFactoryUtilities.buildCodeType("", m_identifier); // 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, 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"); final String statusLocation = WPSUtilities .convertInternalToClient(resultFile.getURL().toExternalForm()); final StatusType status = WPS040ObjectFactoryUtilities.buildStatusType("Process accepted.", true); m_executionResponse = WPS040ObjectFactoryUtilities.buildExecuteResponseType(simulationIdentifier, status, 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 { m_executionResponse = WPSUtilities.callExecute(m_serviceEndpoint, m_identifier, dataInputs, m_outputDefinitions); // TODO: check status, should now at least be 'accepted' } // TODO: move outside // 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. */ // ProgressUtilities.worked( monitor, 100 ); } 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.refactoring.DefaultWPSProcess.6"), 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$ schema = schemaLocationString; // 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; } throw new UnsupportedOperationException(); } public String getStatusLocation() { return m_statusLocation; } /** * @see org.kalypso.service.wps.client.IWPSProcess#getExecuteResponse() */ @Override public synchronized ExecuteResponseType getExecuteResponse() throws CoreException { switch (getProcessStatus()) { case NONE: return null; case SUCEEDED: case FAILED: return m_executionResponse; default: final String statusLocation = m_executionResponse.getStatusLocation(); m_executionResponse = WPSUtilities.readExecutionResponse(m_manager, statusLocation); m_output = null; return m_executionResponse; } } /** * @see org.kalypso.service.wps.client.IWPSProcess#getResult(java.lang.String) */ @Override public synchronized Object[] getResult(final String id) { if (m_executionResponse == null) return null; if (m_output == null) { // TODO: decide if to recollect if (m_executionResponse != null) m_output = collectOutput(m_executionResponse.getProcessOutputs()); } return m_output.get(id); } /** * Collects the process output.<br> * <br> * <ol> * <li>All files (ComplexValueReference) will be collected with their id.</li> * <li>All literals (LiteralValueType) will be collected with their id.</li> * <li>All bounding boxes (BoundingBoxType) will be collected with their id.</li> * <li>All complex datas (ComplexDataType) will be collected with their id.</li> * </ol> * * @param processOutputs * The process outputs contains the info of the results, which are to be collected. */ private static Map<String, Object[]> collectOutput(final ExecuteResponseType.ProcessOutputs processOutputs) { // TODO: maybe check, if all desired outputs have been created?? final Map<String, Object[]> result = new HashMap<>(); if (processOutputs == null) return result; /* Collect all data for the client. */ final List<IOValueType> ioValues = processOutputs.getOutput(); for (final IOValueType ioValue : ioValues) { final String id = ioValue.getIdentifier().getValue(); final ComplexValueReference complexValueReference = ioValue.getComplexValueReference(); final LiteralValueType literalValue = ioValue.getLiteralValue(); final BoundingBoxType boundingBox = ioValue.getBoundingBoxValue(); final ComplexValueType complexValue = ioValue.getComplexValue(); if (complexValueReference != null) addItem(result, id, complexValueReference); else if (literalValue != null) { final String value = literalValue.getValue(); final String dataType = literalValue.getDataType(); final Object parsedValue = parseValue(value, dataType); addItem(result, id, parsedValue); } else if (boundingBox != null) { addItem(result, id, boundingBox); } else if (complexValue != null) addItem(result, id, complexValue); else throw new IllegalStateException(); } return result; } private static Object parseValue(final String value, final String dataType) { if ("string".equals(dataType)) //$NON-NLS-1$ return DatatypeConverter.parseString(value); if ("int".equals(dataType)) //$NON-NLS-1$ return DatatypeConverter.parseInt(value); if ("double".equals(dataType)) //$NON-NLS-1$ return DatatypeConverter.parseDouble(value); if ("boolean".equals(dataType)) //$NON-NLS-1$ return DatatypeConverter.parseBoolean(value); throw new UnsupportedOperationException("Unknown result type: " + dataType); //$NON-NLS-1$ } private static void addItem(final Map<String, Object[]> map, final String id, final Object value) { final Object[] objects = map.get(id); map.put(id, ArrayUtils.add(objects, value)); } /** * @see org.kalypso.service.wps.client.NonBlockingWPSRequest#cancelJob() */ @Override public IStatus cancelJob() { if (WPSRequest.SERVICE_LOCAL.equals(m_serviceEndpoint) && !"".equals(m_jobId)) { 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 running and local simulations."); } } }