/* 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 } }