/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wps.remote;
import java.awt.RenderingHints.Key;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.platform.GeoServerExtensions;
import org.geotools.data.Parameter;
import org.geotools.process.Process;
import org.geotools.process.ProcessFactory;
import org.geotools.text.Text;
import org.geotools.util.SimpleInternationalString;
import org.geotools.util.logging.Logging;
import org.opengis.feature.type.Name;
import org.opengis.util.InternationalString;
/**
* A process factory that wraps a {@link RemoteProcessClient} and can be used to get information about it and create the corresponding process.
*
* @author Alessio Fabiani, GeoSolutions
*
*/
public class RemoteProcessFactory implements ProcessFactory, RemoteProcessFactoryListener {
public static final String WPS_VERSION = "1.0.0";
/** The LOGGER */
public static final Logger LOGGER = Logging
.getLogger(RemoteProcessFactory.class.getPackage().getName());
/** Associates the generic sets of inputs and outputs declared by the remote service to the {@link RemoteProcess} instance */
private Map<Name, RemoteServiceDescriptor> descriptors = new ConcurrentHashMap<Name, RemoteServiceDescriptor>();
/**
* The list of currently running {@link RemoteProcess} instances; each of them is identified by a unique pId generated by the
* {@link RemoteProcessClient} which must be used to honor the asynchronous communication handshake. Remember that only the
* {@link RemoteProcessClient} specific implementation knows how to handle the communication with the remote service.
*/
private Map<Name, RemoteProcess> remoteInstances = new ConcurrentHashMap<Name, RemoteProcess>();
/** The {@link RemoteProcessClient} instance */
private RemoteProcessClient remoteClient;
/**
* Constructs a {@link RemoteProcessFactory} able to dynamically create stubs for the remote communication
*/
public RemoteProcessFactory() {
try {
List<RemoteProcessClient> availableRemoteClientInstances = GeoServerExtensions
.extensions(RemoteProcessClient.class);
for (RemoteProcessClient ext : availableRemoteClientInstances) {
if (ext.isEnabled()) {
remoteClient = ext;
remoteClient.init();
if (remoteClient.isEnabled()) {
remoteClient.registerProcessFactoryListener(this);
break;
}
}
}
} catch (Exception e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
}
/**
* @return the remoteClient
*/
public RemoteProcessClient getRemoteClient() {
return remoteClient;
}
/**
* @param remoteClient the remoteClient to set
*/
public void setRemoteClient(RemoteProcessClient remoteClient) {
this.remoteClient = remoteClient;
}
/** The Title of the {@link RemoteProcessFactory} */
public InternationalString getTitle() {
return new SimpleInternationalString("Remote");
}
/** The currently available {@link RemoteProcess} stubs on the {@link RemoteProcessFactory} */
public Set<Name> getNames() {
return descriptors.keySet();
}
/** Utility method to check if a {@link RemoteProcess} stub has been already registered */
boolean checkName(Name name) {
if (name == null)
throw new NullPointerException("Process name cannot be null");
if (!descriptors.containsKey(name)) {
LOGGER.warning("Unknown process '" + name + "'");
return false;
}
for (Name registeredService : descriptors.keySet()) {
if (registeredService.getLocalPart().equalsIgnoreCase((name.getLocalPart()))) {
return true;
}
}
return true;
}
/**
* Creates a new {@link RemoteProcess} stub
*
* @throws IllegalArgumentException
*/
public Process create(Name name) throws IllegalArgumentException {
synchronized (remoteInstances) {
if (checkName(name)) {
try {
RemoteProcess process = new RemoteProcess(name, remoteClient,
descriptors.get(name).getMetadata());
remoteInstances.put(name, process);
return process;
} catch (Exception e) {
throw new RuntimeException("Error occurred cloning the prototype "
+ "algorithm... this should not happen", e);
}
}
return null;
}
}
/** Get the {@link RemoteProcess} textual description */
public InternationalString getDescription(Name name) {
synchronized (descriptors) {
if (checkName(name))
return Text.text(descriptors.get(name).getDescription());
return null;
}
}
/** Get the {@link RemoteProcess} title */
public InternationalString getTitle(Name name) {
synchronized (descriptors) {
if (checkName(name))
return Text.text(descriptors.get(name).getTitle());
return null;
}
}
/** Get the {@link RemoteProcess} short name */
public String getName(Name name) {
if (checkName(name)) {
return name.getLocalPart();
}
return null;
}
/** */
public boolean supportsProgress(Name name) {
return true;
}
/** */
public String getVersion(Name name) {
return WPS_VERSION;
}
/** Get the {@link RemoteProcess} textual description */
public Map<String, Parameter<?>> getParameterInfo(Name name) {
synchronized (descriptors) {
if (checkName(name))
return descriptors.get(name).getParamInfo();
return null;
}
}
/** Get the {@link RemoteProcess} textual description */
public Map<String, Parameter<?>> getResultInfo(Name name, Map<String, Object> inputs)
throws IllegalArgumentException {
synchronized (descriptors) {
if (checkName(name))
return descriptors.get(name).getOutputInfo();
return null;
}
}
@Override
public String toString() {
return "RemoteProcessFactory";
}
/** */
public boolean isAvailable() {
return true;
}
/** */
public Map<Key, ?> getImplementationHints() {
return Collections.EMPTY_MAP;
}
/** Registers a new remote service */
@Override
public void registerProcess(RemoteServiceDescriptor serviceDescriptor) {
Name name = serviceDescriptor.getName();
if (descriptors.containsKey(name)) {
LOGGER.warning("Service " + name + " already registered!");
return;
}
descriptors.put(name, serviceDescriptor);
create(name);
LOGGER.info("Registered Service [" + name + "]");
}
/** De-registers a remote service */
@Override
public void deregisterProcess(Name name) {
if (checkName(name)) {
descriptors.remove(name);
remoteInstances.remove(name);
LOGGER.info("Deregistered Service [" + name + "]");
}
}
}