http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-cassandra/src/main/java/org/qi4j/entitystore/cassandra/CassandraMapEntityStoreMixin.java ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-cassandra/src/main/java/org/qi4j/entitystore/cassandra/CassandraMapEntityStoreMixin.java b/qi4j/extensions/entitystore-cassandra/src/main/java/org/qi4j/entitystore/cassandra/CassandraMapEntityStoreMixin.java new file mode 100644 index 0000000..eb6a214 --- /dev/null +++ b/qi4j/extensions/entitystore-cassandra/src/main/java/org/qi4j/entitystore/cassandra/CassandraMapEntityStoreMixin.java @@ -0,0 +1,200 @@ +package org.qi4j.entitystore.cassandra; + +import me.prettyprint.cassandra.serializers.BytesSerializer; +import me.prettyprint.cassandra.serializers.StringSerializer; +import me.prettyprint.hector.api.Cluster; +import me.prettyprint.hector.api.Keyspace; +import me.prettyprint.hector.api.beans.HColumn; +import me.prettyprint.hector.api.factory.HFactory; +import me.prettyprint.hector.api.mutation.MutationResult; +import me.prettyprint.hector.api.query.ColumnQuery; +import me.prettyprint.hector.api.query.QueryResult; +import org.qi4j.api.entity.EntityReference; +import org.qi4j.api.injection.scope.Service; +import org.qi4j.api.entity.EntityDescriptor; +import org.qi4j.api.service.ServiceActivation; +import org.qi4j.io.Input; +import org.qi4j.spi.entitystore.helpers.MapEntityStore; +import org.qi4j.spi.entitystore.EntityAlreadyExistsException; +import org.qi4j.spi.entitystore.EntityNotFoundException; +import org.qi4j.spi.entitystore.EntityStoreException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import static me.prettyprint.hector.api.factory.HFactory.*; + +/** + * // TODO: Document this + * + * @author pvdyck + * @since 4.0 + */ +public class CassandraMapEntityStoreMixin implements MapEntityStore, ServiceActivation { + private static final String COLUMN_NAME = "entry"; + private final Logger logger = LoggerFactory.getLogger(CassandraMapEntityStoreMixin.class); + + private static final String keySpace = "Qi4j"; + static final String cf = "Qi4jEntries"; + + static final int BYTE_ARRAY_BUFFER_INITIAL_SIZE = 512; + + Cluster c; + Keyspace ko; + + + private static final StringSerializer se = new StringSerializer(); + private static final BytesSerializer bs = new BytesSerializer(); + + + @Service + private + CassandraConfiguration conf; + + public void activateService() throws Exception { + c = HFactory.getOrCreateCluster("Qi4jCluster", conf.getHost() + ":" + conf.getPort()); + ko = HFactory.createKeyspace(keySpace, c); + logger.info("started cassandra store"); + } + + + public void passivateService() throws Exception { + logger.info("shutting down cassandra"); + } + + + public void applyChanges(final MapChanges changes) throws IOException { + if (conf.readOnly()) { + throw new EntityStoreException("Read-only Entity Store"); + } + + try { + final MapUpdater changer = new MapUpdater(); + changes.visitMap(changer); + + MutationResult result = changer.m.execute(); + + logger.info("applying changes to cassandra store " + result.getExecutionTimeMicro() + " / " + result.getHostUsed()); + + } catch (Throwable e) { + throw new EntityStoreException("Exception during cassandra batch " + + " - ", e); + } + } + + boolean contains(EntityReference ref) throws EntityStoreException { + try { + //TODO optimise this... no need to fetch everything + get(ref); + return true; + } catch (final EntityNotFoundException e1) { + return false; + } catch (final Exception e1) { + throw new EntityStoreException(e1); + } + } + + public Reader get(final EntityReference ref) { + //TODO .. should be able to use only one ColumnQuery ... btw .. is it thread-safe ? + ColumnQuery<String, byte[]> q = createColumnQuery(ko, se, bs); + q.setName(COLUMN_NAME).setColumnFamily(cf); + QueryResult<HColumn<String, byte[]>> r; + try { + r = q.setKey(ref.toString()).execute(); + } catch (Exception e) { + throw new EntityStoreException(e); + } + if (r.get() == null) throw new EntityNotFoundException(ref); + + try { + return createReader(new ByteArrayInputStream(r.get().getValue())); + } catch (IOException e) { + throw new EntityStoreException(e); + } + } + + + public Input<Reader, IOException> entityStates() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + + Writer createWriter(OutputStream out) throws IOException { + if (conf.gzipCompress()) + return new OutputStreamWriter(new GZIPOutputStream(out)); + return new OutputStreamWriter(out); + } + + private Reader createReader(InputStream in) throws IOException { + if (conf.gzipCompress()) + return new InputStreamReader(new GZIPInputStream(in)); + return new InputStreamReader(in); + } + + void checkAbsentBeforeCreate(EntityReference ref) { + if (!conf.checkAbsentBeforeCreate()) + return; + if (contains(ref)) + throw new EntityAlreadyExistsException(ref); + } + + void checkPresentBeforeDelete(EntityReference ref) { + if (!conf.checkPresentBeforeDelete()) + return; + if (!contains(ref)) + throw new EntityNotFoundException(ref); + } + + void checkPresentBeforeUpdate(EntityReference ref) { + if (!conf.checkPresentBeforeUpdate()) + return; + if (!contains(ref)) + throw new EntityNotFoundException(ref); + } + + class MapUpdater implements MapEntityStore.MapChanger { + me.prettyprint.hector.api.mutation.Mutator m = createMutator(ko); + + public Writer newEntity(final EntityReference ref, EntityDescriptor entityDescriptor) { + checkAbsentBeforeCreate(ref); + return getWriter(ref); + } + + public Writer updateEntity(final EntityReference ref, EntityDescriptor entityDescriptor) + throws IOException { + checkPresentBeforeUpdate(ref); + return getWriter(ref); + } + + public void removeEntity(EntityReference ref, EntityDescriptor entityDescriptor) + throws EntityNotFoundException { + checkPresentBeforeDelete(ref); + m.addDeletion(ref.identity(), cf, COLUMN_NAME, se); + } + + private Writer getWriter(final EntityReference ref) { + try { + return createWriter(new ByteArrayOutputStream(CassandraMapEntityStoreMixin.BYTE_ARRAY_BUFFER_INITIAL_SIZE) { + @Override + public void close() throws IOException { + super.close(); + m.addInsertion(ref.identity(), cf, createColumn(COLUMN_NAME, toByteArray(), se, bs)); + } + }); + } catch (final Exception e) { + throw new EntityStoreException(e); + } + } + } +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-cassandra/src/test/java/org/qi4j/entitystore/cassandra/CassandraConfigurationService.java ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-cassandra/src/test/java/org/qi4j/entitystore/cassandra/CassandraConfigurationService.java b/qi4j/extensions/entitystore-cassandra/src/test/java/org/qi4j/entitystore/cassandra/CassandraConfigurationService.java new file mode 100644 index 0000000..93bed0d --- /dev/null +++ b/qi4j/extensions/entitystore-cassandra/src/test/java/org/qi4j/entitystore/cassandra/CassandraConfigurationService.java @@ -0,0 +1,57 @@ +package org.qi4j.entitystore.cassandra; + +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.service.ServiceComposite; + +@Mixins(CassandraConfigurationService.CassandraConfigurationMixin.class) +public interface CassandraConfigurationService extends ServiceComposite, + CassandraConfiguration { + public class CassandraConfigurationMixin implements CassandraConfiguration { + private final boolean gzipCompress = true; + private final boolean checkAbsentBeforeCreate = false; + private final boolean checkPresentBeforeDelete = false; + private final boolean checkPresentBeforeUpdate = false; + + private final String NULL = null; + private final String LOCALHOST = "localhost"; + + public boolean checkAbsentBeforeCreate() { + return checkAbsentBeforeCreate; + } + + public boolean checkPresentBeforeDelete() { + return checkPresentBeforeDelete; + } + + public boolean checkPresentBeforeUpdate() { + return checkPresentBeforeUpdate; + } + + public boolean gzipCompress() { + return gzipCompress; + } + + + public boolean readOnly() { + return false; + } + + public String getHost() { + return LOCALHOST; + } + + public String getLogin() { + return NULL; + } + + public String getPassword() { + return NULL; + } + + public int getPort() { + return 9160; + } + + + } +} http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-cassandra/src/test/java/org/qi4j/entitystore/cassandra/CassandraEntityStoreTest.java ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-cassandra/src/test/java/org/qi4j/entitystore/cassandra/CassandraEntityStoreTest.java b/qi4j/extensions/entitystore-cassandra/src/test/java/org/qi4j/entitystore/cassandra/CassandraEntityStoreTest.java new file mode 100644 index 0000000..d018404 --- /dev/null +++ b/qi4j/extensions/entitystore-cassandra/src/test/java/org/qi4j/entitystore/cassandra/CassandraEntityStoreTest.java @@ -0,0 +1,46 @@ +package org.qi4j.entitystore.cassandra; + +import org.apache.cassandra.contrib.utils.service.CassandraServiceDataCleaner; +import org.apache.cassandra.service.EmbeddedCassandraService; +import org.apache.thrift.transport.TTransportException; +import org.junit.BeforeClass; +import org.qi4j.bootstrap.AssemblyException; +import org.qi4j.bootstrap.ModuleAssembly; +import org.qi4j.test.entity.AbstractEntityStoreTest; + +import java.io.IOException; +import org.qi4j.api.value.ValueSerialization; +import org.qi4j.valueserialization.orgjson.OrgJsonValueSerializationService; + + +//need to launch a cassandra instance b4 running this test... +//waiting for Hector version of the ES +public class CassandraEntityStoreTest extends AbstractEntityStoreTest { + + private static EmbeddedCassandraService cassandra; + private static Thread t; + + @Override + public void assemble(ModuleAssembly module) throws AssemblyException { + super.assemble(module); + module.addServices(CassandraEntityStoreService.class, + CassandraConfigurationService.class); + module.services( OrgJsonValueSerializationService.class ).taggedWith( ValueSerialization.Formats.JSON ); + } + + @BeforeClass + public static void setup() throws TTransportException, IOException, + InterruptedException { + // Tell cassandra where the configuration files are. + // Use the test configuration file. + + System.setProperty("storage-config", "src/test/resources"); + + new CassandraServiceDataCleaner().prepare(); + cassandra = new EmbeddedCassandraService(); + cassandra.init(); + t = new Thread(cassandra); + t.setDaemon(true); + t.start(); + } +} http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-cassandra/src/test/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-cassandra/src/test/resources/log4j.properties b/qi4j/extensions/entitystore-cassandra/src/test/resources/log4j.properties new file mode 100644 index 0000000..3648e32 --- /dev/null +++ b/qi4j/extensions/entitystore-cassandra/src/test/resources/log4j.properties @@ -0,0 +1,7 @@ +log4j.rootLogger=INFO,stdout + +# stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout + +log4j.category.org.apache=INFO, stdout \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-cassandra/src/test/resources/storage-conf.xml ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-cassandra/src/test/resources/storage-conf.xml b/qi4j/extensions/entitystore-cassandra/src/test/resources/storage-conf.xml new file mode 100644 index 0000000..902c7bc --- /dev/null +++ b/qi4j/extensions/entitystore-cassandra/src/test/resources/storage-conf.xml @@ -0,0 +1,347 @@ +<Storage> + <!--======================================================================--> + <!-- Basic Configuration --> + <!--======================================================================--> + + <!-- + ~ The name of this cluster. This is mainly used to prevent machines in + ~ one logical cluster from joining another. + --> + <ClusterName>Qi4j</ClusterName> + + <!-- + ~ Turn on to make new [non-seed] nodes automatically migrate the right data + ~ to themselves. (If no InitialToken is specified, they will pick one + ~ such that they will get half the range of the most-loaded node.) + ~ If a node starts up without bootstrapping, it will mark itself bootstrapped + ~ so that you can't subsequently accidently bootstrap a node with + ~ data on it. (You can reset this by wiping your data and commitlog + ~ directories.) + ~ + ~ Off by default so that new clusters and upgraders from 0.4 don't + ~ bootstrap immediately. You should turn this on when you start adding + ~ new nodes to a cluster that already has data on it. (If you are upgrading + ~ from 0.4, start your cluster with it off once before changing it to true. + ~ Otherwise, no data will be lost but you will incur a lot of unnecessary + ~ I/O before your cluster starts up.) + --> + <AutoBootstrap>false</AutoBootstrap> + + <!-- + ~ Keyspaces and ColumnFamilies: + ~ A ColumnFamily is the Cassandra concept closest to a relational + ~ table. Keyspaces are separate groups of ColumnFamilies. Except in + ~ very unusual circumstances you will have one Keyspace per application. + + ~ There is an implicit keyspace named 'system' for Cassandra internals. + --> + <Keyspaces> + <Keyspace Name="Qi4j"> + <!-- + ~ ColumnFamily definitions have one required attribute (Name) + ~ and several optional ones. + ~ + ~ The CompareWith attribute tells Cassandra how to sort the columns + ~ for slicing operations. The default is BytesType, which is a + ~ straightforward lexical comparison of the bytes in each column. + ~ Other options are AsciiType, UTF8Type, LexicalUUIDType, TimeUUIDType, + ~ and LongType. You can also specify the fully-qualified class + ~ name to a class of your choice extending + ~ org.apache.cassandra.db.marshal.AbstractType. + ~ + ~ SuperColumns have a similar CompareSubcolumnsWith attribute. + ~ + ~ BytesType: Simple sort by byte value. No validation is performed. + ~ AsciiType: Like BytesType, but validates that the input can be + ~ parsed as US-ASCII. + ~ UTF8Type: A string encoded as UTF8 + ~ LongType: A 64bit long + ~ LexicalUUIDType: A 128bit UUID, compared lexically (by byte value) + ~ TimeUUIDType: a 128bit version 1 UUID, compared by timestamp + ~ + ~ (To get the closest approximation to 0.3-style supercolumns, you + ~ would use CompareWith=UTF8Type CompareSubcolumnsWith=LongType.) + ~ + ~ An optional `Comment` attribute may be used to attach additional + ~ human-readable information about the column family to its definition. + ~ + ~ The optional KeysCached attribute specifies + ~ the number of keys per sstable whose locations we keep in + ~ memory in "mostly LRU" order. (JUST the key locations, NOT any + ~ column values.) Specify a fraction (value less than 1), a percentage + ~ (ending in a % sign) or an absolute number of keys to cache. + ~ + ~ The optional RowsCached attribute specifies the number of rows + ~ whose entire contents we cache in memory. Do not use this on + ~ ColumnFamilies with large rows, or ColumnFamilies with high write:read + ~ ratios. Specify a fraction (value less than 1), a percentage (ending in + ~ a % sign) or an absolute number of rows to cache. + --> + + <ColumnFamily CompareWith="BytesType" Name="Qi4jEntries" KeysCached="10%"/> + + + <!-- + ~ Strategy: Setting this to the class that implements + ~ IReplicaPlacementStrategy will change the way the node picker works. + ~ Out of the box, Cassandra provides + ~ org.apache.cassandra.locator.RackUnawareStrategy and + ~ org.apache.cassandra.locator.RackAwareStrategy (place one replica in + ~ a different datacenter, and the others on different racks in the same + ~ one.) + --> + <ReplicaPlacementStrategy>org.apache.cassandra.locator.RackUnawareStrategy</ReplicaPlacementStrategy> + + <!-- Number of replicas of the data --> + <ReplicationFactor>1</ReplicationFactor> + + <!-- + ~ EndPointSnitch: Setting this to the class that implements + ~ AbstractEndpointSnitch, which lets Cassandra know enough + ~ about your network topology to route requests efficiently. + ~ Out of the box, Cassandra provides org.apache.cassandra.locator.EndPointSnitch, + ~ and PropertyFileEndPointSnitch is available in contrib/. + --> + <EndPointSnitch>org.apache.cassandra.locator.EndPointSnitch</EndPointSnitch> + + </Keyspace> + </Keyspaces> + + <!-- + ~ Authenticator: any IAuthenticator may be used, including your own as long + ~ as it is on the classpath. Out of the box, Cassandra provides + ~ org.apache.cassandra.auth.AllowAllAuthenticator and, + ~ org.apache.cassandra.auth.SimpleAuthenticator + ~ (SimpleAuthenticator uses access.properties and passwd.properties by + ~ default). + ~ + ~ If you don't specify an authenticator, AllowAllAuthenticator is used. + --> + <Authenticator>org.apache.cassandra.auth.AllowAllAuthenticator</Authenticator> + + <!-- + ~ Partitioner: any IPartitioner may be used, including your own as long + ~ as it is on the classpath. Out of the box, Cassandra provides + ~ org.apache.cassandra.dht.RandomPartitioner, + ~ org.apache.cassandra.dht.OrderPreservingPartitioner, and + ~ org.apache.cassandra.dht.CollatingOrderPreservingPartitioner. + ~ (CollatingOPP colates according to EN,US rules, not naive byte + ~ ordering. Use this as an example if you need locale-aware collation.) + ~ Range queries require using an order-preserving partitioner. + ~ + ~ Achtung! Changing this parameter requires wiping your data + ~ directories, since the partitioner can modify the sstable on-disk + ~ format. + --> + <Partitioner>org.apache.cassandra.dht.OrderPreservingPartitioner</Partitioner> + + <!-- + ~ If you are using an order-preserving partitioner and you know your key + ~ distribution, you can specify the token for this node to use. (Keys + ~ are sent to the node with the "closest" token, so distributing your + ~ tokens equally along the key distribution space will spread keys + ~ evenly across your cluster.) This setting is only checked the first + ~ time a node is started. + + ~ This can also be useful with RandomPartitioner to force equal spacing + ~ of tokens around the hash space, especially for clusters with a small + ~ number of nodes. + --> + <InitialToken></InitialToken> + + <!-- + ~ Directories: Specify where Cassandra should store different data on + ~ disk. Keep the data disks and the CommitLog disks separate for best + ~ performance + --> + <CommitLogDirectory>./tmp/cassandra-cachestore/commitlog</CommitLogDirectory> + <DataFileDirectories> + <DataFileDirectory>./tmp/cassandra-cachestore/data</DataFileDirectory> + </DataFileDirectories> + <CalloutLocation>./tmp/cassandra-cachestore/callouts</CalloutLocation> + <SavedCachesDirectory>./tmp/saved_caches</SavedCachesDirectory> + + + <!-- + ~ Addresses of hosts that are deemed contact points. Cassandra nodes + ~ use this list of hosts to find each other and learn the topology of + ~ the ring. You must change this if you are running multiple nodes! + --> + <Seeds> + <Seed>127.0.0.1</Seed> + </Seeds> + + + <!-- Miscellaneous --> + + <!-- Time to wait for a reply from other nodes before failing the command --> + <RpcTimeoutInMillis>10000</RpcTimeoutInMillis> + <!-- Size to allow commitlog to grow to before creating a new segment --> + <CommitLogRotationThresholdInMB>128</CommitLogRotationThresholdInMB> + + + <!-- Local hosts and ports --> + + <!-- + ~ Address to bind to and tell other nodes to connect to. You _must_ + ~ change this if you want multiple nodes to be able to communicate! + ~ + ~ Leaving it blank leaves it up to InetAddress.getLocalHost(). This + ~ will always do the Right Thing *if* the node is properly configured + ~ (hostname, name resolution, etc), and the Right Thing is to use the + ~ address associated with the hostname (it might not be). + --> + <ListenAddress></ListenAddress> + <!-- internal communications port --> + <StoragePort>7000</StoragePort> + + <!-- + ~ The address to bind the Thrift RPC service to. Unlike ListenAddress + ~ above, you *can* specify 0.0.0.0 here if you want Thrift to listen on + ~ all interfaces. + ~ + ~ Leaving this blank has the same effect it does for ListenAddress, + ~ (i.e. it will be based on the configured hostname of the node). + --> + <ThriftAddress>localhost</ThriftAddress> + <!-- Thrift RPC port (the port clients connect to). --> + <ThriftPort>9160</ThriftPort> + <!-- + ~ Whether or not to use a framed transport for Thrift. If this option + ~ is set to true then you must also use a framed transport on the + ~ client-side, (framed and non-framed transports are not compatible). + --> + <ThriftFramedTransport>false</ThriftFramedTransport> + + + <!--======================================================================--> + <!-- Memory, Disk, and Performance --> + <!--======================================================================--> + + <!-- + ~ Access mode. mmapped i/o is substantially faster, but only practical on + ~ a 64bit machine (which notably does not include EC2 "small" instances) + ~ or relatively small datasets. "auto", the safe choice, will enable + ~ mmapping on a 64bit JVM. Other values are "mmap", "mmap_index_only" + ~ (which may allow you to get part of the benefits of mmap on a 32bit + ~ machine by mmapping only index files) and "standard". + ~ (The buffer size settings that follow only apply to standard, + ~ non-mmapped i/o.) + --> + <DiskAccessMode>auto</DiskAccessMode> + + <!-- + ~ Size of compacted row above which to log a warning. (If compacted + ~ rows do not fit in memory, Cassandra will crash. This is explained + ~ in http://wiki.apache.org/cassandra/CassandraLimitations and is + ~ scheduled to be fixed in 0.7.) + --> + <RowWarningThresholdInMB>512</RowWarningThresholdInMB> + + <!-- + ~ Buffer size to use when performing contiguous column slices. Increase + ~ this to the size of the column slices you typically perform. + ~ (Name-based queries are performed with a buffer size of + ~ ColumnIndexSizeInKB.) + --> + <SlicedBufferSizeInKB>64</SlicedBufferSizeInKB> + + <!-- +~ Buffer size to use when flushing memtables to disk. (Only one +~ memtable is ever flushed at a time.) Increase (decrease) the index +~ buffer size relative to the data buffer if you have few (many) +~ columns per key. Bigger is only better _if_ your memtables get large +~ enough to use the space. (Check in your data directory after your +~ app has been running long enough.) --> + <FlushDataBufferSizeInMB>32</FlushDataBufferSizeInMB> + <FlushIndexBufferSizeInMB>8</FlushIndexBufferSizeInMB> + + <!-- + ~ Add column indexes to a row after its contents reach this size. + ~ Increase if your column values are large, or if you have a very large + ~ number of columns. The competing causes are, Cassandra has to + ~ deserialize this much of the row to read a single column, so you want + ~ it to be small - at least if you do many partial-row reads - but all + ~ the index data is read for each access, so you don't want to generate + ~ that wastefully either. + --> + <ColumnIndexSizeInKB>64</ColumnIndexSizeInKB> + + <!-- + ~ Flush memtable after this much data has been inserted, including + ~ overwritten data. There is one memtable per column family, and + ~ this threshold is based solely on the amount of data stored, not + ~ actual heap memory usage (there is some overhead in indexing the + ~ columns). + --> + <MemtableThroughputInMB>64</MemtableThroughputInMB> + <!-- + ~ Throughput setting for Binary Memtables. Typically these are + ~ used for bulk load so you want them to be larger. + --> + <BinaryMemtableThroughputInMB>256</BinaryMemtableThroughputInMB> + <!-- + ~ The maximum number of columns in millions to store in memory per + ~ ColumnFamily before flushing to disk. This is also a per-memtable + ~ setting. Use with MemtableThroughputInMB to tune memory usage. + --> + <MemtableOperationsInMillions>0.3</MemtableOperationsInMillions> + <!-- + ~ The maximum time to leave a dirty memtable unflushed. + ~ (While any affected columnfamilies have unflushed data from a + ~ commit log segment, that segment cannot be deleted.) + ~ This needs to be large enough that it won't cause a flush storm + ~ of all your memtables flushing at once because none has hit + ~ the size or count thresholds yet. For production, a larger + ~ value such as 1440 is recommended. + --> + <MemtableFlushAfterMinutes>60</MemtableFlushAfterMinutes> + + <!-- + ~ Unlike most systems, in Cassandra writes are faster than reads, so + ~ you can afford more of those in parallel. A good rule of thumb is 2 + ~ concurrent reads per processor core. Increase ConcurrentWrites to + ~ the number of clients writing at once if you enable CommitLogSync + + ~ CommitLogSyncDelay. --> + <ConcurrentReads>8</ConcurrentReads> + <ConcurrentWrites>32</ConcurrentWrites> + + <!-- + ~ CommitLogSync may be either "periodic" or "batch." When in batch + ~ mode, Cassandra won't ack writes until the commit log has been + ~ fsynced to disk. It will wait up to CommitLogSyncBatchWindowInMS + ~ milliseconds for other writes, before performing the sync. + + ~ This is less necessary in Cassandra than in traditional databases + ~ since replication reduces the odds of losing data from a failure + ~ after writing the log entry but before it actually reaches the disk. + ~ So the other option is "timed," where writes may be acked immediately + ~ and the CommitLog is simply synced every CommitLogSyncPeriodInMS + ~ milliseconds. + --> + <CommitLogSync>periodic</CommitLogSync> + <!-- + ~ Interval at which to perform syncs of the CommitLog in periodic mode. + ~ Usually the default of 10000ms is fine; increase it if your i/o + ~ load is such that syncs are taking excessively long times. + --> + <CommitLogSyncPeriodInMS>10000</CommitLogSyncPeriodInMS> + <!-- + ~ Delay (in milliseconds) during which additional commit log entries + ~ may be written before fsync in batch mode. This will increase + ~ latency slightly, but can vastly improve throughput where there are + ~ many writers. Set to zero to disable (each entry will be synced + ~ individually). Reasonable values range from a minimal 0.1 to 10 or + ~ even more if throughput matters more than latency. + --> + <!-- <CommitLogSyncBatchWindowInMS>1</CommitLogSyncBatchWindowInMS> --> + + <!-- + ~ Time to wait before garbage-collection deletion markers. Set this to + ~ a large enough value that you are confident that the deletion marker + ~ will be propagated to all replicas by the time this many seconds has + ~ elapsed, even in the face of hardware failures. The default value is + ~ ten days. + --> + <GCGraceSeconds>864000</GCGraceSeconds> +</Storage> http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/.gitignore ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/.gitignore b/qi4j/extensions/entitystore-coherence/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/.gitignore @@ -0,0 +1 @@ +target/ http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/osgi.bundle ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/osgi.bundle b/qi4j/extensions/entitystore-coherence/osgi.bundle new file mode 100644 index 0000000..d771f2e --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/osgi.bundle @@ -0,0 +1,285 @@ +Bundle-Activator: + +Private-Package: + +Ignore-Package: com_cenqua_clover, \ + javax.crypto, \ + javax.crypto.spec, \ + javax.security.auth, \ + javax.jms, \ + javax.management, \ + javax.management.loading, \ + javax.management.openmbean, \ + javax.management.remote, \ + javax.naming, \ + org.omg.CORBA, \ + org.omg.CORBA.ORBPackage, \ + org.omg.Security, \ + org.omg.SecurityLevel2, \ + org.xml.sax, \ + org.xml.sax.helpers, \ + com.ibm.CORBA.iiop, \ + com.ibm.IExtendedSecurity, \ + com.ibm.ejs.oa, \ + javax.ejb, \ + javax.resource.cci, \ + javax.rmi, \ + javax.security.auth.callback, \ + javax.security.auth.login, \ + javax.security.auth.x500, \ + javax.servlet, \ + javax.servlet.http, \ + javax.transaction, \ + org.apache.log4j, \ + sun.misc, \ + sun.rmi.server + +Import-Package: org.qi4j.api.common, \ + org.qi4j.api.concern, \ + org.qi4j.api.configuration, \ + org.qi4j.api.entity, \ + org.qi4j.api.injection.scope, \ + org.qi4j.api.mixin, \ + org.qi4j.api.property, \ + org.qi4j.api.service, \ + org.qi4j.api.sideeffect, \ + org.qi4j.bootstrap, \ + org.qi4j.entitystore.memory, \ + org.qi4j.library.locking, \ + org.qi4j.spi.entity, \ + org.qi4j.spi.entity.helpers, \ + org.qi4j.spi.service, \ + com.tangosol.coherence, \ + com.tangosol.coherence.component, \ + com.tangosol.coherence.component.application, \ + com.tangosol.coherence.component.application.console, \ + com.tangosol.coherence.component.manageable, \ + com.tangosol.coherence.component.manageable.modelAdapter, \ + com.tangosol.coherence.component.manageable.modelAdapter.wrapperMBean, \ + com.tangosol.coherence.component.net, \ + com.tangosol.coherence.component.net.extend, \ + com.tangosol.coherence.component.net.extend.connection, \ + com.tangosol.coherence.component.net.extend.message, \ + com.tangosol.coherence.component.net.extend.message.request, \ + com.tangosol.coherence.component.net.extend.message.request.namedCacheRequest, \ + com.tangosol.coherence.component.net.extend.message.response, \ + com.tangosol.coherence.component.net.extend.messageFactory, \ + com.tangosol.coherence.component.net.extend.protocol, \ + com.tangosol.coherence.component.net.extend.proxy, \ + com.tangosol.coherence.component.net.extend.remoteService, \ + com.tangosol.coherence.component.net.extend.util, \ + com.tangosol.coherence.component.net.jmxHelper, \ + com.tangosol.coherence.component.net.management, \ + com.tangosol.coherence.component.net.management.gateway, \ + com.tangosol.coherence.component.net.management.model, \ + com.tangosol.coherence.component.net.management.model.localModel, \ + com.tangosol.coherence.component.net.management.model.localModel.wrapperModel, \ + com.tangosol.coherence.component.net.memberSet, \ + com.tangosol.coherence.component.net.memberSet.actualMemberSet, \ + com.tangosol.coherence.component.net.message, \ + com.tangosol.coherence.component.net.message.leaseMessage, \ + com.tangosol.coherence.component.net.message.requestMessage, \ + com.tangosol.coherence.component.net.message.requestMessage.distributedCacheKeyRequest, \ + com.tangosol.coherence.component.net.message.requestMessage.distributedCacheRequest, \ + com.tangosol.coherence.component.net.message.requestMessage.distributedCacheRequest.partialRequest, \ + com.tangosol.coherence.component.net.packet, \ + com.tangosol.coherence.component.net.packet.messagePacket, \ + com.tangosol.coherence.component.net.packet.notifyPacket, \ + com.tangosol.coherence.component.net.requestContext, \ + com.tangosol.coherence.component.net.requestStatus, \ + com.tangosol.coherence.component.net.security, \ + com.tangosol.coherence.component.net.socket, \ + com.tangosol.coherence.component.net.socket.udpSocket, \ + com.tangosol.coherence.component.net.udpPacket, \ + com.tangosol.coherence.component.util, \ + com.tangosol.coherence.component.util.cacheHandler, \ + com.tangosol.coherence.component.util.collections, \ + com.tangosol.coherence.component.util.collections.wrapperMap, \ + com.tangosol.coherence.component.util.collections.wrapperSet, \ + com.tangosol.coherence.component.util.daemon, \ + com.tangosol.coherence.component.util.daemon.queueProcessor, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.packetProcessor, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service.grid, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service.grid.replicatedCache, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service.peer, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service.peer.acceptor, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service.peer.initiator, \ + com.tangosol.coherence.component.util.deltaMap, \ + com.tangosol.coherence.component.util.deltaMap.transactionMap, \ + com.tangosol.coherence.component.util.deltaMap.transactionMap.optimistic, \ + com.tangosol.coherence.component.util.deltaMap.transactionMap.pessimistic, \ + com.tangosol.coherence.component.util.logOutput, \ + com.tangosol.coherence.component.util.pool, \ + com.tangosol.coherence.component.util.pool.simplePool, \ + com.tangosol.coherence.component.util.queue, \ + com.tangosol.coherence.component.util.queue.concurrentQueue, \ + com.tangosol.coherence.component.util.queue.concurrentQueue.balancedQueue, \ + com.tangosol.coherence.component.util.queue.concurrentQueue.dualQueue, \ + com.tangosol.coherence.component.util.safeService, \ + com.tangosol.coherence.component.util.safeService.safeCacheService, \ + com.tangosol.coherence.component.util.transactionCache, \ + com.tangosol.coherence.component.util.windowedArray, \ + com.tangosol.coherence.reporter, \ + com.tangosol.coherence.reporter.extractor, \ + com.tangosol.coherence.reporter.locator, \ + com.tangosol.dev.assembler, \ + com.tangosol.dev.compiler, \ + com.tangosol.dev.compiler.java, \ + com.tangosol.dev.component, \ + com.tangosol.dev.packager, \ + com.tangosol.dev.tools, \ + com.tangosol.engarde, \ + com.tangosol.engarde.websphere, \ + com.tangosol.io, \ + com.tangosol.io.bdb, \ + com.tangosol.io.lh, \ + com.tangosol.io.nio, \ + com.tangosol.io.pof, \ + com.tangosol.java.type, \ + com.tangosol.license, \ + com.tangosol.net, \ + com.tangosol.net.cache, \ + com.tangosol.net.internal, \ + com.tangosol.net.management, \ + com.tangosol.net.management.jmx, \ + com.tangosol.net.messaging, \ + com.tangosol.net.partition, \ + com.tangosol.net.security, \ + com.tangosol.run.component, \ + com.tangosol.run.jca, \ + com.tangosol.run.sql, \ + com.tangosol.run.xml, \ + com.tangosol.util, \ + com.tangosol.util.aggregator, \ + com.tangosol.util.comparator, \ + com.tangosol.util.extractor, \ + com.tangosol.util.filter, \ + com.tangosol.util.internal, \ + com.tangosol.util.processor, \ + com.tangosol.util.registry, \ + com.tangosol.util.stats, \ + com.tangosol.util.transformer, \ + com.sleepycat.je, \ + com.tangosol.tde.component.dev.service + + +Export-Package: org.qi4j.entitystore.coherence, \ + com.tangosol.coherence, \ + com.tangosol.coherence.component, \ + com.tangosol.coherence.component.application, \ + com.tangosol.coherence.component.application.console, \ + com.tangosol.coherence.component.manageable, \ + com.tangosol.coherence.component.manageable.modelAdapter, \ + com.tangosol.coherence.component.manageable.modelAdapter.wrapperMBean, \ + com.tangosol.coherence.component.net, \ + com.tangosol.coherence.component.net.extend, \ + com.tangosol.coherence.component.net.extend.connection, \ + com.tangosol.coherence.component.net.extend.message, \ + com.tangosol.coherence.component.net.extend.message.request, \ + com.tangosol.coherence.component.net.extend.message.request.namedCacheRequest, \ + com.tangosol.coherence.component.net.extend.message.response, \ + com.tangosol.coherence.component.net.extend.messageFactory, \ + com.tangosol.coherence.component.net.extend.protocol, \ + com.tangosol.coherence.component.net.extend.proxy, \ + com.tangosol.coherence.component.net.extend.remoteService, \ + com.tangosol.coherence.component.net.extend.util, \ + com.tangosol.coherence.component.net.jmxHelper, \ + com.tangosol.coherence.component.net.management, \ + com.tangosol.coherence.component.net.management.gateway, \ + com.tangosol.coherence.component.net.management.model, \ + com.tangosol.coherence.component.net.management.model.localModel, \ + com.tangosol.coherence.component.net.management.model.localModel.wrapperModel, \ + com.tangosol.coherence.component.net.memberSet, \ + com.tangosol.coherence.component.net.memberSet.actualMemberSet, \ + com.tangosol.coherence.component.net.message, \ + com.tangosol.coherence.component.net.message.leaseMessage, \ + com.tangosol.coherence.component.net.message.requestMessage, \ + com.tangosol.coherence.component.net.message.requestMessage.distributedCacheKeyRequest, \ + com.tangosol.coherence.component.net.message.requestMessage.distributedCacheRequest, \ + com.tangosol.coherence.component.net.message.requestMessage.distributedCacheRequest.partialRequest, \ + com.tangosol.coherence.component.net.packet, \ + com.tangosol.coherence.component.net.packet.messagePacket, \ + com.tangosol.coherence.component.net.packet.notifyPacket, \ + com.tangosol.coherence.component.net.requestContext, \ + com.tangosol.coherence.component.net.requestStatus, \ + com.tangosol.coherence.component.net.security, \ + com.tangosol.coherence.component.net.socket, \ + com.tangosol.coherence.component.net.socket.udpSocket, \ + com.tangosol.coherence.component.net.udpPacket, \ + com.tangosol.coherence.component.util, \ + com.tangosol.coherence.component.util.cacheHandler, \ + com.tangosol.coherence.component.util.collections, \ + com.tangosol.coherence.component.util.collections.wrapperMap, \ + com.tangosol.coherence.component.util.collections.wrapperSet, \ + com.tangosol.coherence.component.util.daemon, \ + com.tangosol.coherence.component.util.daemon.queueProcessor, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.packetProcessor, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service.grid, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service.grid.replicatedCache, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service.peer, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service.peer.acceptor, \ + com.tangosol.coherence.component.util.daemon.queueProcessor.service.peer.initiator, \ + com.tangosol.coherence.component.util.deltaMap, \ + com.tangosol.coherence.component.util.deltaMap.transactionMap, \ + com.tangosol.coherence.component.util.deltaMap.transactionMap.optimistic, \ + com.tangosol.coherence.component.util.deltaMap.transactionMap.pessimistic, \ + com.tangosol.coherence.component.util.logOutput, \ + com.tangosol.coherence.component.util.pool, \ + com.tangosol.coherence.component.util.pool.simplePool, \ + com.tangosol.coherence.component.util.queue, \ + com.tangosol.coherence.component.util.queue.concurrentQueue, \ + com.tangosol.coherence.component.util.queue.concurrentQueue.balancedQueue, \ + com.tangosol.coherence.component.util.queue.concurrentQueue.dualQueue, \ + com.tangosol.coherence.component.util.safeService, \ + com.tangosol.coherence.component.util.safeService.safeCacheService, \ + com.tangosol.coherence.component.util.transactionCache, \ + com.tangosol.coherence.component.util.windowedArray, \ + com.tangosol.coherence.reporter, \ + com.tangosol.coherence.reporter.extractor, \ + com.tangosol.coherence.reporter.locator, \ + com.tangosol.dev.assembler, \ + com.tangosol.dev.compiler, \ + com.tangosol.dev.compiler.java, \ + com.tangosol.dev.component, \ + com.tangosol.dev.packager, \ + com.tangosol.dev.tools, \ + com.tangosol.engarde, \ + com.tangosol.engarde.websphere, \ + com.tangosol.io, \ + com.tangosol.io.bdb, \ + com.tangosol.io.lh, \ + com.tangosol.io.nio, \ + com.tangosol.io.pof, \ + com.tangosol.java.type, \ + com.tangosol.license, \ + com.tangosol.net, \ + com.tangosol.net.cache, \ + com.tangosol.net.internal, \ + com.tangosol.net.management, \ + com.tangosol.net.management.jmx, \ + com.tangosol.net.messaging, \ + com.tangosol.net.partition, \ + com.tangosol.net.security, \ + com.tangosol.run.component, \ + com.tangosol.run.jca, \ + com.tangosol.run.sql, \ + com.tangosol.run.xml, \ + com.tangosol.util, \ + com.tangosol.util.aggregator, \ + com.tangosol.util.comparator, \ + com.tangosol.util.extractor, \ + com.tangosol.util.filter, \ + com.tangosol.util.internal, \ + com.tangosol.util.processor, \ + com.tangosol.util.registry, \ + com.tangosol.util.stats, \ + com.tangosol.util.transformer, \ + com.sleepycat.je, \ + com.tangosol.tde.component.dev.service, \ + + +Embed-Dependency: com.tangosol.*;scope=compile|runtime + +Embed-Transitive: true http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/pom.xml ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/pom.xml b/qi4j/extensions/entitystore-coherence/pom.xml new file mode 100644 index 0000000..ded2a8e --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/pom.xml @@ -0,0 +1,45 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.qi4j.sandbox</groupId> + <artifactId>qi4j-sandbox-extensions</artifactId> + <version>0-SNAPSHOT</version> + </parent> + <groupId>org.qi4j.extension</groupId> + <artifactId>qi4j-entitystore-coherence</artifactId> + <name>Qi4j Extension - Entity Store - Coherence</name> + <packaging>jar</packaging> + + <dependencies> + <dependency> + <groupId>org.qi4j.core</groupId> + <artifactId>org.qi4j.core.spi</artifactId> + </dependency> + <dependency> + <groupId>org.qi4j.core</groupId> + <artifactId>org.qi4j.core.bootstrap</artifactId> + </dependency> + <dependency> + <groupId>org.qi4j.library</groupId> + <artifactId>org.qi4j.library.locking</artifactId> + </dependency> + <dependency> + <groupId>com.tangosol</groupId> + <artifactId>coherence</artifactId> + <version>3.5.1</version> + </dependency> + + <!-- For Tests --> + <dependency> + <groupId>org.qi4j.core</groupId> + <artifactId>org.qi4j.core.testsupport</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.qi4j.core</groupId> + <artifactId>org.qi4j.core.runtime</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceConfiguration.java ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceConfiguration.java b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceConfiguration.java new file mode 100644 index 0000000..dbaea64 --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceConfiguration.java @@ -0,0 +1,27 @@ +/* + * Copyright 2009 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.coherence; + +import org.qi4j.api.configuration.ConfigurationComposite; +import org.qi4j.api.property.Property; + +public interface CoherenceConfiguration + extends ConfigurationComposite +{ + Property<String> cacheName(); +} http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreAssembler.java ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreAssembler.java b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreAssembler.java new file mode 100644 index 0000000..1efc3d1 --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreAssembler.java @@ -0,0 +1,46 @@ +/* + * Copyright 2009 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.coherence; + +import org.qi4j.api.common.Visibility; +import org.qi4j.bootstrap.Assembler; +import org.qi4j.bootstrap.AssemblyException; +import org.qi4j.bootstrap.ModuleAssembly; +import org.qi4j.entitystore.memory.MemoryEntityStoreService; +import org.qi4j.spi.uuid.UuidIdentityGeneratorService; + +public class CoherenceEntityStoreAssembler + implements Assembler +{ + private String configurationModule; + + public CoherenceEntityStoreAssembler( String configurationModule ) + { + this.configurationModule = configurationModule; + } + + public void assemble( ModuleAssembly module ) + throws AssemblyException + { + module.addServices( CoherenceEntityStoreService.class, UuidIdentityGeneratorService.class ); + ModuleAssembly config = module.layer().module( configurationModule ); + config.addEntities( CoherenceConfiguration.class ).visibleIn( Visibility.layer ); + config.addServices( MemoryEntityStoreService.class ); + } + +} http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreMixin.java ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreMixin.java b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreMixin.java new file mode 100644 index 0000000..a341d9e --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreMixin.java @@ -0,0 +1,216 @@ +/* + * Copyright 2009 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.coherence; + +import com.tangosol.net.CacheFactory; +import com.tangosol.net.NamedCache; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.util.Iterator; +import java.util.Map; +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.io.Receiver; +import org.qi4j.api.io.Sender; +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; + +public class CoherenceEntityStoreMixin + implements Activatable, MapEntityStore, DatabaseExport, DatabaseImport +{ + @This + private ReadWriteLock lock; + + @This + private Configuration<CoherenceConfiguration> config; + + @Uses + private ServiceDescriptor descriptor; + + private NamedCache cache; + + // Activatable implementation + public void activate() + throws Exception + { + String cacheName = config.configuration().cacheName().get(); + cache = CacheFactory.getCache( cacheName ); + } + + public void passivate() + throws Exception + { + cache.destroy(); + } + + public Reader get( EntityReference entityReference ) + throws EntityStoreException + { + byte[] data = (byte[]) cache.get( entityReference.identity() ); + + if( data == null ) + { + throw new EntityNotFoundException( entityReference ); + } + try + { + return new StringReader( new String( data, "UTF-8" ) ); + } + catch( UnsupportedEncodingException e ) + { + // Can not happen. + throw new InternalError(); + } + } + + 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" ); + cache.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" ); + cache.put( ref.identity(), stateArray ); + } + }; + } + + public void removeEntity( EntityReference ref, EntityType entityType ) + throws EntityNotFoundException + { + cache.remove( 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>() + { + public <ReceiverThrowableType extends Throwable> void transferTo( Output<? super Reader, ReceiverThrowableType> output ) + throws IOException, ReceiverThrowableType + { + output.receiveFrom( + new Sender<Reader, IOException>() + { + public <ReceiverThrowableType extends Throwable> void sendTo( Receiver<? super Reader, ReceiverThrowableType> receiver ) + throws ReceiverThrowableType, IOException + { + Iterator<Map.Entry<String, byte[]>> list = cache.entrySet().iterator(); + while( list.hasNext() ) + { + Map.Entry<String, byte[]> entry = list.next(); + byte[] data = entry.getValue(); + receiver.receive( new StringReader( new String( data, "UTF-8" ) ) ); + } + } + } + ); + } + }; + } + + public void exportTo( Writer out ) + throws IOException + { + Iterator<Map.Entry<String, byte[]>> list = cache.entrySet().iterator(); + while( list.hasNext() ) + { + Map.Entry<String, byte[]> entry = list.next(); + byte[] data = entry.getValue(); + String value = new String( data, "UTF-8" ); + out.write( value ); + out.write( '\n' ); + } + } + + public void importFrom( Reader in ) + throws IOException + { + BufferedReader reader = new BufferedReader( in ); + String object; + while( ( object = reader.readLine() ) != null ) + { + String id = object.substring( "{\"identity\":\"".length() ); + id = id.substring( 0, id.indexOf( '"' ) ); + byte[] stateArray = object.getBytes( "UTF-8" ); + cache.put( id, stateArray ); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreService.java ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreService.java b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreService.java new file mode 100644 index 0000000..05f9b07 --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreService.java @@ -0,0 +1,39 @@ +/* + * Copyright 2009 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.coherence; + +import org.qi4j.api.concern.Concerns; +import org.qi4j.api.configuration.Configuration; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.service.Activatable; +import org.qi4j.api.service.ServiceComposite; +import org.qi4j.entitystore.map.MapEntityStoreMixin; +import org.qi4j.library.locking.LockingAbstractComposite; +import org.qi4j.spi.entitystore.ConcurrentModificationCheckConcern; +import org.qi4j.spi.entitystore.EntityStateVersions; +import org.qi4j.spi.entitystore.EntityStore; +import org.qi4j.spi.entitystore.StateChangeNotificationConcern; + +@Concerns( { StateChangeNotificationConcern.class, ConcurrentModificationCheckConcern.class } ) +@Mixins( { MapEntityStoreMixin.class, CoherenceEntityStoreMixin.class } ) +public interface CoherenceEntityStoreService + extends EntityStore, EntityStateVersions, DatabaseExport, DatabaseImport, ServiceComposite, Activatable, LockingAbstractComposite, Configuration +{ +} + http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/DatabaseExport.java ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/DatabaseExport.java b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/DatabaseExport.java new file mode 100644 index 0000000..64025e5 --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/DatabaseExport.java @@ -0,0 +1,34 @@ +/* + * Copyright 2009 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.coherence; + +import java.io.IOException; +import java.io.Writer; + +public interface DatabaseExport +{ + /** + * Export data to the writer, with one line per object, in JSON format. + * + * @param out the destination to write the data to. + * @throws java.io.IOException if problems in the underlying stream. + */ + void exportTo( Writer out ) + throws IOException; +} http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/DatabaseImport.java ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/DatabaseImport.java b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/DatabaseImport.java new file mode 100644 index 0000000..2f95ab9 --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/src/main/java/org/qi4j/entitystore/coherence/DatabaseImport.java @@ -0,0 +1,34 @@ +/* + * Copyright 2009 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.coherence; + +import java.io.IOException; +import java.io.Reader; + +public interface DatabaseImport +{ + /** + * Import data from the Reader, with one line per object, in JSON format. + * + * @param in The source of the data to be imported. + * @throws java.io.IOException if problems in the underlying stream. + */ + void importFrom( Reader in ) + throws IOException; +} http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/src/test/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreTest.java ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/src/test/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreTest.java b/qi4j/extensions/entitystore-coherence/src/test/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreTest.java new file mode 100644 index 0000000..83334a3 --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/src/test/java/org/qi4j/entitystore/coherence/CoherenceEntityStoreTest.java @@ -0,0 +1,66 @@ +/* Copyright 2008 Rickard Ãberg. + * + * 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.coherence; + +import org.junit.After; +import org.junit.Test; +import org.qi4j.api.common.Visibility; +import org.qi4j.api.unitofwork.UnitOfWorkCompletionException; +import org.qi4j.bootstrap.AssemblyException; +import org.qi4j.bootstrap.ModuleAssembly; +import org.qi4j.entitystore.memory.MemoryEntityStoreService; +import org.qi4j.spi.uuid.UuidIdentityGeneratorService; +import org.qi4j.test.entity.AbstractEntityStoreTest; + +/** + * JAVADOC + */ +public class CoherenceEntityStoreTest extends AbstractEntityStoreTest +{ + public void assemble( ModuleAssembly module ) throws AssemblyException + { + super.assemble( module ); + module.addServices( CoherenceEntityStoreService.class, UuidIdentityGeneratorService.class ); + + ModuleAssembly config = module.layer().module( "config" ); + config.addEntities( CoherenceConfiguration.class ).visibleIn( Visibility.layer ); + config.addServices( MemoryEntityStoreService.class ); + } + + @Test + @Override public void whenRemovedEntityThenCannotFindEntity() throws Exception + { + super.whenRemovedEntityThenCannotFindEntity(); + } + + @Test + @Override public void givenPropertyIsModifiedWhenUnitOfWorkCompletesThenStoreState() throws UnitOfWorkCompletionException + { + super.givenPropertyIsModifiedWhenUnitOfWorkCompletesThenStoreState(); + } + + @Test + @Override public void givenEntityIsNotModifiedWhenUnitOfWorkCompletesThenDontStoreState() throws UnitOfWorkCompletionException + { + super.givenEntityIsNotModifiedWhenUnitOfWorkCompletesThenDontStoreState(); + } + + @Override @After public void tearDown() throws Exception + { + super.tearDown(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-coherence/src/test/resources/org/qi4j/entitystore/coherence/CoherenceEntityStoreService.properties ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-coherence/src/test/resources/org/qi4j/entitystore/coherence/CoherenceEntityStoreService.properties b/qi4j/extensions/entitystore-coherence/src/test/resources/org/qi4j/entitystore/coherence/CoherenceEntityStoreService.properties new file mode 100644 index 0000000..9751855 --- /dev/null +++ b/qi4j/extensions/entitystore-coherence/src/test/resources/org/qi4j/entitystore/coherence/CoherenceEntityStoreService.properties @@ -0,0 +1,4 @@ + +# The cache that will contain the data. +cacheName=qi4j-default-store + http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-jgroups/LICENSE ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-jgroups/LICENSE b/qi4j/extensions/entitystore-jgroups/LICENSE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/qi4j/extensions/entitystore-jgroups/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-jgroups/NOTICE ---------------------------------------------------------------------- diff --git a/qi4j/extensions/entitystore-jgroups/NOTICE b/qi4j/extensions/entitystore-jgroups/NOTICE new file mode 100644 index 0000000..2b93893 --- /dev/null +++ b/qi4j/extensions/entitystore-jgroups/NOTICE @@ -0,0 +1,23 @@ +Qi4j jGroups Persistence Extension +Copyright 2007-2008, The Qi4j Development Team of individuals. + +See http://www.qi4j.org/contributors.html for list of of individuals. +Also see each file for additional information of Copyright claims. + +Qi4j is a community aggregated works under Copyright law. +All parts of the original works at Qi4j is licensed under the +Apache License ver 2.0 http://www.apache.org/licenses + +Below follows a list of binary dependencies and their licenses; +---------------------------------------------------------------- + +This module uses JGroups, software developed at http://www.jgroups.org +and by JBoss Inc. +NOTE: JGroups is licensed under LGPL. See licenses/jgroups.license + +This module uses Apache Commons Logging, software developed at The Apache +Software Foundation (http://www.apache.org/). +See licenses/commons-logging.license + + +END OF NOTICE \ No newline at end of file
