/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.remoting.jbr;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jboss.remoting.transporter.TransporterClient;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.remoting.support.RemoteAccessor;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
/**
* Supports proxying clients for JBoss Remoting.
* <p/>
* There are two different types of serialization
* <p/>
* the default, JBoss, uses a special serialization library from JBoss that
* <ul>
* <LI> Eschews the need to implement {@link java.io.Serializable}</LI>
* <LI> Eschews the need to </LI>
* </ul>
* <p/>
* http://www.theserverlabs.com/blog/2009/02/19/jboss-remoting-jboss-serialization-kills-javarmi-and-spring-remoting/
*
* @author Josh Long
*/
public class JbossRemotingProxyFactoryBean<T> extends RemoteAccessor implements InitializingBean, BeanNameAware, MethodInterceptor, FactoryBean<T> {
private JbossSerialization serializationType = JbossSerialization.JBOSS;
private String transport = "socket";
private String host = "127.0.0.1";
private int port = 5400;
private Object client;
private T serviceProxy;
private String serverName;
private boolean clustered;
private String beanName;
private String url;
public void setSerializationType(JbossSerialization serializationType) {
this.serializationType = serializationType;
}
public void setHost(String host) {
this.host = host;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public void setClustered(boolean clustered) {
this.clustered = clustered;
}
public void setPort(int port) {
this.port = port;
}
public void setUrl(String url) {
this.url = url;
}
public void setTransport(String transport) {
this.transport = transport;
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
@Override
@SuppressWarnings("unchecked")
public void afterPropertiesSet() throws Exception {
if (!StringUtils.hasText(this.serverName)) {
this.serverName = this.beanName;
}
if (!StringUtils.hasText(this.url)) {
this.url = buildUrl();
}
if (logger.isDebugEnabled()) {
logger.debug(getServiceInterface() + " client URL is " + this.url);
}
Class<T> clientInterface = getServiceInterface();
// build the jbr client
this.client = (T) TransporterClient.createTransporterClient(this.url, clientInterface);
Assert.isInstanceOf(getServiceInterface(), this.client);
// build the object that'll manage calls from the client to that jbr proxy
serviceProxy = (T) new ProxyFactory(clientInterface, this).getProxy(getBeanClassLoader());
}
protected String buildUrl() {
Assert.hasText(this.transport);
Assert.hasText(this.host);
Assert.notNull(this.serializationType, "you must specify a serialization type (" +
JbossSerialization.JBOSS.name() + "," +
JbossSerialization.JAVA.name() + ")");
Assert.isTrue(this.port > 0, "you must specify a non-zero port");
return transport + "://" + host + ":" + port + "/?serializationtype=" + serializationType.name().toLowerCase();
}
@Override
public T getObject() throws Exception {
return serviceProxy;
}
@Override
public Class<?> getObjectType() {
return getServiceInterface();
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// now, unless i miss my mark, the interface of this proxy and the proxy we've created locally are the same,
// so it should be a simple matter to forward the MethodInvocation on to the local client
Method method = invocation.getMethod();
if (method.getName().equals("toString")) {
return "proxy for " + getServiceInterface();
}
if (logger.isDebugEnabled()) {
logger.debug("invoking " + invocation.toString() + " on the client proxy");
}
// find the same method on the client
if (logger.isDebugEnabled()) {
for (Method m : client.getClass().getMethods()) {
logger.debug("discovered " + m.toGenericString() + " on the client proxy.");
}
}
Assert.isInstanceOf(getServiceInterface(), this.client);
Method clientMethodSpecifically = this.client.getClass().getMethod(method.getName(), invocation.getMethod().getParameterTypes());
Assert.notNull(clientMethodSpecifically);
return clientMethodSpecifically.invoke(this.client, invocation.getArguments());
}
}