Hi,
in GeoServer land I'm banging my head against a problem
that requires a bridge between SPI and Spring.
Basically I need to build processes that deal with the
catalog, and eventually with some other of the GS machinery.
Normally that would mean they need to be Spring beans, however
SPI and the Spring context do not exactly mix well togheter,
and I'm looking for some advice.
I've tried a few ways and come up with one that seems satisfactory,
but requires some helper methods in Processors.
The idea is to have a process factory that is a Spring bean
itself, and looks in the Spring context for annotated beans
that implement a certain marker interface (so that we
don't need to scan every single bean in the app context).
It then integrates in the SPI somehow.
The first approach I tried was creating its own factory iterator
provider. This works sort of fine, but there is a catch:
the spring context can be started and stopped multiple
times, each time a different factory bean will be created,
and the old one must be removed.
Now, just removing the iterator does not cut it, because that
won't remove the eventual process factory that has already
been created out of it (and that SPI will hold onto).
To do that one need to call
FactoryRegistry.deregisterServiceProvider(...)
So, in the end, using the factory iterator is not enough, because
one needs direct access to the factory registry anyways to
manage the different Spring lifecycle and deregister the factory
when the Spring context goes down (which gets quite evident
in testing, when the Spring context gets started up and shut
down multiple times in a row, whilst the SPI keeps on being
alive and kicking).
However, the moment you get access to FactoryRegistry, you can
also directly register a factory using
FactoryRegistry.registerServiceProvider
Long story short, the provider iterators are useless for
an effective Spring integration because of the different
lifecycle handling between the two systems.
Attached there is a class that uses a Spring callback to register
and deregister itself from SPI at the right time.
To work it requires Processors to expose methods to do so (exposing
the factory registry directly is not the best idea encapsulation
wise, and the methods also need to cater for the fact Processors
caches the last factory).
Attached there is a patch to add the necessary helper methods.
Opinions?
Cheers
Andrea
package org.geoserver.wps.jts;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.geotools.factory.FactoryIteratorProvider;
import org.geotools.factory.GeoTools;
import org.geotools.feature.NameImpl;
import org.geotools.process.ProcessFactory;
import org.geotools.process.Processors;
import org.geotools.util.SimpleInternationalString;
import org.opengis.feature.type.Name;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
public class SpringBeanProcessFactory extends AnnotationDrivenProcessFactory
implements ApplicationContextAware, ApplicationListener {
Map<String, Class> classMap;
Map<String, String> beanMap;
Class markerInterface;
ApplicationContext applicationContext;
FactoryIteratorProvider iterator;
public SpringBeanProcessFactory(String title, String namespace,
Class markerInterface) {
super(new SimpleInternationalString(title), namespace);
this.markerInterface = markerInterface;
// create an iterator that will register this factory into SPI
iterator = new FactoryIteratorProvider() {
public <T> Iterator<T> iterator(Class<T> category) {
if (ProcessFactory.class.isAssignableFrom(category)) {
return (Iterator<T>) Collections.singletonList(
SpringBeanProcessFactory.this).iterator();
} else {
return null;
}
}
};
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
// loads all of the beans
String[] beanNames = applicationContext.getBeanNamesForType(
markerInterface, true, true);
classMap = new HashMap<String, Class>();
beanMap = new HashMap<String, String>();
for (String beanName : beanNames) {
Class c = applicationContext.getType(beanName);
String name = c.getSimpleName();
if (name.endsWith("Process")) {
name = name.substring(0, name.indexOf("Process"));
}
classMap.put(name, c);
beanMap.put(name, beanName);
}
}
@Override
protected DescribeProcess getProcessDescription(Name name) {
Class c = classMap.get(name.getLocalPart());
if (c == null) {
return null;
} else {
return (DescribeProcess) c.getAnnotation(DescribeProcess.class);
}
}
@Override
protected Method method(String className) {
Class c = classMap.get(className);
if (c != null) {
for (Method m : c.getMethods()) {
if ("execute".equals(m.getName())) {
return m;
}
}
}
return null;
}
public Set<Name> getNames() {
Set<Name> result = new LinkedHashSet<Name>();
List<String> names = new ArrayList<String>(classMap.keySet());
Collections.sort(names);
for (String name : names) {
result.add(new NameImpl(namespace, name));
}
return result;
}
@Override
protected Object createProcessBean(Name name) {
String beanName = beanMap.get(name.getLocalPart());
if (beanName == null) {
return null;
}
return applicationContext.getBean(beanName);
}
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ContextRefreshedEvent) {
Processors.addProcessFactory(this);
System.out.println("Startup");
} else if(event instanceof ContextClosedEvent) {
Processors.removeProcessFactory(this);
System.out.println("Shutdown");
}
}
}
diff --git a/modules/unsupported/process/src/main/java/org/geotools/process/Processors.java b/modules/unsupported/process/src/main/java/org/geotools/process/Processors.java
index 2bca217..2795a6c 100644
--- a/modules/unsupported/process/src/main/java/org/geotools/process/Processors.java
+++ b/modules/unsupported/process/src/main/java/org/geotools/process/Processors.java
@@ -68,6 +68,17 @@ public class Processors extends FactoryFinder {
return registry;
}
+ public static void addProcessFactory(ProcessFactory factory) {
+ getServiceRegistry().registerServiceProvider(factory);
+ }
+
+ public static void removeProcessFactory(ProcessFactory factory) {
+ if(lastFactory == factory) {
+ lastFactory = null;
+ }
+ getServiceRegistry().deregisterServiceProvider(factory);
+ }
+
/**
* Set of available ProcessFactory; each of which is responsible for one or more processes.
*
------------------------------------------------------------------------------
This SF.net email is sponsored by Sprint
What will you do first with EVO, the first 4G phone?
Visit sprint.com/first -- http://p.sf.net/sfu/sprint-com-first
_______________________________________________
Geotools-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/geotools-devel