com.googlecode.jsonrpc4j.spring.AutoJsonRpcServiceExporter Maven / Gradle / Ivy
Show all versions of jsonrpc4j Show documentation
package com.googlecode.jsonrpc4j.spring;
import static java.lang.String.format;
import static org.springframework.util.ClassUtils.forName;
import static org.springframework.util.ClassUtils.getAllInterfacesForClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import com.googlecode.jsonrpc4j.ConvertedParameterTransformer;
import com.googlecode.jsonrpc4j.ErrorResolver;
import com.googlecode.jsonrpc4j.HttpStatusCodeProvider;
import com.googlecode.jsonrpc4j.InvocationListener;
import com.googlecode.jsonrpc4j.JsonRpcService;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* This exporter class is deprecated because it exposes all beans from a spring context that has the
* {@link JsonRpcService} annotation. If that context is also consuming JSON-RPC services from a remote
* system and has proxy clients instantiated in the same context then those proxy clients will also
* be (inadvertently) exposed by {@link AutoJsonRpcServiceExporter}. To avoid this, switch over to use
* {@link AutoJsonRpcServiceImplExporter} which exposes specific implementations of the JSON-RPC services'
* interfaces rather than all beans that implement {@link JsonRpcService}.
* @deprecated use {@link AutoJsonRpcServiceImplExporter} instead.
*/
@Deprecated
@SuppressWarnings("unused")
public class AutoJsonRpcServiceExporter implements BeanFactoryPostProcessor {
private static final Logger logger = LoggerFactory.getLogger(AutoJsonRpcServiceExporter.class);
private static final String PATH_PREFIX = "/";
private ObjectMapper objectMapper;
private ErrorResolver errorResolver = null;
private Boolean registerTraceInterceptor;
private boolean backwardsCompatible = true;
private boolean rethrowExceptions = false;
private boolean allowExtraParams = false;
private boolean allowLessParams = false;
private InvocationListener invocationListener = null;
private HttpStatusCodeProvider httpStatusCodeProvider = null;
private ConvertedParameterTransformer convertedParameterTransformer = null;
/**
* Finds the beans to expose
* map.
*
* Searches parent factories as well.
*/
private static Map findServiceBeanDefinitions(ConfigurableListableBeanFactory beanFactory) {
final Map serviceBeanNames = new HashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
JsonRpcService jsonRpcPath = beanFactory.findAnnotationOnBean(beanName, JsonRpcService.class);
if (hasServiceAnnotation(jsonRpcPath)) {
String pathValue = jsonRpcPath.value();
logger.debug("Found JSON-RPC path '{}' for bean [{}].", pathValue, beanName);
if (isNotDuplicateService(serviceBeanNames, beanName, pathValue)) serviceBeanNames.put(pathValue, beanName);
}
}
collectFromParentBeans(beanFactory, serviceBeanNames);
return serviceBeanNames;
}
@SuppressWarnings("Convert2streamapi")
private static void collectFromParentBeans(ConfigurableListableBeanFactory beanFactory, Map serviceBeanNames) {
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
if (parentBeanFactory != null && ConfigurableListableBeanFactory.class.isInstance(parentBeanFactory)) {
for (Entry entry : findServiceBeanDefinitions((ConfigurableListableBeanFactory) parentBeanFactory).entrySet()) {
if (isNotDuplicateService(serviceBeanNames, entry.getKey(), entry.getValue())) serviceBeanNames.put(entry.getKey(), entry.getValue());
}
}
}
private static boolean isNotDuplicateService(Map serviceBeanNames, String beanName, String pathValue) {
if (serviceBeanNames.containsKey(pathValue)) {
String otherBeanName = serviceBeanNames.get(pathValue);
logger.debug("Duplicate JSON-RPC path specification: found {} on both [{}] and [{}].", pathValue, beanName, otherBeanName);
return false;
}
return true;
}
private static boolean hasServiceAnnotation(JsonRpcService jsonRpcPath) {
return jsonRpcPath != null;
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
Map servicePathToBeanName = findServiceBeanDefinitions(defaultListableBeanFactory);
for (Entry entry : servicePathToBeanName.entrySet()) {
registerServiceProxy(defaultListableBeanFactory, makeUrlPath(entry.getKey()), entry.getValue());
}
}
/**
* To make the
* {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}
* export a bean automatically, the name should start with a '/'.
*/
private String makeUrlPath(String servicePath) {
return PATH_PREFIX.concat(servicePath);
}
/**
* Registers the new beans with the bean factory.
*/
private void registerServiceProxy(DefaultListableBeanFactory defaultListableBeanFactory, String servicePath, String serviceBeanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(JsonServiceExporter.class).addPropertyReference("service", serviceBeanName);
BeanDefinition serviceBeanDefinition = findBeanDefinition(defaultListableBeanFactory, serviceBeanName);
for (Class> currentInterface : getBeanInterfaces(serviceBeanDefinition, defaultListableBeanFactory.getBeanClassLoader())) {
if (currentInterface.isAnnotationPresent(JsonRpcService.class)) {
String serviceInterface = currentInterface.getName();
logger.debug("Registering interface '{}' for JSON-RPC bean [{}].", serviceInterface, serviceBeanName);
builder.addPropertyValue("serviceInterface", serviceInterface);
break;
}
}
if (objectMapper != null) {
builder.addPropertyValue("objectMapper", objectMapper);
}
if (errorResolver != null) {
builder.addPropertyValue("errorResolver", errorResolver);
}
if (invocationListener != null) {
builder.addPropertyValue("invocationListener", invocationListener);
}
if (registerTraceInterceptor != null) {
builder.addPropertyValue("registerTraceInterceptor", registerTraceInterceptor);
}
if (httpStatusCodeProvider != null) {
builder.addPropertyValue("httpStatusCodeProvider", httpStatusCodeProvider);
}
if (convertedParameterTransformer != null) {
builder.addPropertyValue("convertedParameterTransformer", convertedParameterTransformer);
}
builder.addPropertyValue("backwardsCompatible", backwardsCompatible);
builder.addPropertyValue("rethrowExceptions", rethrowExceptions);
builder.addPropertyValue("allowExtraParams", allowExtraParams);
builder.addPropertyValue("allowLessParams", allowLessParams);
defaultListableBeanFactory.registerBeanDefinition(servicePath, builder.getBeanDefinition());
}
/**
* Find a {@link BeanDefinition} in the {@link BeanFactory} or it's parents.
*/
private BeanDefinition findBeanDefinition(ConfigurableListableBeanFactory beanFactory, String serviceBeanName) {
if (beanFactory.containsLocalBean(serviceBeanName)) return beanFactory.getBeanDefinition(serviceBeanName);
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
if (parentBeanFactory != null && ConfigurableListableBeanFactory.class.isInstance(parentBeanFactory))
return findBeanDefinition((ConfigurableListableBeanFactory) parentBeanFactory, serviceBeanName);
throw new RuntimeException(format("Bean with name '%s' can no longer be found.", serviceBeanName));
}
private Class>[] getBeanInterfaces(BeanDefinition serviceBeanDefinition, ClassLoader beanClassLoader) {
String beanClassName = serviceBeanDefinition.getBeanClassName();
try {
Class> beanClass = forName(beanClassName, beanClassLoader);
return getAllInterfacesForClass(beanClass, beanClassLoader);
} catch (ClassNotFoundException | LinkageError e) {
throw new RuntimeException(format("Cannot find bean class '%s'.", beanClassName), e);
}
}
/**
* @param objectMapper the objectMapper to set
*/
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
/**
* @param errorResolver the errorResolver to set
*/
public void setErrorResolver(ErrorResolver errorResolver) {
this.errorResolver = errorResolver;
}
/**
* @param backwardsCompatible the backwardsCompatible to set
*/
public void setBackwardsCompatible(boolean backwardsCompatible) {
this.backwardsCompatible = backwardsCompatible;
}
/**
* @param rethrowExceptions the rethrowExceptions to set
*/
public void setRethrowExceptions(boolean rethrowExceptions) {
this.rethrowExceptions = rethrowExceptions;
}
/**
* @param allowExtraParams the allowExtraParams to set
*/
public void setAllowExtraParams(boolean allowExtraParams) {
this.allowExtraParams = allowExtraParams;
}
/**
* @param allowLessParams the allowLessParams to set
*/
public void setAllowLessParams(boolean allowLessParams) {
this.allowLessParams = allowLessParams;
}
/**
* See {@link org.springframework.remoting.support.RemoteExporter#setRegisterTraceInterceptor(boolean)}
* @param registerTraceInterceptor the registerTraceInterceptor value to set
*/
public void setRegisterTraceInterceptor(boolean registerTraceInterceptor) {
this.registerTraceInterceptor = registerTraceInterceptor;
}
/**
* @param invocationListener the invocationListener to set
*/
public void setInvocationListener(InvocationListener invocationListener) {
this.invocationListener = invocationListener;
}
/**
* @param httpStatusCodeProvider the HttpStatusCodeProvider to set
*/
public void setHttpStatusCodeProvider(HttpStatusCodeProvider httpStatusCodeProvider) {
this.httpStatusCodeProvider = httpStatusCodeProvider;
}
/**
*
* @param convertedParameterTransformer the convertedParameterTransformer to set
*/
public void setConvertedParameterTransformer(ConvertedParameterTransformer convertedParameterTransformer) {
this.convertedParameterTransformer = convertedParameterTransformer;
}
}