/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wps;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Logger;
import org.geoserver.config.ConfigurationListenerAdapter;
import org.geoserver.config.GeoServer;
import org.geoserver.config.GeoServerInfo;
import org.geoserver.config.GeoServerInitializer;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.platform.resource.FileLockProvider;
import org.geoserver.platform.resource.FileSystemResourceStore;
import org.geoserver.platform.resource.Resource;
import org.geoserver.wps.executor.DefaultProcessManager;
import org.geoserver.wps.executor.WPSExecutionManager;
import org.geoserver.wps.resource.DefaultProcessArtifactsStore;
import org.geoserver.wps.resource.WPSResourceManager;
import org.geotools.process.ProcessFactory;
import org.geotools.process.Processors;
import org.geotools.util.logging.Logging;
/**
* Initializes WPS functionality from configuration.
*
* @author Andrea Aime, GeoSolutions
*/
public class WPSInitializer implements GeoServerInitializer {
static final Logger LOGGER = Logging.getLogger(WPSInitializer.class);
WPSExecutionManager executionManager;
DefaultProcessManager processManager;
WPSStorageCleaner cleaner;
WPSResourceManager resources;
GeoServerResourceLoader resourceLoader;
public WPSInitializer(WPSExecutionManager executionManager,
DefaultProcessManager processManager, WPSStorageCleaner cleaner,
WPSResourceManager resources, GeoServerResourceLoader resourceLoader) {
this.executionManager = executionManager;
this.processManager = processManager;
this.cleaner = cleaner;
this.resources = resources;
this.resourceLoader = resourceLoader;
}
public void initialize(final GeoServer geoServer) throws Exception {
initWPS(geoServer.getService(WPSInfo.class), geoServer);
geoServer.addListener(new ConfigurationListenerAdapter() {
@Override
public void handlePostGlobalChange(GeoServerInfo global) {
initWPS(geoServer.getService(WPSInfo.class), geoServer);
}
});
}
void initWPS(WPSInfo info, GeoServer geoServer) {
// Handle the http connection timeout.
// The specified timeout is in seconds. Convert it to milliseconds
double connectionTimeout = info.getConnectionTimeout();
if (connectionTimeout > 0) {
executionManager.setConnectionTimeout((int) connectionTimeout * 1000);
} else {
// specified timeout == -1 represents infinite timeout.
// by convention, for infinite URLConnection timeouts, we need to use zero.
executionManager.setConnectionTimeout(0);
}
// handle the resource expiration timeout
int expirationTimeout = info.getResourceExpirationTimeout() * 1000;
if (expirationTimeout < 0) {
// use the default of five minutes
expirationTimeout = 5 * 60 * 1000;
}
cleaner.setExpirationDelay(expirationTimeout);
executionManager.setHeartbeatDelay(expirationTimeout / 2);
// the max number of synch proceesses
int defaultMaxProcesses = Runtime.getRuntime().availableProcessors() * 2;
int maxSynch = info.getMaxSynchronousProcesses();
if (maxSynch > 0) {
processManager.setMaxSynchronousProcesses(maxSynch);
} else {
processManager.setMaxSynchronousProcesses(defaultMaxProcesses);
}
// the max number of asynch proceesses
int maxAsynch = info.getMaxAsynchronousProcesses();
if (maxAsynch > 0) {
processManager.setMaxAsynchronousProcesses(maxAsynch);
} else {
processManager.setMaxAsynchronousProcesses(defaultMaxProcesses);
}
// update the location of the artifact storage in case we are using a file system based
// one
if(resources.getArtifactsStore() instanceof DefaultProcessArtifactsStore) {
WPSInfo wps = geoServer.getService(WPSInfo.class);
String outputStorageDirectory = wps.getStorageDirectory();
FileSystemResourceStore resourceStore;
if (outputStorageDirectory == null || outputStorageDirectory.trim().isEmpty()) {
Resource temp = resourceLoader.get("temp/wps");
resourceStore = new FileSystemResourceStore(temp.dir());
} else {
File storage = new File(outputStorageDirectory);
// if it's a path relative to the data directory, make it absolute
if (!storage.isAbsolute()) {
storage = resourceLoader.url(outputStorageDirectory);
}
if(storage.exists() && !storage.isDirectory()) {
throw new IllegalArgumentException("Invalid wps storage path, "
+ "it represents a file: " + storage.getPath());
}
if(!storage.exists()) {
if (!storage.mkdirs()) {
throw new IllegalArgumentException(
"Invalid wps storage path, it does not exists and cannot be created: "
+ storage.getPath());
}
}
resourceStore = new FileSystemResourceStore(storage);
}
// use a clustering ready lock provider
try {
Resource lockDirectory = resourceLoader.get("tmp");
resourceStore.setLockProvider(new FileLockProvider(lockDirectory.dir()));
} catch (IllegalStateException e) {
throw new RuntimeException(
"Unexpected failure searching for tmp directory inside geoserver data dir",
e);
}
DefaultProcessArtifactsStore artifactsStore = (DefaultProcessArtifactsStore) resources
.getArtifactsStore();
artifactsStore.setResourceStore(resourceStore);
}
lookupNewProcessGroups(info, geoServer);
}
static void lookupNewProcessGroups(WPSInfo info, GeoServer geoServer) {
List<ProcessGroupInfo> newGroups = new ArrayList();
for (ProcessGroupInfo available : lookupProcessGroups()) {
boolean found = false;
for (ProcessGroupInfo configured : info.getProcessGroups()) {
if (configured.getFactoryClass().equals(available.getFactoryClass())) {
found = true;
break;
}
}
if (!found) {
//add it
newGroups.add(available);
}
}
//only save if we have anything new to add
if (!newGroups.isEmpty()) {
info.getProcessGroups().addAll(newGroups);
geoServer.save(info);
}
}
static List<ProcessGroupInfo> lookupProcessGroups() {
List<ProcessGroupInfo> processFactories = new ArrayList<ProcessGroupInfo>();
// here we build a full list of process factories infos, covering all available
// factories: this makes sure the available factories are availablefrom both
// GUI and REST configuration
// get the full list of factories
List<ProcessFactory> factories = new ArrayList<ProcessFactory>(Processors.getProcessFactories());
// ensure there is a stable order across invocations, JDK and so on
Collections.sort(factories, new Comparator<ProcessFactory>() {
@Override
public int compare(ProcessFactory o1, ProcessFactory o2) {
if(o1 == null) {
return o2 == null ? 0 : -1;
} else if(o2 == null) {
return 1;
} else {
return o1.getClass().getName().compareTo(o2.getClass().getName());
}
}
});
// build the result, adding the ProcessFactoryInfo as necessary for the factories
// that do not already have a configuration
for (final ProcessFactory pf : factories) {
ProcessGroupInfo pfi = new ProcessGroupInfoImpl();
pfi.setEnabled(true);
pfi.setFactoryClass(pf.getClass());
processFactories.add(pfi);
}
return processFactories;
}
}