/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.internal.soa.esb.dependencies;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import org.jboss.soa.esb.listeners.jca.ActivationBridge;
import org.jboss.soa.esb.listeners.jca.EndpointContainer;
import org.jboss.soa.esb.listeners.jca.EndpointFactory;
import org.jboss.soa.esb.listeners.jca.JBoss42ActivationBridge;
import org.jboss.system.ServiceMBeanSupport;
/**
* Provide generic JCA inflow adapter functionality.
*
* Initialises a JCA inflow activation and adapts onto the required interface.
*
* @author <a href="kevin.conner@jboss.com">Kevin Conner</a>
*/
public class JCAInflowAdapter extends ServiceMBeanSupport implements JCAInflowAdapterMBean
{
private static final Logger LOGGER = Logger.getLogger(JCAInflowAdapter.class) ;
/**
* Optional messaging interface, will try to determine if not specified.
*/
private String messagingInterface;
/**
* The name of the bean driven by the inflow.
*/
private String beanType;
/**
* The name of the JCA resource adapter.
*/
private String adapter;
/**
* Optional flag indicating whether deliver is transactional.
*/
private boolean isTransacted = true;
/**
* Optional name of activation bridge, defaults to ESB JBoss42ActivationBridge.
*/
private String activationBridge;
/**
* Activation specification properties.
*/
private Properties activationSpec ;
/**
* Optional description for message inflow.
*/
private String description ;
/**
* Activation bridge.
*/
private ActivationBridge bridge ;
/**
* Set the messaging interface.
* @param messagingInterface The name of the messaging interface.
*/
public void setMessagingInterface(final String messagingInterface)
{
this.messagingInterface = messagingInterface ;
}
/**
* Get the messaging interface.
* @return The name of the messaging interface or null if not set.
*/
public String getMessagingInterface()
{
return messagingInterface ;
}
/**
* Set the type of the inflow bean.
* @param beanType The type of the inflow bean.
*/
public void setBeanType(final String beanType)
{
this.beanType = beanType ;
}
/**
* Get the type of the inflow bean.
* @return The type of the inflow bean.
*/
public String getBeanType()
{
return beanType ;
}
/**
* Set the name of the JCA resource adapter.
* @param adapter The JCA resource adapter.
*/
public void setAdapter(final String adapter)
{
this.adapter = adapter ;
}
/**
* Get the name of the JCA resource adapter.
* @return The JCA resource adapter.
*/
public String getAdapter()
{
return adapter ;
}
/**
* Set the transacted flag.
* @param isTransacted true if the delivery is transacted, false otherwise.
*/
public void setIsTransacted(final boolean isTransacted)
{
this.isTransacted = isTransacted ;
}
/**
* Get the transacted flag.
* @return true if the delivery is transacted, false otherwise.
*/
public boolean getIsTransacted()
{
return isTransacted ;
}
/**
* Set the name of the activation bridge.
* @param activationBridge The name of the activation bridge.
*/
public void setActivationBridge(final String activationBridge)
{
this.activationBridge = activationBridge ;
}
/**
* Get the name of the activation bridge.
* @return The name of the activation bridge or null if not set.
*/
public String getActivationBridge()
{
return activationBridge ;
}
/**
* Set the activation specification properties.
* @param activationSpec The activation specification properties.
*/
public void setActivationSpec(final Properties activationSpec)
{
this.activationSpec = activationSpec ;
}
/**
* Get the activation specification properties.
* @return The activation specification properties.
*/
public Properties getActivationSpec()
{
return activationSpec ;
}
/**
* Set the description for the message inflow.
* @param description The description for the message inflow.
*/
public void setDescription(final String description)
{
this.description = description ;
}
/**
* Get the description for the message inflow.
* @return The description for the message inflow or null if not set.
*/
public String getDescription()
{
return description ;
}
/**
* Create the inflow service.
*/
@Override
protected void createService()
throws Exception
{
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader() ;
if (beanType == null)
{
throw new IllegalArgumentException("BeanType must be specified") ;
}
if (adapter == null)
{
throw new IllegalArgumentException("Adapter name must be specified") ;
}
if (activationSpec == null)
{
throw new IllegalArgumentException("Activation specification properties must be specified") ;
}
final Class<?> beanClass = classLoader.loadClass(beanType) ;
final Class<?> messagingClass ;
if (messagingInterface != null)
{
messagingClass = classLoader.loadClass(messagingInterface) ;
}
else
{
messagingClass = findMessagingClass(beanClass) ;
}
if (activationBridge != null)
{
bridge = (ActivationBridge)classLoader.loadClass(activationBridge).newInstance() ;
}
else
{
bridge = new JBoss42ActivationBridge() ;
}
final Object bean = beanClass.newInstance() ;
final String inflowDescription ;
if (description != null)
{
inflowDescription = description ;
}
else
{
inflowDescription = "Message Inflow" ;
}
final String inflowAdapter = adapter ;
final boolean transacted = isTransacted ;
final EndpointContainer container = new EndpointContainer() {
public String getDescription()
{
return inflowDescription + " jca adapter: " + inflowAdapter;
}
public Object invoke(final Method method, final Object[] args)
throws Throwable
{
try
{
return method.invoke(bean, args);
}
catch (final IllegalAccessException iae)
{
throw new RuntimeException(iae) ;
}
catch (final InvocationTargetException ite)
{
throw ite.getTargetException() ;
}
}
public boolean isDeliveryTransacted(final Method method)
throws NoSuchMethodException
{
return transacted ;
}
};
final Map<String, String> activationProperties = getActivationProperties() ;
final EndpointFactory mef = new EndpointFactory();
mef.setContainer(container);
mef.setLoader(classLoader);
mef.setMessagingType(messagingClass);
mef.start();
bridge.setActivationProperties(activationProperties);
bridge.setAdapter(adapter);
bridge.setMessageEndpointFactory(mef);
bridge.setMessagingTypeClass(messagingClass);
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Created JCA Inflow bridge " + getServiceName() + ", messagingClass: " + messagingClass + ", bean: " + bean + ", activationProperties: " + activationProperties) ;
}
}
/**
* Start the inflow service.
*/
@Override
protected void startService()
throws Exception
{
bridge.activate() ;
}
/**
* Stop the inflow service.
*/
@Override
protected void stopService()
throws Exception
{
bridge.deactivate() ;
}
/**
* Try to guess the type of the inflow messaging class.
* @param clazz The class type of the bean.
* @return the messaging type or null if unable to determin.
* @throws IllegalArgumentException if bean type has multiple interfaces.
*/
private Class<?> findMessagingClass(final Class<?> clazz)
{
if (clazz.equals(Object.class))
{
throw new IllegalArgumentException("Unable to guess MessagingClass interface, specify this explicity") ;
}
final Class<?>[] interfaces = clazz.getInterfaces() ;
Class<?> type = null ;
for (Class<?> intf : interfaces)
{
if (type != null)
{
throw new IllegalArgumentException("Unable to guess MessagingClass interface as the base class implements too many interfaces, specify this explicity") ;
}
type = intf ;
}
if (type == null)
{
type = findMessagingClass(clazz.getSuperclass()) ;
}
return type ;
}
private Map<String, String> getActivationProperties()
{
final Map<String, String> activationProperties = new HashMap<String, String>() ;
for (Entry<Object, Object> entry : activationSpec.entrySet())
{
activationProperties.put(entry.getKey().toString(), entry.getValue().toString()) ;
}
return activationProperties ;
}
}