/* Copyright 2010 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.entitystore.voldemort;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import org.qi4j.api.configuration.Configuration;
import org.qi4j.api.entity.EntityReference;
import org.qi4j.api.injection.scope.This;
import org.qi4j.api.injection.scope.Uses;
import org.qi4j.api.service.Activatable;
import org.qi4j.entitystore.map.MapEntityStore;
import org.qi4j.spi.entity.EntityType;
import org.qi4j.spi.entitystore.EntityNotFoundException;
import org.qi4j.spi.entitystore.EntityStoreException;
import org.qi4j.spi.service.ServiceDescriptor;
import voldemort.client.ClientConfig;
import voldemort.client.RoutingTier;
import voldemort.client.SocketStoreClientFactory;
import voldemort.client.StoreClient;
import voldemort.client.StoreClientFactory;
import voldemort.client.protocol.RequestFormatType;
import voldemort.versioning.ObsoleteVersionException;
import voldemort.versioning.Versioned;
/**
* JDBM implementation of SerializationStore
*/
public class VoldemortEntityStoreMixin
implements Activatable, MapEntityStore
{
@This
private ReadWriteLock lock;
@This
private Configuration<VoldemortConfiguration> config;
@Uses
private ServiceDescriptor descriptor;
private StoreClient<String, byte[]> client;
private StoreClientFactory factory;
// Activatable implementation
public void activate()
throws Exception
{
VoldemortConfiguration conf = config.configuration();
ClientConfig config = new ClientConfig();
{
List<String> value = conf.bootstrapUrl().get();
if( value != null )
{
config.setBootstrapUrls( value );
}
else
{
config.setBootstrapUrls( "tcp://localhost:8581" );
}
}
{
Integer id = conf.clientZoneId().get();
if( id != null )
{
config.setClientZoneId( id );
}
}
{
Integer connectionTimeout = conf.connectionTimeout().get();
if( connectionTimeout != null )
{
config.setConnectionTimeout( connectionTimeout, TimeUnit.MILLISECONDS );
}
}
{
Boolean enable = conf.enableJmx().get();
if( enable != null )
{
config.setEnableJmx( enable );
}
}
{
Boolean enable = conf.enablePipelineRoutedStore().get();
if( enable != null )
{
config.setEnablePipelineRoutedStore( enable );
}
}
{
Long recoveryInterval = conf.failureDetectorAsyncRecoveryInterval().get();
if( recoveryInterval != null )
{
config.setFailureDetectorAsyncRecoveryInterval( recoveryInterval );
}
}
{
Long bannagePeriod = conf.failureDetectorBannagePeriod().get();
if( bannagePeriod != null )
{
config.setFailureDetectorBannagePeriod( bannagePeriod );
}
}
{
List<String> failureDetectorCatastrophicErrorTypes = conf.failureDetectorCatastrophicErrorType().get();
if( failureDetectorCatastrophicErrorTypes != null )
{
config.setFailureDetectorCatastrophicErrorTypes( failureDetectorCatastrophicErrorTypes );
}
}
{
String failureDetectorImplementation = conf.failureDetectorImplementation().get();
if( failureDetectorImplementation != null )
{
config.setFailureDetectorImplementation( failureDetectorImplementation );
}
}
{
Long failureDetectorRequestLengthThreshold = conf.failureDetectoreRequestLengthThreshold().get();
if( failureDetectorRequestLengthThreshold != null )
{
config.setFailureDetectorRequestLengthThreshold( failureDetectorRequestLengthThreshold );
}
}
{
Integer failureDetectorThreshold = conf.failureDetectorThreshold().get();
if( failureDetectorThreshold != null )
{
config.setFailureDetectorThreshold( failureDetectorThreshold );
}
}
{
Integer detectorThresholdCountMinimum = conf.failureDetectorThresholdCountMinimum().get();
if( detectorThresholdCountMinimum != null )
{
config.setFailureDetectorThresholdCountMinimum( detectorThresholdCountMinimum );
}
}
{
Long failureDetectorThresholdInterval = conf.failureDetectorThreasholdInterval().get();
if( failureDetectorThresholdInterval != null )
{
config.setFailureDetectorThresholdInterval( failureDetectorThresholdInterval );
}
}
{
Integer maxBootstrapRetries = conf.maxBootstrapRetries().get();
if( maxBootstrapRetries != null )
{
config.setMaxBootstrapRetries( maxBootstrapRetries );
}
}
{
Integer maxConnectionsPerNode = conf.setMaxConnectionsPerNode().get();
if( maxConnectionsPerNode != null )
{
config.setMaxConnectionsPerNode( maxConnectionsPerNode );
}
}
{
Integer maxQueueRequests = conf.maxQueueRequests().get();
if( maxQueueRequests != null )
{
config.setMaxQueuedRequests( maxQueueRequests );
}
}
{
Integer maxThreads = conf.maxThreads().get();
if( maxThreads != null )
{
config.setMaxThreads( maxThreads );
}
}
{
Integer maxTotalConnections = conf.maxTotalConnections().get();
if( maxTotalConnections != null )
{
config.setMaxTotalConnections( maxTotalConnections );
}
}
{
String formatTypeCode = conf.requestFormatType().get();
if( formatTypeCode != null )
{
RequestFormatType formatType = RequestFormatType.fromCode( formatTypeCode );
config.setRequestFormatType( formatType );
}
}
{
String routingTierString = conf.routingTier().get();
if( routingTierString != null )
{
RoutingTier routingTier = RoutingTier.fromDisplay( routingTierString );
config.setRoutingTier( routingTier );
}
}
{
Integer routingTimeout = conf.routingTimeout().get();
if( routingTimeout != null )
{
config.setRoutingTimeout( routingTimeout, TimeUnit.MILLISECONDS );
}
}
{
Integer selectors = conf.selectors().get();
if( selectors != null )
{
config.setSelectors( selectors );
}
}
{
Integer bufferSize = conf.socketBufferSize().get();
if( bufferSize != null )
{
config.setSocketBufferSize( bufferSize );
}
}
{
Boolean socketKeepAlive = conf.socketKeepAlive().get();
if( socketKeepAlive != null )
{
config.setSocketKeepAlive( socketKeepAlive );
}
}
{
Integer socketTimeout = conf.socketTimeout().get();
if( socketTimeout != null )
{
config.setSocketTimeout( socketTimeout, TimeUnit.MILLISECONDS );
}
}
{
Integer idleTime = conf.threadIdleTime().get();
if( idleTime != null )
{
config.setThreadIdleTime( idleTime, TimeUnit.MILLISECONDS );
}
}
factory = new SocketStoreClientFactory( config );
// create a client that executes operations on a single store
String storeName = conf.storeName().get();
if( storeName == null)
storeName = "qi4j-entities";
client = factory.getStoreClient( storeName );
}
public void passivate()
throws Exception
{
factory.close();
}
public Reader get( EntityReference entityReference )
throws EntityStoreException
{
try
{
Versioned<byte[]> versioned = client.get( entityReference.identity() );
if( versioned == null )
{
throw new EntityNotFoundException( entityReference );
}
byte[] serializedState = versioned.getValue();
return new StringReader( new String( serializedState, "UTF-8" ) );
}
catch( IOException e )
{
throw new EntityStoreException( e );
}
}
public void applyChanges( MapChanges changes )
throws IOException
{
try
{
changes.visitMap( new MapChanger()
{
public Writer newEntity( final EntityReference ref, EntityType entityType )
throws IOException
{
return new StringWriter( 1000 )
{
@Override
public void close()
throws IOException
{
super.close();
byte[] stateArray = toString().getBytes( "UTF-8" );
client.put( ref.identity(), stateArray );
}
};
}
public Writer updateEntity( final EntityReference ref, EntityType entityType )
throws IOException
{
return new StringWriter( 1000 )
{
@Override
public void close()
throws IOException
{
super.close();
byte[] stateArray = toString().getBytes( "UTF-8" );
try
{
client.put( ref.identity(), stateArray );
}
catch( ObsoleteVersionException e )
{
throw new ConcurrentModificationException(
"Concurrent modification attempted for " + ref.identity() );
}
}
}
;
}
public void removeEntity( EntityReference ref, EntityType entityType )
throws EntityNotFoundException
{
client.delete( ref.identity() );
}
} );
}
catch( Exception e )
{
if( e instanceof IOException )
{
throw (IOException) e;
}
else if( e instanceof EntityStoreException )
{
throw (EntityStoreException) e;
}
else
{
IOException exception = new IOException();
exception.initCause( e );
throw exception;
}
}
}
public void visitMap( MapEntityStoreVisitor visitor )
{
// TODO: Can't get hold of all entities, unless storing all the keys separately, which is enormously expensive
}
}