/**
* Copyright (C) 2009 - 2016 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* If the program is linked with libraries which are licensed under one of
* the following licenses, the combination of the program with the linked
* library is not considered a "derivative work" of the program:
*
* • Apache License, version 2.0
* • Apache Software License, version 1.0
* • GNU Lesser General Public License, version 3
* • Mozilla Public License, versions 1.0, 1.1 and 2.0
* • Common Development and Distribution License (CDDL), version 1.0
*
* Therefore the distribution of the program linked with libraries licensed
* under the aforementioned licenses, is permitted by the copyright holders
* if the distribution is compliant with both the GNU General Public
* License version 2 and the aforementioned licenses.
*
* This program 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 General
* Public License for more details.
*/
package org.n52.wps.ags;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.opengis.wps.x100.InputDescriptionType;
import net.opengis.wps.x100.OutputDescriptionType;
import net.opengis.wps.x100.ProcessDescriptionType;
import net.opengis.wps.x100.ProcessDescriptionsDocument;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.n52.wps.ags.workspace.AGSWorkspace;
import org.n52.wps.server.IAlgorithm;
import org.n52.wps.server.IAlgorithmRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Comment;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* @author Matthias Mueller, TU Dresden; Christin Henzen, TU Dresden
*
*/
public class AGSProcessRepository implements IAlgorithmRepository {
private static Logger LOGGER = LoggerFactory.getLogger(AGSProcessRepository.class);
private Map<String, ProcessDescriptionType> registeredProcessDescriptions;
private Map<String, ToolParameter[]> registeredAlgorithmParameters;
public AGSProcessRepository() {
LOGGER.info("Initializing ArcGIS Server Repository ...");
//initialize local variables
this.registeredProcessDescriptions = new HashMap<String, ProcessDescriptionType>();
this.registeredAlgorithmParameters = new HashMap<String, ToolParameter[]>();
String describeProcessPathString = AGSProperties.getInstance().getProcessDescriptionDir();
LOGGER.info("Loading AGS process descriptions from: " + describeProcessPathString);
File describeProcessPath = new File(describeProcessPathString);
ArrayList<File> describeProcessFiles = listAllFiles(describeProcessPath,false);
for (File currentFile : describeProcessFiles){
addAlgorithm(currentFile);
}
}
/**
* Checks if a given processID exists
* @param processID
* @return
*/
public boolean containsAlgorithm(String processID) {
return registeredProcessDescriptions.containsKey(processID);
}
/**
* Returns an IAlgorithm through GenericAGSProcessDelegator
* @param processID
* @return
*/
public IAlgorithm getAlgorithm(String processID) {
if (!containsAlgorithm(processID)){
//TODO returning null should be better than throwing an unexpectable exception. Test if it breaks other parts
return null;
}
// create a unique directory for each instance
String randomDirName = AGSProperties.getInstance().getWorkspaceBase() + File.separator + UUID.randomUUID();
return new GenericAGSProcessDelegator(new File (randomDirName), processID, registeredAlgorithmParameters.get(processID), registeredProcessDescriptions.get(processID));
}
private void addAlgorithm(File processDescriptionFile){
ProcessDescriptionType pd = this.loadProcessDescription(processDescriptionFile);
String processID = pd.getIdentifier().getStringValue();
LOGGER.debug("Registering: " + processID);
this.registeredProcessDescriptions.put(processID, pd);
ToolParameter[] params = this.loadParameters(pd);
this.registeredAlgorithmParameters.put(processID, params);
if (!pd.validate()){
LOGGER.debug("ProcessDescription is not valid. Removing " + processID + " from Repository.");
this.registeredProcessDescriptions.remove(processID);
this.registeredAlgorithmParameters.remove(processID);
}
}
public Collection<String> getAlgorithmNames() {
return registeredProcessDescriptions.keySet();
}
private ToolParameter[] loadParameters(ProcessDescriptionType pd){
// if(pd.getIdentifier().getStringValue().contains("buffer")){
// System.out.println("Buffer");
// }
InputDescriptionType[] inputArray = pd.getDataInputs().getInputArray();
OutputDescriptionType[] outputArray = pd.getProcessOutputs().getOutputArray();
int paramCount = inputArray.length + outputArray.length;
//create parameter array
//LegacyParameter[] paramArray = new LegacyParameter[inputArray.length + outputArray.length];
ToolParameter[] paramArray = new ToolParameter[paramCount];
for (InputDescriptionType currentDesc : inputArray){
NodeList nl = currentDesc.getDomNode().getChildNodes();
String pameterID = null;
for (int i = 0; i < nl.getLength(); i++){
Node currentNode = nl.item(i);
if(currentNode instanceof Comment){
pameterID = ((Comment)currentNode).getNodeValue().replaceAll("\\s+", "");
}
}
int index = Integer.parseInt(pameterID);
String mimeType = null;
String schema = null;
String literalDataType = null;
String defaultCRS = null;
String prefixString = null;
String suffixString = null;
String separatorString = " ";
String wpsInputID = currentDesc.getIdentifier().getStringValue();
String wpsOutputID = null;
//complex data
if (currentDesc.isSetComplexData()){
mimeType = currentDesc.getComplexData().getDefault().getFormat().getMimeType();
schema = currentDesc.getComplexData().getDefault().getFormat().getSchema();
}
//literal data
if (currentDesc.isSetLiteralData()){
if (currentDesc.getLiteralData().isSetDataType()){
literalDataType = currentDesc.getLiteralData().getDataType().getStringValue();
}
if (currentDesc.getLiteralData().isSetValuesReference()){
literalDataType = currentDesc.getLiteralData().getDataType().getReference();
}
}
//BBox data
if (currentDesc.isSetBoundingBoxData()){
defaultCRS = currentDesc.getBoundingBoxData().toString();
}
boolean isOptional = false;
if(currentDesc.getMinOccurs().equals(new BigInteger("0"))){
isOptional = true;
}
paramArray[index] = new ToolParameter(wpsInputID, wpsOutputID, pameterID, schema, mimeType, literalDataType, defaultCRS, prefixString, suffixString, separatorString, isOptional);
}
for (OutputDescriptionType currentDesc : outputArray){
NodeList nl = currentDesc.getDomNode().getChildNodes();
String pameterID = null;
for (int i = 0; i < nl.getLength(); i++){
Node currentNode = nl.item(i);
if(currentNode instanceof Comment){
pameterID = ((Comment)currentNode).getNodeValue().replaceAll("\\s+", "");
}
}
int index = Integer.parseInt(pameterID);
String mimeType = null;
String schema = null;
String literalDataType = null;
String defaultCRS = null;
String prefixString = null;
String suffixString = null;
String separatorString = null;
String wpsInputID = null;
// if parameter is already present
if(paramArray[index] != null){
wpsInputID = paramArray[index].wpsInputID;
}
String wpsOutputID = currentDesc.getIdentifier().getStringValue();
//complex data
if (currentDesc.isSetComplexOutput()){
mimeType = currentDesc.getComplexOutput().getDefault().getFormat().getMimeType();
schema = currentDesc.getComplexOutput().getDefault().getFormat().getSchema();
}
//literal data
if (currentDesc.isSetLiteralOutput()){
if (currentDesc.getLiteralOutput().isSetDataType()){
literalDataType = currentDesc.getLiteralOutput().getDataType().getStringValue();
}
}
//BBox data
if (currentDesc.isSetBoundingBoxOutput()){
defaultCRS = currentDesc.getBoundingBoxOutput().toString();
}
boolean isOptional = false;
paramArray[index] = new ToolParameter(wpsInputID, wpsOutputID, pameterID, schema, mimeType, literalDataType, defaultCRS, prefixString, suffixString, separatorString,isOptional);
}
return paramArray;
}
private ArrayList<File> listAllFiles(File rootDir, boolean includeDirNames) {
ArrayList<File> result = new ArrayList<File>();
try {
File[] fileList = rootDir.listFiles();
for (int i = 0; i < fileList.length; i++) {
if (fileList[i].isDirectory() == true) {
if (includeDirNames)
result.add(fileList[i]);
result.addAll(listAllFiles(fileList[i], includeDirNames));
} else{
result.add(fileList[i]);
LOGGER.info("Found AGS PD: " + fileList[i].getPath());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private ProcessDescriptionType loadProcessDescription(File describeProcessFile){
try {
InputStream xmlDesc = new FileInputStream(describeProcessFile);
XmlOptions option = new XmlOptions();
option.setLoadTrimTextBuffer();
ProcessDescriptionsDocument doc = ProcessDescriptionsDocument.Factory.parse(xmlDesc, option);
if(doc.getProcessDescriptions().getProcessDescriptionArray().length == 0) {
LOGGER.warn("ProcessDescription is empty! " + describeProcessFile.getName());
return null;
}
return doc.getProcessDescriptions().getProcessDescriptionArray(0);
}
catch(IOException e) {
LOGGER.warn("Could not initialize algorithm, parsing error! " + describeProcessFile.getName(), e);
}
catch(XmlException e) {
LOGGER.warn("Could not initialize algorithm, parsing error! " + describeProcessFile.getName(), e);
}
return null;
}
@Override
public ProcessDescriptionType getProcessDescription(String processID) {
if(!registeredProcessDescriptions.containsKey(processID)){
registeredProcessDescriptions.put(processID, getAlgorithm(processID).getDescription());
}
return registeredProcessDescriptions.get(processID);
}
@Override
public void shutdown() {
AGSWorkspace.shutdown();
}
}