/*
* Copyright (c) 2002-2009 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.transaction;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import org.neo4j.graphdb.TransactionFailureException;
import org.neo4j.kernel.impl.transaction.xaframework.XaDataSource;
import org.neo4j.kernel.impl.transaction.xaframework.XaResource;
/**
* All datasources that have been defined in the XA data source configuration
* file or manually added will be created and registered here. A mapping between
* "name", "data source" and "branch id" is kept by this manager.
* <p>
* Use the {@link #getXaDataSource} to obtain the instance of a datasource that
* has been defined in the XA data source configuration.
*
* @see XaDataSource
*/
public class XaDataSourceManager
{
// key = data source name, value = data source
private final Map<String,XaDataSource> dataSources =
new HashMap<String,XaDataSource>();
// key = branchId, value = data source
private final Map<String,XaDataSource> branchIdMapping =
new HashMap<String,XaDataSource>();
// key = data source name, value = branchId
private final Map<String,byte[]> sourceIdMapping =
new HashMap<String,byte[]>();
XaDataSourceManager()
{
}
XaDataSource create( String className, Map<?,?> params )
throws ClassNotFoundException,
InstantiationException, IllegalAccessException,
InvocationTargetException
{
Class<?> clazz = Class.forName( className );
Constructor<?>[] constructors = clazz.getConstructors();
for ( Constructor<?> constructor : constructors )
{
Class<?>[] parameters = constructor.getParameterTypes();
if ( parameters.length == 1 && parameters[0].equals( Map.class ) )
{
return (XaDataSource) constructor
.newInstance( new Object[] { params } );
}
}
throw new InstantiationException( "Unable to instantiate " + className
+ ", no valid constructor found." );
}
/**
* Convenience method since {@link #getXaDataSource} returns null if name
* doesn't exist.
*
* @return True if name exists
*/
public boolean hasDataSource( String name )
{
return dataSources.containsKey( name );
}
/**
* Returns the {@link org.neo4j.kernel.impl.transaction.xaframework.XaDataSource}
* registered as <CODE>name</CODE>. If no data source is registered with
* that name <CODE>null</CODE> is returned.
*
* @param name
* the name of the data source
*/
public XaDataSource getXaDataSource( String name )
{
return dataSources.get( name );
}
/**
* Public for testing purpose. Do not use.
*/
public synchronized void registerDataSource( String name,
XaDataSource dataSource, byte branchId[] )
{
dataSource.setBranchId( branchId );
dataSource.setName( name );
dataSources.put( name, dataSource );
branchIdMapping.put( new String( branchId ), dataSource );
sourceIdMapping.put( name, branchId );
}
/**
* Public for testing purpose. Do not use.
*/
public synchronized void unregisterDataSource( String name )
{
XaDataSource dataSource = dataSources.get( name );
byte branchId[] = getBranchId(
dataSource.getXaConnection().getXaResource() );
dataSources.remove( name );
branchIdMapping.remove( new String( branchId ) );
sourceIdMapping.remove( name );
dataSource.close();
}
synchronized void unregisterAllDataSources()
{
branchIdMapping.clear();
sourceIdMapping.clear();
Iterator<XaDataSource> itr = dataSources.values().iterator();
while ( itr.hasNext() )
{
XaDataSource dataSource = itr.next();
dataSource.close();
}
dataSources.clear();
}
synchronized byte[] getBranchId( XAResource xaResource )
{
if ( xaResource instanceof XaResource )
{
byte branchId[] = ((XaResource) xaResource).getBranchId();
if ( branchId != null )
{
return branchId;
}
}
Iterator<Map.Entry<String,XaDataSource>> itr =
dataSources.entrySet().iterator();
while ( itr.hasNext() )
{
Map.Entry<String,XaDataSource> entry = itr.next();
XaDataSource dataSource = entry.getValue();
XAResource resource = dataSource.getXaConnection().getXaResource();
try
{
if ( resource.isSameRM( xaResource ) )
{
String name = entry.getKey();
return sourceIdMapping.get( name );
}
}
catch ( XAException e )
{
throw new TransactionFailureException(
"Unable to check is same resource", e );
}
}
throw new TransactionFailureException(
"Unable to find mapping for XAResource[" + xaResource + "]" );
}
synchronized XAResource getXaResource( byte branchId[] )
{
XaDataSource dataSource = branchIdMapping.get( new String( branchId ) );
if ( dataSource == null )
{
throw new TransactionFailureException(
"No mapping found for branchId[0x" +
new String( branchId ) + "]" );
}
return dataSource.getXaConnection().getXaResource();
}
// not thread safe
public Iterable<XaDataSource> getAllRegisteredDataSources()
{
return dataSources.values();
}
}