/*
* Copyright 2008 Niclas Hedhman.
*
* 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.qi4j.library.spaces.javaspaces;
import net.jini.config.ConfigurationException;
import net.jini.config.ConfigurationFile;
import net.jini.core.entry.UnusableEntryException;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.transaction.Transaction;
import net.jini.core.transaction.TransactionException;
import net.jini.core.transaction.TransactionFactory;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryManagement;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lookup.LookupCache;
import net.jini.lookup.ServiceDiscoveryEvent;
import net.jini.lookup.ServiceDiscoveryListener;
import net.jini.lookup.ServiceDiscoveryManager;
import net.jini.space.JavaSpace05;
import net.jini.space.MatchSet;
import org.qi4j.api.configuration.Configuration;
import org.qi4j.api.injection.scope.This;
import org.qi4j.api.service.Activatable;
import org.qi4j.library.spaces.Space;
import org.qi4j.library.spaces.SpaceException;
import org.qi4j.library.spaces.SpaceTransaction;
import java.io.*;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;
import java.util.StringTokenizer;
public class JavaSpacesClientMixin
implements Space, Activatable
{
@This private Configuration<JavaSpacesClientConfiguration> my;
private ServiceHolder<JavaSpace05> spaceService;
private ServiceHolder<TransactionManager> transactionService;
private StackThreadLocal<TransactionProxy> transactionStack;
public JavaSpacesClientMixin()
{
transactionStack = new StackThreadLocal<TransactionProxy>();
}
public void write( String id, String entry )
{
synchronized( this )
{
if( spaceService.service != null )
{
try
{
StorageEntry storageEntry = new StorageEntry( id, entry );
spaceService.service.write( storageEntry, currentTransaction(), 6000 );
}
catch( TransactionException e )
{
e.printStackTrace(); // Can not happen without own transaction.
}
catch( RemoteException e )
{
throw new SpaceException( "Remote problems: " + e.getMessage(), e );
}
}
}
}
public Serializable take( String id, long timeout )
{
synchronized( this )
{
if( spaceService.service != null )
{
try
{
StorageEntry entry = (StorageEntry) spaceService.service.take( new StorageEntry( id, null ), currentTransaction(), 6000 );
if( entry == null )
{
return null;
}
return entry.data();
}
catch( TransactionException e )
{
e.printStackTrace(); // Can not happen without own transaction.
}
catch( RemoteException e )
{
throw new SpaceException( "Remote problems: " + e.getMessage(), e );
}
catch( UnusableEntryException e )
{
e.printStackTrace();
}
catch( InterruptedException e )
{
e.printStackTrace();
}
}
}
return null;
}
public String takeIfExists( String id )
{
synchronized( this )
{
if( spaceService.service != null )
{
try
{
StorageEntry entry = (StorageEntry) spaceService.service.takeIfExists( new StorageEntry( id, null ), currentTransaction(), 6000 );
if( entry == null )
{
return null;
}
return entry.data();
}
catch( TransactionException e )
{
e.printStackTrace(); // Can not happen without own transaction.
}
catch( RemoteException e )
{
throw new SpaceException( "Remote problems: " + e.getMessage(), e );
}
catch( UnusableEntryException e )
{
e.printStackTrace();
}
catch( InterruptedException e )
{
e.printStackTrace();
}
}
return null;
}
}
public Serializable read( String id, long timeout )
{
synchronized( this )
{
if( spaceService.service != null )
{
try
{
StorageEntry entry = (StorageEntry) spaceService.service.read( new StorageEntry( id, null ), currentTransaction(), timeout );
if( entry == null )
{
return null;
}
return entry.data();
}
catch( TransactionException e )
{
e.printStackTrace(); // Can not happen without own transaction.
}
catch( RemoteException e )
{
throw new SpaceException( "Remote problems: " + e.getMessage(), e );
}
catch( UnusableEntryException e )
{
e.printStackTrace();
}
catch( InterruptedException e )
{
e.printStackTrace();
}
}
}
return null;
}
public String readIfExists( String id )
{
synchronized( this )
{
if( spaceService.service != null )
{
try
{
StorageEntry entry = (StorageEntry) spaceService.service.readIfExists( new StorageEntry( id, null ), currentTransaction(), 6000 );
if( entry == null )
{
return null;
}
return entry.data();
}
catch( TransactionException e )
{
e.printStackTrace(); // Can not happen without own transaction.
}
catch( RemoteException e )
{
throw new SpaceException( "Remote problems: " + e.getMessage(), e );
}
catch( UnusableEntryException e )
{
e.printStackTrace();
}
catch( InterruptedException e )
{
e.printStackTrace();
}
}
}
return null;
}
public SpaceTransaction newTransaction()
{
synchronized( this )
{
if( transactionService.service != null )
{
try
{
Transaction.Created created = TransactionFactory.create( transactionService.service, 60000 );
Transaction transaction = created.transaction;
System.out.println( "NEW: " + transaction );
TransactionProxy transactionProxy = new TransactionProxy( transaction, this );
transactionStack.get().push( transactionProxy );
return transactionProxy;
}
catch( LeaseDeniedException e )
{
e.printStackTrace();
}
catch( RemoteException e )
{
e.printStackTrace();
}
}
return new NullTransactionProxy();
}
}
public boolean isReady()
{
return spaceService.service != null && transactionService.service != null;
}
public void activate()
throws Exception
{
String groupConfig = my.configuration().groups().get();
System.out.println( "GROUPS: " + groupConfig );
String[] groups = convert( groupConfig );
spaceService = initializeLookup( groups, JavaSpace05.class );
transactionService = initializeLookup( groups, TransactionManager.class );
}
private String[] convert( String data )
{
if( data == null )
{
return new String[0];
}
StringTokenizer st = new StringTokenizer( data, ",", false );
ArrayList<String> result = new ArrayList<String>();
while( st.hasMoreTokens() )
{
String item = st.nextToken().trim();
result.add( item );
}
String[] retVal = new String[result.size()];
return result.toArray( retVal );
}
public void passivate()
throws Exception
{
spaceService.lookupCache.terminate();
transactionService.lookupCache.terminate();
}
static <T> ServiceHolder<T> initializeLookup( String[] groups, Class<T> type )
throws IOException
{
ClassLoader classloader = JavaSpacesClientMixin.class.getClassLoader();
InputStream in = JavaSpacesClientMixin.class.getResourceAsStream( "jini.config" );
Reader reader = new InputStreamReader( in );
String[] options = new String[0];
DiscoveryManagement dm = null;
try
{
net.jini.config.Configuration config = new ConfigurationFile( reader, options, classloader );
dm = new LookupDiscoveryManager( groups, null, null, config );
}
catch( ConfigurationException e )
{
e.printStackTrace();
}
ServiceDiscoveryManager sdm = new ServiceDiscoveryManager( dm, null );
Class[] types = new Class[]{ type };
ServiceTemplate template = new ServiceTemplate( null, types, null );
LookupCache lookupCache = sdm.createLookupCache( template, null, null );
return new ServiceHolder<T>( dm, lookupCache );
}
Transaction currentTransaction()
{
Stack<TransactionProxy> curStack = transactionStack.get();
if( curStack.size() == 0 )
{
System.err.println( "WARNING: Transaction is null." );
return null;
}
TransactionProxy proxy = curStack.peek();
if( proxy == null )
{
System.err.println( "WARNING: Transaction is null." );
return null;
}
else if( proxy.transaction == null )
{
System.err.println( "WARNING: Transaction is null." );
}
Transaction transaction = proxy.transaction;
System.out.println( "LOOKUP: " + transaction );
return transaction;
}
void removeTransaction( TransactionProxy transaction )
{
Stack<TransactionProxy> curStack = transactionStack.get();
curStack.remove( transaction );
}
public Iterator<String> iterator()
{
ArrayList templates = new ArrayList();
templates.add( new StorageEntry(null, null) );
try
{
Transaction txn = currentTransaction();
MatchSet matchSet = spaceService.service.contents( templates, txn, 60000, 1000 );
return new EntryIterator( matchSet );
}
catch( TransactionException e )
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
catch( RemoteException e )
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return null;
}
static class ServiceHolder<T>
implements DiscoveryListener
{
private DiscoveryManagement dm;
private LookupCache lookupCache;
T service;
public ServiceHolder( DiscoveryManagement dm, LookupCache lookupCache )
{
this.dm = dm;
this.lookupCache = lookupCache;
this.dm.addDiscoveryListener( this );
lookupCache.addListener( new ServiceDiscoveryListener()
{
public void serviceAdded( ServiceDiscoveryEvent event )
{
System.out.println( "Service Added: " + event );
service = (T) event.getPostEventServiceItem().service;
}
public void serviceRemoved( ServiceDiscoveryEvent event )
{
System.out.println( "Service Removed: " + event );
service = null;
}
public void serviceChanged( ServiceDiscoveryEvent event )
{
System.out.println( "Service Changed: " + event );
}
} );
}
public void dispose()
{
dm.removeDiscoveryListener( this );
}
public void discovered( DiscoveryEvent e )
{
System.out.println( "Discovered: " + e.getGroups() );
}
public void discarded( DiscoveryEvent e )
{
System.out.println( "Discarded: " + e.getGroups() );
}
}
private static class EntryIterator
implements Iterator<String>
{
private MatchSet matchSet;
private StorageEntry nextEntry;
public EntryIterator( MatchSet matchSet )
{
this.matchSet = matchSet;
try
{
nextEntry = getNext( matchSet );
}
catch( RemoteException e )
{
e.printStackTrace();
}
catch( UnusableEntryException e )
{
e.printStackTrace();
}
}
private StorageEntry getNext( MatchSet matchSet )
throws RemoteException, UnusableEntryException
{
StorageEntry storageEntry = (StorageEntry) matchSet.next();
return storageEntry;
}
public boolean hasNext()
{
return nextEntry != null;
}
public String next()
{
String result = nextEntry.data();
try
{
nextEntry = getNext( matchSet );
}
catch( RemoteException e )
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
catch( UnusableEntryException e )
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return result;
}
public void remove()
{
throw new UnsupportedOperationException();
}
}
}