package org.jboss.resource.adapter.jdbc.xa;
import org.jboss.resource.JBossResourceException;
import javax.resource.ResourceException;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ConnectionRequestInfo;
import javax.sql.XADataSource;
import javax.security.auth.Subject;
import java.util.List;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Iterator;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.beans.PropertyEditorManager;
import java.beans.PropertyEditor;
public class HAXAManagedConnectionFactory
extends XAManagedConnectionFactory
{
private String urlProperty;
private String urlDelimeter;
private XADataSelector xadsSelector;
public String getURLProperty()
{
return urlProperty;
}
public void setURLProperty(String urlProperty) throws ResourceException
{
this.urlProperty = urlProperty;
initSelector();
}
public String getURLDelimeter()
{
return urlDelimeter;
}
public void setURLDelimeter(String urlDelimeter) throws ResourceException
{
this.urlDelimeter = urlDelimeter;
initSelector();
}
public void setXADataSourceProperties(String xaDataSourceProperties) throws ResourceException
{
super.setXADataSourceProperties(xaDataSourceProperties);
initSelector();
}
public ManagedConnection createManagedConnection(Subject subject, ConnectionRequestInfo cri)
throws javax.resource.ResourceException
{
if(xadsSelector == null)
{
throw new JBossResourceException("Datasource selector is not initialized!");
}
for(int i = 0; i < xadsSelector.getXADataSourceList().size(); ++i)
{
XAData xaData = xadsSelector.getXAData();
if(log.isTraceEnabled())
{
log.trace("Trying to create an XA connection to " + xaData.url);
}
try
{
return super.createManagedConnection(subject, cri);
}
catch(ResourceException e)
{
log.warn("Failed to create an XA connection to " + xaData.url + ": " + e.getMessage());
xadsSelector.failedXAData(xaData);
}
}
throw new JBossResourceException(
"Could not create connection using any of the URLs: " + xadsSelector.getXADataSourceList()
);
}
protected synchronized XADataSource getXADataSource() throws ResourceException
{
return xadsSelector.getXAData().xads;
}
private XADataSource createXaDataSource(Properties xaProps)
throws JBossResourceException
{
if(getXADataSourceClass() == null)
{
throw new JBossResourceException("No XADataSourceClass supplied!");
}
XADataSource xads;
try
{
Class clazz = Thread.currentThread().getContextClassLoader().loadClass(getXADataSourceClass());
xads = (XADataSource)clazz.newInstance();
Class[] NOCLASSES = new Class[]{};
for(Iterator i = xaProps.keySet().iterator(); i.hasNext();)
{
String name = (String)i.next();
String value = xaProps.getProperty(name);
Class type = null;
try
{
Method getter = clazz.getMethod("get" + name, NOCLASSES);
type = getter.getReturnType();
}
catch(NoSuchMethodException e)
{
type = String.class;
}
Method setter = clazz.getMethod("set" + name, new Class[]{type});
PropertyEditor editor = PropertyEditorManager.findEditor(type);
if(editor == null)
{
throw new JBossResourceException("No property editor found for type: " + type);
} editor.setAsText(value);
setter.invoke(xads, new Object[]{editor.getValue()});
} }
catch(ClassNotFoundException cnfe)
{
throw new JBossResourceException("Class not found for XADataSource " + getXADataSourceClass(), cnfe);
} catch(InstantiationException ie)
{
throw new JBossResourceException("Could not create an XADataSource: ", ie);
} catch(IllegalAccessException iae)
{
throw new JBossResourceException("Could not set a property: ", iae);
}
catch(IllegalArgumentException iae)
{
throw new JBossResourceException("Could not set a property: ", iae);
}
catch(InvocationTargetException ite)
{
throw new JBossResourceException("Could not invoke setter on XADataSource: ", ite);
} catch(NoSuchMethodException nsme)
{
throw new JBossResourceException("Could not find accessor on XADataSource: ", nsme);
}
return xads;
}
private void initSelector() throws JBossResourceException
{
if(urlProperty != null && urlProperty.length() > 0)
{
String urlsStr = xaProps.getProperty(urlProperty);
if(urlsStr != null && urlsStr.trim().length() > 0 && urlDelimeter != null && urlDelimeter.trim().length() > 0)
{
List xaDataList = new ArrayList();
Properties xaPropsCopy = new Properties();
for(Iterator i = xaProps.keySet().iterator(); i.hasNext();)
{
Object key = i.next();
xaPropsCopy.put(key, xaProps.get(key));
}
int urlStart = 0;
int urlEnd = urlsStr.indexOf(urlDelimeter);
while(urlEnd > 0)
{
String url = urlsStr.substring(urlStart, urlEnd);
xaPropsCopy.setProperty(urlProperty, url);
XADataSource xads = createXaDataSource(xaPropsCopy);
xaDataList.add(new XAData(xads, url));
urlStart = ++urlEnd;
urlEnd = urlsStr.indexOf(urlDelimeter, urlEnd);
log.debug("added XA HA connection url: " + url);
}
if(urlStart != urlsStr.length())
{
String url = urlsStr.substring(urlStart, urlsStr.length());
xaPropsCopy.setProperty(urlProperty, url);
XADataSource xads = createXaDataSource(xaPropsCopy);
xaDataList.add(new XAData(xads, url));
log.debug("added XA HA connection url: " + url);
}
xadsSelector = new XADataSelector(xaDataList);
}
}
}
public static class XADataSelector
{
private final List xaDataList;
private int xaDataIndex;
private XAData xaData;
public XADataSelector(List xaDataList)
{
if(xaDataList == null || xaDataList.size() == 0)
{
throw new IllegalStateException("Expected non-empty list of XADataSource/URL pairs but got: " + xaDataList);
}
this.xaDataList = xaDataList;
}
public synchronized XAData getXAData()
{
if(xaData == null)
{
if(xaDataIndex == xaDataList.size())
{
xaDataIndex = 0;
}
xaData = (XAData)xaDataList.get(xaDataIndex++);
}
return xaData;
}
public synchronized void failedXAData(XAData xads)
{
if(xads.equals(this.xaData))
{
this.xaData = null;
}
}
public List getXADataSourceList()
{
return xaDataList;
}
}
private static class XAData
{
public final XADataSource xads;
public final String url;
public XAData(XADataSource xads, String url)
{
this.xads = xads;
this.url = url;
}
public boolean equals(Object o)
{
if(this == o)
{
return true;
}
if(!(o instanceof XAData))
{
return false;
}
final XAData xaData = (XAData)o;
if(!url.equals(xaData.url))
{
return false;
}
return true;
}
public int hashCode()
{
return url.hashCode();
}
public String toString()
{
return "[XA URL=" + url + "]";
}
}
}