/* 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.io.Input;
import org.qi4j.api.io.Output;
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;
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 connectionTimeout = conf.connectionTimeout().get();
if (connectionTimeout != null)
{
config.setConnectionTimeout(connectionTimeout, TimeUnit.MILLISECONDS);
}
}
{
Boolean enable = conf.enableJmx().get();
if (enable != null)
{
config.setEnableJmx(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 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 Input<Reader, IOException> entityStates()
{
return new Input<Reader, IOException>()
{
@Override
public <ReceiverThrowableType extends Throwable> void transferTo(Output<? super Reader, ReceiverThrowableType> receiverThrowableTypeOutput) throws IOException, ReceiverThrowableType
{
// TODO: Can't get hold of all entities, unless storing all the keys separately, which is enormously expensive
}
};
}
}