You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by wi...@apache.org on 2013/02/19 13:52:00 UTC
[35/52] [partial] code contribution,
initial import of relevant modules of LMF-3.0.0-SNAPSHOT based on
revision 4bf944319368 of the default branch at https://code.google.com/p/lmf/
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-ehcache/src/main/resources/ehcache-ldcache.xml
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-ehcache/src/main/resources/ehcache-ldcache.xml b/ldcache/ldcache-backend-ehcache/src/main/resources/ehcache-ldcache.xml
new file mode 100644
index 0000000..8a2a883
--- /dev/null
+++ b/ldcache/ldcache-backend-ehcache/src/main/resources/ehcache-ldcache.xml
@@ -0,0 +1,740 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright (C) 2013 Salzburg Research.
+
+ 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.
+
+-->
+<!--
+CacheManager Configuration
+==========================
+An ehcache.xml corresponds to a single CacheManager.
+
+See instructions below or the ehcache schema (ehcache.xsd) on how to configure.
+
+System property tokens can be specified in this file which are replaced when the configuration
+is loaded. For example multicastGroupPort=${multicastGroupPort} can be replaced with the
+System property either from an environment variable or a system property specified with a
+command line switch such as -DmulticastGroupPort=4446. Another example, useful for Terracotta
+server based deployments is <terracottaConfig url="${serverAndPort}"/ and specify a command line
+switch of -Dserver36:9510
+
+The attributes of <ehcache> are:
+* name - an optional name for the CacheManager. The name is optional and primarily used
+for documentation or to distinguish Terracotta clustered cache state. With Terracotta
+clustered caches, a combination of CacheManager name and cache name uniquely identify a
+particular cache store in the Terracotta clustered memory.
+* updateCheck - an optional boolean flag specifying whether this CacheManager should check
+for new versions of Ehcache over the Internet. If not specified, updateCheck="true".
+* dynamicConfig - an optional setting that can be used to disable dynamic configuration of caches
+associated with this CacheManager. By default this is set to true - i.e. dynamic configuration
+is enabled. Dynamically configurable caches can have their TTI, TTL and maximum disk and
+in-memory capacity changed at runtime through the cache's configuration object.
+* monitoring - an optional setting that determines whether the CacheManager should
+automatically register the SampledCacheMBean with the system MBean server.
+
+Currently, this monitoring is only useful when using Terracotta clustering and using the
+Terracotta Developer Console. With the "autodetect" value, the presence of Terracotta clustering
+will be detected and monitoring, via the Developer Console, will be enabled. Other allowed values
+are "on" and "off". The default is "autodetect". This setting does not perform any function when
+used with JMX monitors.
+
+* maxBytesLocalHeap - optional setting that constraints the memory usage of the Caches managed by the CacheManager
+to use at most the specified number of bytes of the local VM's heap.
+* maxBytesLocalOffHeap - optional setting that constraints the offHeap usage of the Caches managed by the CacheManager
+to use at most the specified number of bytes of the local VM's offHeap memory.
+* maxBytesLocalDisk - optional setting that constraints the disk usage of the Caches managed by the CacheManager
+to use at most the specified number of bytes of the local disk.
+
+These settings let you define "resource pools", caches will share. For instance setting maxBytesLocalHeap to 100M, will result in
+all caches sharing 100 MegaBytes of ram. The CacheManager will balance these 100 MB across all caches based on their respective usage
+patterns. You can allocate a precise amount of bytes to a particular cache by setting the appropriate maxBytes* attribute for that cache.
+That amount will be subtracted from the CacheManager pools, so that if a cache a specified 30M requirement, the other caches will share
+the remaining 70M.
+
+Also, specifying a maxBytesLocalOffHeap at the CacheManager level will result in overflowToOffHeap to be true by default. If you don't want
+a specific cache to overflow to off heap, you'll have to set overflowToOffHeap="false" explicitly
+
+Here is an example of CacheManager level resource tuning, which will use up to 400M of heap and 2G of offHeap:
+
+<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="ehcache.xsd"
+ updateCheck="true" monitoring="autodetect"
+ dynamicConfig="true" maxBytesLocalHeap="400M" maxBytesLocalOffHeap="2G">
+
+-->
+<ehcache name="LDCache"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
+ updateCheck="true" monitoring="autodetect"
+ dynamicConfig="true">
+
+
+
+ <!--
+ DiskStore configuration
+ =======================
+
+ The diskStore element is optional. To turn off disk store path creation, comment out the diskStore
+ element below.
+
+ Configure it if you have disk persistence enabled for any cache or if you use
+ unclustered indexed search.
+
+ If it is not configured, and a cache is created which requires a disk store, a warning will be
+ issued and java.io.tmpdir will automatically be used.
+
+ diskStore has only one attribute - "path". It is the path to the directory where
+ any required disk files will be created.
+
+ If the path is one of the following Java System Property it is replaced by its value in the
+ running VM. For backward compatibility these should be specified without being enclosed in the ${token}
+ replacement syntax.
+
+ The following properties are translated:
+ * user.home - User's home directory
+ * user.dir - User's current working directory
+ * java.io.tmpdir - Default temp file path
+ * ehcache.disk.store.dir - A system property you would normally specify on the command line
+ e.g. java -Dehcache.disk.store.dir=/u01/myapp/diskdir ...
+
+ Subdirectories can be specified below the property e.g. java.io.tmpdir/one
+
+ -->
+ <diskStore path="java.io.tmpdir"/>
+
+
+ <!--
+ Cache configuration
+ ===================
+
+ The following attributes are required.
+
+ name:
+ Sets the name of the cache. This is used to identify the cache. It must be unique.
+
+ maxEntriesLocalHeap:
+ Sets the maximum number of objects that will be created in memory. 0 = no limit.
+ In practice no limit means Integer.MAX_SIZE (2147483647) unless the cache is distributed
+ with a Terracotta server in which case it is limited by resources.
+
+ maxEntriesLocalDisk:
+ Sets the maximum number of objects that will be maintained in the DiskStore
+ The default value is zero, meaning unlimited.
+
+ eternal:
+ Sets whether elements are eternal. If eternal, timeouts are ignored and the
+ element is never expired.
+
+ The following attributes and elements are optional.
+
+ overflowToOffHeap:
+ (boolean) This feature is available only in enterprise versions of Ehcache.
+ When set to true, enables the cache to utilize off-heap memory
+ storage to improve performance. Off-heap memory is not subject to Java
+ GC. The default value is false.
+
+ maxBytesLocalHeap:
+ Defines how many bytes the cache may use from the VM's heap. If a CacheManager
+ maxBytesLocalHeap has been defined, this Cache's specified amount will be
+ subtracted from the CacheManager. Other caches will share the remainder.
+ This attribute's values are given as <number>k|K|m|M|g|G for
+ kilobytes (k|K), megabytes (m|M), or gigabytes (g|G).
+ For example, maxBytesLocalHeap="2g" allots 2 gigabytes of heap memory.
+ If you specify a maxBytesLocalHeap, you can't use the maxEntriesLocalHeap attribute.
+ maxEntriesLocalHeap can't be used if a CacheManager maxBytesLocalHeap is set.
+
+ Elements put into the cache will be measured in size using net.sf.ehcache.pool.sizeof.SizeOf
+ If you wish to ignore some part of the object graph, see net.sf.ehcache.pool.sizeof.annotations.IgnoreSizeOf
+
+ maxBytesLocalOffHeap:
+ This feature is available only in enterprise versions of Ehcache.
+ Sets the amount of off-heap memory this cache can use, and will reserve.
+
+ This setting will set overflowToOffHeap to true. Set explicitly to false to disable overflow behavior.
+
+ Note that it is recommended to set maxEntriesLocalHeap to at least 100 elements
+ when using an off-heap store, otherwise performance will be seriously degraded,
+ and a warning will be logged.
+
+ The minimum amount that can be allocated is 128MB. There is no maximum.
+
+ maxBytesLocalDisk:
+ As for maxBytesLocalHeap, but specifies the limit of disk storage this cache will ever use.
+
+ timeToIdleSeconds:
+ Sets the time to idle for an element before it expires.
+ i.e. The maximum amount of time between accesses before an element expires
+ Is only used if the element is not eternal.
+ Optional attribute. A value of 0 means that an Element can idle for infinity.
+ The default value is 0.
+
+ timeToLiveSeconds:
+ Sets the time to live for an element before it expires.
+ i.e. The maximum time between creation time and when an element expires.
+ Is only used if the element is not eternal.
+ Optional attribute. A value of 0 means that and Element can live for infinity.
+ The default value is 0.
+
+ diskExpiryThreadIntervalSeconds:
+ The number of seconds between runs of the disk expiry thread. The default value
+ is 120 seconds.
+
+ diskSpoolBufferSizeMB:
+ This is the size to allocate the DiskStore for a spool buffer. Writes are made
+ to this area and then asynchronously written to disk. The default size is 30MB.
+ Each spool buffer is used only by its cache. If you get OutOfMemory errors consider
+ lowering this value. To improve DiskStore performance consider increasing it. Trace level
+ logging in the DiskStore will show if put back ups are occurring.
+
+ clearOnFlush:
+ whether the MemoryStore should be cleared when flush() is called on the cache.
+ By default, this is true i.e. the MemoryStore is cleared.
+
+ statistics:
+ Whether to collect statistics. Note that this should be turned on if you are using
+ the Ehcache Monitor. By default statistics is turned off to favour raw performance.
+ To enable set statistics="true"
+
+ memoryStoreEvictionPolicy:
+ Policy would be enforced upon reaching the maxEntriesLocalHeap limit. Default
+ policy is Least Recently Used (specified as LRU). Other policies available -
+ First In First Out (specified as FIFO) and Less Frequently Used
+ (specified as LFU)
+
+ copyOnRead:
+ Whether an Element is copied when being read from a cache.
+ By default this is false.
+
+ copyOnWrite:
+ Whether an Element is copied when being added to the cache.
+ By default this is false.
+
+ Cache persistence is configured through the persistence sub-element. The attributes of the
+ persistence element are:
+
+ strategy:
+ Configures the type of persistence provided by the configured cache. This must be one of the
+ following values:
+
+ * localRestartable - Enables the RestartStore and copies all cache entries (on-heap and/or off-heap)
+ to disk. This option provides fast restartability with fault tolerant cache persistence on disk.
+ It is available for Enterprise Ehcache users only.
+
+ * localTempSwap - Swaps cache entries (on-heap and/or off-heap) to disk when the cache is full.
+ "localTempSwap" is not persistent.
+
+ * none - Does not persist cache entries.
+
+ * distributed - Defers to the <terracotta> configuration for persistence settings. This option
+ is not applicable for standalone.
+
+ synchronousWrites:
+ When set to true write operations on the cache do not return until after the operations data has been
+ successfully flushed to the disk storage. This option is only valid when used with the "localRestartable"
+ strategy, and defaults to false.
+
+ The following example configuration shows a cache configured for localTempSwap restartability.
+
+ <cache name="persistentCache" maxEntriesLocalHeap="1000">
+ <persistence strategy="localTempSwap"/>
+ </cache>
+
+ Cache elements can also contain sub elements which take the same format of a factory class
+ and properties. Defined sub-elements are:
+
+ * cacheEventListenerFactory - Enables registration of listeners for cache events, such as
+ put, remove, update, and expire.
+
+ * bootstrapCacheLoaderFactory - Specifies a BootstrapCacheLoader, which is called by a
+ cache on initialisation to prepopulate itself.
+
+ * cacheExtensionFactory - Specifies a CacheExtension, a generic mechanism to tie a class
+ which holds a reference to a cache to the cache lifecycle.
+
+ * cacheExceptionHandlerFactory - Specifies a CacheExceptionHandler, which is called when
+ cache exceptions occur.
+
+ * cacheLoaderFactory - Specifies a CacheLoader, which can be used both asynchronously and
+ synchronously to load objects into a cache. More than one cacheLoaderFactory element
+ can be added, in which case the loaders form a chain which are executed in order. If a
+ loader returns null, the next in chain is called.
+
+ * copyStrategy - Specifies a fully qualified class which implements
+ net.sf.ehcache.store.compound.CopyStrategy. This strategy will be used for copyOnRead
+ and copyOnWrite in place of the default which is serialization.
+
+ Example of cache level resource tuning:
+ <cache name="memBound" maxBytesLocalHeap="100m" maxBytesLocalOffHeap="4g" maxBytesLocalDisk="200g" />
+
+
+ Cache Event Listeners
+ +++++++++++++++++++++
+
+ All cacheEventListenerFactory elements can take an optional property listenFor that describes
+ which events will be delivered in a clustered environment. The listenFor attribute has the
+ following allowed values:
+
+ * all - the default is to deliver all local and remote events
+ * local - deliver only events originating in the current node
+ * remote - deliver only events originating in other nodes
+
+ Example of setting up a logging listener for local cache events:
+
+ <cacheEventListenerFactory class="my.company.log.CacheLogger"
+ listenFor="local" />
+
+
+ Search
+ ++++++
+
+ A <cache> can be made searchable by adding a <searchable/> sub-element. By default the keys
+ and value objects of elements put into the cache will be attributes against which
+ queries can be expressed.
+
+ <cache>
+ <searchable/>
+ </cache>
+
+
+ An "attribute" of the cache elements can also be defined to be searchable. In the example below
+ an attribute with the name "age" will be available for use in queries. The value for the "age"
+ attribute will be computed by calling the method "getAge()" on the value object of each element
+ in the cache. See net.sf.ehcache.search.attribute.ReflectionAttributeExtractor for the format of
+ attribute expressions. Attribute values must also conform to the set of types documented in the
+ net.sf.ehcache.search.attribute.AttributeExtractor interface
+
+ <cache>
+ <searchable>
+ <searchAttribute name="age" expression="value.getAge()"/>
+ </searchable>
+ </cache>
+
+
+ Attributes may also be defined using a JavaBean style. With the following attribute declaration
+ a public method getAge() will be expected to be found on either the key or value for cache elements
+
+ <cache>
+ <searchable>
+ <searchAttribute name="age"/>
+ </searchable>
+ </cache>
+
+ In more complex situations you can create your own attribute extractor by implementing the
+ AttributeExtractor interface. Providing your extractor class is shown in the following example:
+
+ <cache>
+ <searchable>
+ <searchAttribute name="age" class="com.example.MyAttributeExtractor"/>
+ </searchable>
+ </cache>
+
+ Use properties to pass state to your attribute extractor if needed. Your implementation must provide
+ a public constructor that takes a single java.util.Properties instance
+
+ <cache>
+ <searchable>
+ <searchAttribute name="age" class="com.example.MyAttributeExtractor" properties="foo=1,bar=2"/>
+ </searchable>
+ </cache>
+
+
+ RMI Cache Replication
+ +++++++++++++++++++++
+
+ Each cache that will be distributed needs to set a cache event listener which replicates
+ messages to the other CacheManager peers. For the built-in RMI implementation this is done
+ by adding a cacheEventListenerFactory element of type RMICacheReplicatorFactory to each
+ distributed cache's configuration as per the following example:
+
+ <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+ properties="replicateAsynchronously=true,
+ replicatePuts=true,
+ replicatePutsViaCopy=false,
+ replicateUpdates=true,
+ replicateUpdatesViaCopy=true,
+ replicateRemovals=true,
+ asynchronousReplicationIntervalMillis=<number of milliseconds>,
+ asynchronousReplicationMaximumBatchSize=<number of operations>"
+ propertySeparator="," />
+
+ The RMICacheReplicatorFactory recognises the following properties:
+
+ * replicatePuts=true|false - whether new elements placed in a cache are
+ replicated to others. Defaults to true.
+
+ * replicatePutsViaCopy=true|false - whether the new elements are
+ copied to other caches (true), or whether a remove message is sent. Defaults to true.
+
+ * replicateUpdates=true|false - whether new elements which override an
+ element already existing with the same key are replicated. Defaults to true.
+
+ * replicateRemovals=true - whether element removals are replicated. Defaults to true.
+
+ * replicateAsynchronously=true | false - whether replications are
+ asynchronous (true) or synchronous (false). Defaults to true.
+
+ * replicateUpdatesViaCopy=true | false - whether the new elements are
+ copied to other caches (true), or whether a remove message is sent. Defaults to true.
+
+ * asynchronousReplicationIntervalMillis=<number of milliseconds> - The asynchronous
+ replicator runs at a set interval of milliseconds. The default is 1000. The minimum
+ is 10. This property is only applicable if replicateAsynchronously=true
+
+ * asynchronousReplicationMaximumBatchSize=<number of operations> - The maximum
+ number of operations that will be batch within a single RMI message. The default
+ is 1000. This property is only applicable if replicateAsynchronously=true
+
+ JGroups Replication
+ +++++++++++++++++++
+
+ For the Jgroups replication this is done with:
+ <cacheEventListenerFactory class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
+ properties="replicateAsynchronously=true, replicatePuts=true,
+ replicateUpdates=true, replicateUpdatesViaCopy=false,
+ replicateRemovals=true,asynchronousReplicationIntervalMillis=1000"/>
+ This listener supports the same properties as the RMICacheReplicationFactory.
+
+
+ JMS Replication
+ +++++++++++++++
+
+ For JMS-based replication this is done with:
+ <cacheEventListenerFactory
+ class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory"
+ properties="replicateAsynchronously=true,
+ replicatePuts=true,
+ replicateUpdates=true,
+ replicateUpdatesViaCopy=true,
+ replicateRemovals=true,
+ asynchronousReplicationIntervalMillis=1000"
+ propertySeparator=","/>
+
+ This listener supports the same properties as the RMICacheReplicationFactory.
+
+ Cluster Bootstrapping
+ +++++++++++++++++++++
+
+ Bootstrapping a cluster may use a different mechanism to replication. e.g you can mix
+ JMS replication with bootstrap via RMI - just make sure you have the cacheManagerPeerProviderFactory
+ and cacheManagerPeerListenerFactory configured.
+
+ There are two bootstrapping mechanisms: RMI and JGroups.
+
+ RMI Bootstrap
+
+ The RMIBootstrapCacheLoader bootstraps caches in clusters where RMICacheReplicators are
+ used. It is configured as per the following example:
+
+ <bootstrapCacheLoaderFactory
+ class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+ properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"
+ propertySeparator="," />
+
+ The RMIBootstrapCacheLoaderFactory recognises the following optional properties:
+
+ * bootstrapAsynchronously=true|false - whether the bootstrap happens in the background
+ after the cache has started. If false, bootstrapping must complete before the cache is
+ made available. The default value is true.
+
+ * maximumChunkSizeBytes=<integer> - Caches can potentially be very large, larger than the
+ memory limits of the VM. This property allows the bootstraper to fetched elements in
+ chunks. The default chunk size is 5000000 (5MB).
+
+ JGroups Bootstrap
+
+ Here is an example of bootstrap configuration using JGroups boostrap:
+
+ <bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.jgroups.JGroupsBootstrapCacheLoaderFactory"
+ properties="bootstrapAsynchronously=true"/>
+
+ The configuration properties are the same as for RMI above. Note that JGroups bootstrap only supports
+ asynchronous bootstrap mode.
+
+
+ Cache Exception Handling
+ ++++++++++++++++++++++++
+
+ By default, most cache operations will propagate a runtime CacheException on failure. An
+ interceptor, using a dynamic proxy, may be configured so that a CacheExceptionHandler can
+ be configured to intercept Exceptions. Errors are not intercepted.
+
+ It is configured as per the following example:
+
+ <cacheExceptionHandlerFactory class="com.example.ExampleExceptionHandlerFactory"
+ properties="logLevel=FINE"/>
+
+ Caches with ExceptionHandling configured are not of type Cache, but are of type Ehcache only,
+ and are not available using CacheManager.getCache(), but using CacheManager.getEhcache().
+
+
+ Cache Loader
+ ++++++++++++
+
+ A default CacheLoader may be set which loads objects into the cache through asynchronous and
+ synchronous methods on Cache. This is different to the bootstrap cache loader, which is used
+ only in distributed caching.
+
+ It is configured as per the following example:
+
+ <cacheLoaderFactory class="com.example.ExampleCacheLoaderFactory"
+ properties="type=int,startCounter=10"/>
+
+ Element value comparator
+ ++++++++++++++++++++++++
+
+ These two cache atomic methods:
+ removeElement(Element e)
+ replace(Element old, Element element)
+
+ rely on comparison of cached elements value. The default implementation relies on Object.equals()
+ but that can be changed in case you want to use a different way to compute equality of two elements.
+
+ This is configured as per the following example:
+
+ <elementValueComparator class="com.company.xyz.MyElementComparator"/>
+
+ The MyElementComparator class must implement the is net.sf.ehcache.store.ElementValueComparator
+ interface. The default implementation is net.sf.ehcache.store.DefaultElementValueComparator.
+
+
+ SizeOf Policy
+ +++++++++++++
+
+ Control how deep the SizeOf engine can go when sizing on-heap elements.
+
+ This is configured as per the following example:
+
+ <sizeOfPolicy maxDepth="100" maxDepthExceededBehavior="abort"/>
+
+ maxDepth controls how many linked objects can be visited before the SizeOf engine takes any action.
+ maxDepthExceededBehavior specifies what happens when the max depth is exceeded while sizing an object graph.
+ "continue" makes the SizeOf engine log a warning and continue the sizing. This is the default.
+ "abort" makes the SizeOf engine abort the sizing, log a warning and mark the cache as not correctly tracking
+ memory usage. This makes Ehcache.hasAbortedSizeOf() return true when this happens.
+
+ The SizeOf policy can be configured at the cache manager level (directly under <ehcache>) and at
+ the cache level (under <cache> or <defaultCache>). The cache policy always overrides the cache manager
+ one if both are set. This element has no effect on distributed caches.
+
+ Transactions
+ ++++++++++++
+
+ To enable an ehcache as transactions, set the transactionalMode
+
+ transactionalMode="xa" - high performance JTA/XA implementation
+ transactionalMode="xa_strict" - canonically correct JTA/XA implementation
+ transactionMode="local" - high performance local transactions involving caches only
+ transactionalMode="off" - the default, no transactions
+
+ If set, all cache operations will need to be done through transactions.
+
+ To prevent users keeping references on stored elements and modifying them outside of any transaction's control,
+ transactions also require the cache to be configured copyOnRead and copyOnWrite.
+
+ CacheWriter
+ ++++++++++++
+
+ A CacheWriter can be set to write to an underlying resource. Only one CacheWriter can be
+ configured per cache.
+
+ The following is an example of how to configure CacheWriter for write-through:
+
+ <cacheWriter writeMode="write-through" notifyListenersOnException="true">
+ <cacheWriterFactory class="net.sf.ehcache.writer.TestCacheWriterFactory"
+ properties="type=int,startCounter=10"/>
+ </cacheWriter>
+
+ The following is an example of how to configure CacheWriter for write-behind:
+
+ <cacheWriter writeMode="write-behind" minWriteDelay="1" maxWriteDelay="5"
+ rateLimitPerSecond="5" writeCoalescing="true" writeBatching="true" writeBatchSize="1"
+ retryAttempts="2" retryAttemptDelaySeconds="1">
+ <cacheWriterFactory class="net.sf.ehcache.writer.TestCacheWriterFactory"
+ properties="type=int,startCounter=10"/>
+ </cacheWriter>
+
+ The cacheWriter element has the following attributes:
+ * writeMode: the write mode, write-through or write-behind
+
+ These attributes only apply to write-through mode:
+ * notifyListenersOnException: Sets whether to notify listeners when an exception occurs on a writer operation.
+
+ These attributes only apply to write-behind mode:
+ * minWriteDelay: Set the minimum number of seconds to wait before writing behind. If set to a value greater than 0,
+ it permits operations to build up in the queue. This is different from the maximum write delay in that by waiting
+ a minimum amount of time, work is always being built up. If the minimum write delay is set to zero and the
+ CacheWriter performs its work very quickly, the overhead of processing the write behind queue items becomes very
+ noticeable in a cluster since all the operations might be done for individual items instead of for a collection
+ of them.
+ * maxWriteDelay: Set the maximum number of seconds to wait before writing behind. If set to a value greater than 0,
+ it permits operations to build up in the queue to enable effective coalescing and batching optimisations.
+ * writeBatching: Sets whether to batch write operations. If set to true, writeAll and deleteAll will be called on
+ the CacheWriter rather than write and delete being called for each key. Resources such as databases can perform
+ more efficiently if updates are batched, thus reducing load.
+ * writeBatchSize: Sets the number of operations to include in each batch when writeBatching is enabled. If there are
+ less entries in the write-behind queue than the batch size, the queue length size is used.
+ * rateLimitPerSecond: Sets the maximum number of write operations to allow per second when writeBatching is enabled.
+ * writeCoalescing: Sets whether to use write coalescing. If set to true and multiple operations on the same key are
+ present in the write-behind queue, only the latest write is done, as the others are redundant.
+ * retryAttempts: Sets the number of times the operation is retried in the CacheWriter, this happens after the
+ original operation.
+ * retryAttemptDelaySeconds: Sets the number of seconds to wait before retrying an failed operation.
+
+ Cache Extension
+ +++++++++++++++
+
+ CacheExtensions are a general purpose mechanism to allow generic extensions to a Cache.
+ CacheExtensions are tied into the Cache lifecycle.
+
+ CacheExtensions are created using the CacheExtensionFactory which has a
+ <code>createCacheCacheExtension()</code> method which takes as a parameter a
+ Cache and properties. It can thus call back into any public method on Cache, including, of
+ course, the load methods.
+
+ Extensions are added as per the following example:
+
+ <cacheExtensionFactory class="com.example.FileWatchingCacheRefresherExtensionFactory"
+ properties="refreshIntervalMillis=18000, loaderTimeout=3000,
+ flushPeriod=whatever, someOtherProperty=someValue ..."/>
+
+ Cache Decorator Factory
+ +++++++++++++++++++++++
+
+ Cache decorators can be configured directly in ehcache.xml. The decorators will be created and added to the CacheManager.
+ It accepts the name of a concrete class that extends net.sf.ehcache.constructs.CacheDecoratorFactory
+ The properties will be parsed according to the delimiter (default is comma ',') and passed to the concrete factory's
+ <code>createDecoratedEhcache(Ehcache cache, Properties properties)</code> method along with the reference to the owning cache.
+
+ It is configured as per the following example:
+
+ <cacheDecoratorFactory
+ class="com.company.DecoratedCacheFactory"
+ properties="property1=true ..." />
+
+ Distributed Caching with Terracotta
+ +++++++++++++++++++++++++++++++++++
+
+ Distributed Caches connect to a Terracotta Server Array. They are configured with the <terracotta> sub-element.
+
+ The <terracotta> sub-element has the following attributes:
+
+ * clustered=true|false - indicates whether this cache should be clustered (distributed) with Terracotta. By
+ default, if the <terracotta> element is included, clustered=true.
+
+ * valueMode=serialization|identity - the default is serialization
+
+ Indicates whether cache Elements are distributed with serialized copies or whether a single copy
+ in identity mode is distributed.
+
+ The implications of Identity mode should be clearly understood with reference to the Terracotta
+ documentation before use.
+
+ * copyOnRead=true|false - indicates whether cache values are deserialized on every read or if the
+ materialized cache value can be re-used between get() calls. This setting is useful if a cache
+ is being shared by callers with disparate classloaders or to prevent local drift if keys/values
+ are mutated locally without being put back in the cache.
+
+ The default is false.
+
+ Note: This setting is only relevant for caches with valueMode=serialization
+
+ * consistency=strong|eventual - Indicates whether this cache should have strong consistency or eventual
+ consistency. The default is eventual. See the documentation for the meaning of these terms.
+
+ * synchronousWrites=true|false
+
+ Synchronous writes (synchronousWrites="true") maximize data safety by blocking the client thread until
+ the write has been written to the Terracotta Server Array.
+
+ This option is only available with consistency=strong. The default is false.
+
+ * concurrency - the number of segments that will be used by the map underneath the Terracotta Store.
+ Its optional and has default value of 0, which means will use default values based on the internal
+ Map being used underneath the store.
+
+ This value cannot be changed programmatically once a cache is initialized.
+
+ The <terracotta> sub-element also has a <nonstop> sub-element to allow configuration of cache behaviour if a distributed
+ cache operation cannot be completed within a set time or in the event of a clusterOffline message. If this element does not appear, nonstop behavior is off.
+
+ <nonstop> has the following attributes:
+
+ * enabled="true" - defaults to true.
+
+ * timeoutMillis - An SLA setting, so that if a cache operation takes longer than the allowed ms, it will timeout.
+
+ * immediateTimeout="true|false" - What to do on receipt of a ClusterOffline event indicating that communications
+ with the Terracotta Server Array were interrupted.
+
+ <nonstop> has one sub-element, <timeoutBehavior> which has the following attribute:
+
+ * type="noop|exception|localReads" - What to do when a timeout has occurred. Exception is the default.
+
+ Simplest example to indicate clustering:
+ <terracotta/>
+
+ To indicate the cache should not be clustered (or remove the <terracotta> element altogether):
+ <terracotta clustered="false"/>
+
+ To indicate the cache should be clustered using identity mode:
+ <terracotta clustered="true" valueMode="identity"/>
+
+ To indicate the cache should be clustered using "eventual" consistency mode for better performance :
+ <terracotta clustered="true" consistency="eventual"/>
+
+ To indicate the cache should be clustered using synchronous-write locking level:
+ <terracotta clustered="true" synchronousWrites="true"/>
+ -->
+
+ <!--
+ Default Cache configuration. These settings will be applied to caches
+ created programmatically using CacheManager.add(String cacheName).
+ This element is optional, and using CacheManager.add(String cacheName) when
+ its not present will throw CacheException
+
+ The defaultCache has an implicit name "default" which is a reserved cache name.
+ -->
+ <defaultCache
+ maxEntriesLocalHeap="10000"
+ eternal="false"
+ timeToIdleSeconds="120"
+ timeToLiveSeconds="120"
+ diskSpoolBufferSizeMB="30"
+ maxEntriesLocalDisk="10000000"
+ diskExpiryThreadIntervalSeconds="120"
+ memoryStoreEvictionPolicy="LRU"
+ statistics="false">
+ <persistence strategy="localTempSwap"/>
+ </defaultCache>
+
+ <!--
+ Sample caches. Following are some example caches. Remove these before use.
+ -->
+
+ <cache name="ldcache"
+ maxEntriesLocalHeap="10000"
+ maxEntriesLocalDisk="1000"
+ eternal="false"
+ diskSpoolBufferSizeMB="20"
+ timeToIdleSeconds="300"
+ timeToLiveSeconds="600"
+ memoryStoreEvictionPolicy="LFU"
+ transactionalMode="off">
+ <persistence strategy="localTempSwap"/>
+ </cache>
+
+
+
+</ehcache>
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/.classpath
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/.classpath b/ldcache/ldcache-backend-kiwi/.classpath
new file mode 100644
index 0000000..90df7da
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/.project
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/.project b/ldcache/ldcache-backend-kiwi/.project
new file mode 100644
index 0000000..818b3f0
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>ldcache-backend-kiwi</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ </natures>
+</projectDescription>
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/.settings/org.eclipse.jdt.core.prefs
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/.settings/org.eclipse.jdt.core.prefs b/ldcache/ldcache-backend-kiwi/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..60105c1
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/.settings/org.eclipse.m2e.core.prefs
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/.settings/org.eclipse.m2e.core.prefs b/ldcache/ldcache-backend-kiwi/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/pom.xml
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/pom.xml b/ldcache/ldcache-backend-kiwi/pom.xml
new file mode 100644
index 0000000..7127188
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/pom.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (c) 2013 The Apache Software Foundation
+ ~
+ ~ 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.
+ -->
+
+<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/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>at.newmedialab.lmf</groupId>
+ <artifactId>ldcache-parent</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ <relativePath>../</relativePath>
+ </parent>
+
+ <artifactId>ldcache-backend-kiwi</artifactId>
+ <name>LDCache Backend: KiWi</name>
+
+ <description>
+ Linked Data Caching Backend based on the JDBC database used by the KiWi triple store. Caches resources and
+ caching information in the database and triples in the central triple store (using a dedicated context graph).
+ </description>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <h2.url>jdbc:h2:mem:test;MVCC=true;DB_CLOSE_ON_EXIT=TRUE</h2.url>
+ <h2.user>sa</h2.user>
+ <h2.pass />
+
+ <!-- enable or pass on command line for testing local PostgreSQL -->
+ <!--
+ <postgresql.url>jdbc:postgresql://localhost:5433/kiwitest?prepareThreshold=3</postgresql.url>
+ <postgresql.user>lmf</postgresql.user>
+ <postgresql.pass>lmf</postgresql.pass>
+ -->
+
+ <!-- enable or pass on command line for testing local MySQL -->
+ <!--
+ <mysql.url>jdbc:mysql://localhost:3306/kiwitest</mysql.url>
+ <mysql.user>lmf</mysql.user>
+ <mysql.pass>lmf</mysql.pass>
+ -->
+
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+
+
+ <dependencies>
+ <dependency>
+ <groupId>at.newmedialab.lmf</groupId>
+ <artifactId>ldcache-api</artifactId>
+ </dependency>
+
+
+ <!-- Use KiWi Triple Store for caching -->
+ <dependency>
+ <groupId>at.newmedialab.lmf</groupId>
+ <artifactId>kiwi-triplestore</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>at.newmedialab.lmf</groupId>
+ <artifactId>kiwi-contextaware</artifactId>
+ </dependency>
+
+ <!-- Logging -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>log4j-over-slf4j</artifactId>
+ </dependency>
+
+ <!-- Sesame dependencies -->
+ <dependency>
+ <groupId>org.openrdf.sesame</groupId>
+ <artifactId>sesame-model</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.openrdf.sesame</groupId>
+ <artifactId>sesame-repository-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.openrdf.sesame</groupId>
+ <artifactId>sesame-repository-sail</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.openrdf.sesame</groupId>
+ <artifactId>sesame-sail-api</artifactId>
+ </dependency>
+
+
+ <!-- Testing -->
+ <dependency>
+ <artifactId>junit</artifactId>
+ <groupId>junit</groupId>
+ <scope>test</scope>
+ </dependency>
+ <dependency> <!-- see http://www.dbunit.org/howto.html -->
+ <artifactId>dbunit</artifactId>
+ <groupId>org.dbunit</groupId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <artifactId>hamcrest-core</artifactId>
+ <groupId>org.hamcrest</groupId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <artifactId>hamcrest-library</artifactId>
+ <groupId>org.hamcrest</groupId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/LDCachingKiWiBackend.java
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/LDCachingKiWiBackend.java b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/LDCachingKiWiBackend.java
new file mode 100644
index 0000000..19b71ae
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/LDCachingKiWiBackend.java
@@ -0,0 +1,217 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * 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.apache.marmotta.ldcache.backend.kiwi;
+
+import info.aduna.iteration.CloseableIteration;
+import info.aduna.iteration.ExceptionConvertingIteration;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.ldcache.api.LDCachingBackend;
+import org.apache.marmotta.ldcache.api.LDCachingConnection;
+import org.apache.marmotta.ldcache.backend.kiwi.persistence.LDCachingKiWiPersistence;
+import org.apache.marmotta.ldcache.backend.kiwi.repository.LDCachingSailRepositoryConnection;
+import org.apache.marmotta.ldcache.backend.kiwi.sail.LDCachingKiWiSail;
+import org.apache.marmotta.ldcache.backend.kiwi.sail.LDCachingKiWiSailConnection;
+import org.apache.marmotta.ldcache.model.CacheEntry;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.sail.SailException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.SQLException;
+
+/**
+ * Add file description here!
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class LDCachingKiWiBackend implements LDCachingBackend {
+
+ private static Logger log = LoggerFactory.getLogger(LDCachingKiWiBackend.class);
+
+
+
+ /**
+ * URI used as cache context in the central triple store
+ */
+ private String cacheContext;
+
+
+ /**
+ * Direct access to the caching SAIL with its caching maintenance functionality.
+ */
+ private LDCachingKiWiSail sail;
+
+
+ private LDCachingKiWiPersistence persistence;
+
+ /**
+ * Repository API access to the cache data
+ */
+ private SailRepository repository;
+
+ /**
+ * Create a new LDCache KiWi backend using the given store and context for caching triples and storing cache
+ * metadata via JDBC in the database.
+ *
+ * @param store
+ * @param cacheContext
+ */
+ public LDCachingKiWiBackend(KiWiStore store, String cacheContext) {
+ this.cacheContext = cacheContext;
+ this.sail = new LDCachingKiWiSail(store);
+ this.repository = new SailRepository(sail);
+ this.persistence = new LDCachingKiWiPersistence(store.getPersistence());
+ }
+
+ /**
+ * Return a repository connection that can be used for caching. The LDCache will first remove all statements for
+ * the newly cached resources and then add retrieved statements as-is to this connection and properly commit and
+ * close it after use.
+ * <p/>
+ * Note that in case the statements should be rewritten this method must take care of providing the proper
+ * connection, e.g. by using a ContextAwareRepositoryConnection to add a context to all statements when adding them.
+ *
+ *
+ * @param resource the resource that will be cached
+ * @return a repository connection that can be used for storing retrieved triples for caching
+ */
+ @Override
+ public LDCachingConnection getCacheConnection(String resource) throws RepositoryException {
+ try {
+ LDCachingKiWiSailConnection sailConnection = sail.getConnection();
+
+ return new LDCachingSailRepositoryConnection(repository,sailConnection,cacheContext);
+ } catch (SailException e) {
+ throw new RepositoryException(e);
+ }
+ }
+
+ /**
+ * Return an iterator over all expired cache entries (can e.g. be used for refreshing).
+ *
+ * @return
+ */
+ @Override
+ public CloseableIteration<CacheEntry, RepositoryException> listExpiredEntries() throws RepositoryException {
+ try {
+ final LDCachingKiWiSailConnection sailConnection = sail.getConnection();
+ sailConnection.begin();
+
+ return new ExceptionConvertingIteration<CacheEntry, RepositoryException>(sailConnection.listExpired()) {
+ @Override
+ protected RepositoryException convert(Exception e) {
+ return new RepositoryException(e);
+ }
+
+ /**
+ * Closes this Iteration as well as the wrapped Iteration if it happens to be
+ * a {@link info.aduna.iteration.CloseableIteration}.
+ */
+ @Override
+ protected void handleClose() throws RepositoryException {
+ super.handleClose();
+ try {
+ sailConnection.commit();
+ sailConnection.close();
+ } catch (SailException ex) {
+ throw new RepositoryException(ex);
+ }
+ }
+ };
+ } catch (SailException e) {
+ throw new RepositoryException(e);
+ }
+ }
+
+ /**
+ * Return an iterator over all cache entries (can e.g. be used for refreshing or expiring).
+ *
+ * @return
+ */
+ @Override
+ public CloseableIteration<CacheEntry, RepositoryException> listCacheEntries() throws RepositoryException {
+ try {
+ final LDCachingKiWiSailConnection sailConnection = sail.getConnection();
+ sailConnection.begin();
+
+ return new ExceptionConvertingIteration<CacheEntry, RepositoryException>(sailConnection.listAll()) {
+ @Override
+ protected RepositoryException convert(Exception e) {
+ return new RepositoryException(e);
+ }
+
+ /**
+ * Closes this Iteration as well as the wrapped Iteration if it happens to be
+ * a {@link info.aduna.iteration.CloseableIteration}.
+ */
+ @Override
+ protected void handleClose() throws RepositoryException {
+ super.handleClose();
+ try {
+ sailConnection.commit();
+ sailConnection.close();
+ } catch (SailException ex) {
+ throw new RepositoryException(ex);
+ }
+ }
+ };
+ } catch (SailException e) {
+ throw new RepositoryException(e);
+ }
+ }
+
+
+ public LDCachingKiWiPersistence getPersistence() {
+ return persistence;
+ }
+
+ /**
+ * Carry out any initialization tasks that might be necessary
+ */
+ @Override
+ public void initialize() {
+ try {
+ repository.initialize();
+ } catch (RepositoryException e) {
+ log.error("error initializing secondary repository",e);
+ }
+
+ try {
+ persistence.initDatabase();
+ } catch (SQLException e) {
+ log.error("error initializing LDCache database tables",e);
+ }
+
+ // register cache context in database
+ repository.getValueFactory().createURI(cacheContext);
+
+ }
+
+ /**
+ * Shutdown the backend and free all runtime resources.
+ */
+ @Override
+ public void shutdown() {
+ try {
+ repository.shutDown();
+ } catch (RepositoryException e) {
+ log.error("error shutting down secondary repository",e);
+ }
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/model/KiWiCacheEntry.java
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/model/KiWiCacheEntry.java b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/model/KiWiCacheEntry.java
new file mode 100644
index 0000000..519d721
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/model/KiWiCacheEntry.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * 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.apache.marmotta.ldcache.backend.kiwi.model;
+
+import org.apache.marmotta.ldcache.model.CacheEntry;
+
+/**
+ * Add file description here!
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class KiWiCacheEntry extends CacheEntry {
+
+ Long id;
+
+ public KiWiCacheEntry() {
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/persistence/LDCachingKiWiPersistence.java
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/persistence/LDCachingKiWiPersistence.java b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/persistence/LDCachingKiWiPersistence.java
new file mode 100644
index 0000000..2dcd991
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/persistence/LDCachingKiWiPersistence.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * 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.apache.marmotta.ldcache.backend.kiwi.persistence;
+
+import org.apache.marmotta.kiwi.persistence.KiWiDialect;
+import org.apache.marmotta.kiwi.persistence.KiWiPersistence;
+
+import java.sql.SQLException;
+
+/**
+ * A KiWi persistence wrapper for storing caching information in the database used by the KiWi triple store
+ * wrapped by the persistence.
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class LDCachingKiWiPersistence {
+
+ /**
+ * Get the parent persistence service to access the database
+ */
+ private KiWiPersistence persistence;
+
+
+ public LDCachingKiWiPersistence(KiWiPersistence persistence) {
+ this.persistence = persistence;
+
+ persistence.addNodeTableDependency("ldcache_entries","resource_id");
+ }
+
+ /**
+ * Initialise the database, creating or upgrading tables if they do not exist or are of the wrong version.
+ * This method must only be called after the initDatabase of the wrapped KiWiPersistence has been evaluated.
+ */
+ public void initDatabase() throws SQLException {
+ persistence.initDatabase("ldcache", new String[] {"ldcache_entries"});
+ }
+
+ /**
+ * Drop the versioning tables; this method must be called before the dropDatabase method of the underlying
+ * KiWiPersistence is called.
+ *
+ * @throws SQLException
+ */
+ public void dropDatabase() throws SQLException {
+ persistence.dropDatabase("ldcache");
+ }
+
+ /**
+ * Return a connection from the connection pool which already has the auto-commit disabled.
+ *
+ * @return a fresh JDBC connection from the connection pool
+ * @throws java.sql.SQLException in case a new connection could not be established
+ */
+ public LDCachingKiWiPersistenceConnection getConnection() throws SQLException {
+ return new LDCachingKiWiPersistenceConnection(persistence.getConnection());
+ }
+
+
+ public KiWiDialect getDialect() {
+ return persistence.getDialect();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/persistence/LDCachingKiWiPersistenceConnection.java
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/persistence/LDCachingKiWiPersistenceConnection.java b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/persistence/LDCachingKiWiPersistenceConnection.java
new file mode 100644
index 0000000..4a243de
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/persistence/LDCachingKiWiPersistenceConnection.java
@@ -0,0 +1,352 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * 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.apache.marmotta.ldcache.backend.kiwi.persistence;
+
+import info.aduna.iteration.CloseableIteration;
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+import org.apache.marmotta.kiwi.model.rdf.KiWiNode;
+import org.apache.marmotta.kiwi.model.rdf.KiWiResource;
+import org.apache.marmotta.kiwi.persistence.KiWiConnection;
+import org.apache.marmotta.kiwi.persistence.util.ResultSetIteration;
+import org.apache.marmotta.kiwi.persistence.util.ResultTransformerFunction;
+import org.apache.marmotta.ldcache.backend.kiwi.model.KiWiCacheEntry;
+import org.apache.marmotta.ldcache.model.CacheEntry;
+import org.openrdf.model.URI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * Add file description here!
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class LDCachingKiWiPersistenceConnection {
+
+ private static Logger log = LoggerFactory.getLogger(LDCachingKiWiPersistenceConnection.class);
+
+
+ private KiWiConnection connection;
+
+ /**
+ * Cache entries by resource
+ */
+ private Cache entryResourceCache;
+
+
+ /**
+ * Cache entries by ID
+ */
+ private Cache entryIdCache;
+
+
+ public LDCachingKiWiPersistenceConnection(KiWiConnection connection) throws SQLException {
+ this.connection = connection;
+
+ entryResourceCache = connection.getCacheManager().getCacheByName("ldcache-entry-uri");
+ entryIdCache = connection.getCacheManager().getCacheByName("ldcache-entry-id");
+ }
+
+ public KiWiCacheEntry constructCacheEntry(ResultSet row) throws SQLException {
+ Long id = row.getLong("id");
+
+ Element cached = entryIdCache.get(id);
+
+ // lookup element in cache first, so we can avoid reconstructing it if it is already there
+ if(cached != null) {
+ return (KiWiCacheEntry)cached.getObjectValue();
+ }
+
+ KiWiCacheEntry entry = new KiWiCacheEntry();
+ entry.setId(id);
+ entry.setLastRetrieved(new Date(row.getTimestamp("retrieved_at").getTime()));
+ entry.setExpiryDate(new Date(row.getTimestamp("expires_at").getTime()));
+ entry.setUpdateCount(row.getInt("update_count"));
+ entry.setResource((URI) connection.loadNodeById(row.getLong("resource_id")));
+
+ entryIdCache.put(new Element(id,entry));
+ entryResourceCache.put(new Element(entry.getResource().stringValue(),entry));
+
+ return entry;
+ }
+
+ /**
+ * Load the cache entry for the given URI from the database.
+ *
+ * @param uri the URI of the cached resource for which to return the cache entry
+ * @return an instance of KiWiCacheEntry representing the caching metadata for the given resource, or null in case there
+ * is no entry for this resource
+ * @throws SQLException
+ */
+ public KiWiCacheEntry getCacheEntry(String uri) throws SQLException {
+
+ Element cached = entryResourceCache.get(uri);
+
+ // lookup element in cache first, so we can avoid reconstructing it if it is already there
+ if(cached != null) {
+ return (KiWiCacheEntry)cached.getObjectValue();
+ }
+
+ PreparedStatement query = connection.getPreparedStatement("load.entry_by_uri");
+ query.setString(1, uri);
+ query.setMaxRows(1);
+
+ // run the database query and if it yields a result, construct a new node; the method call will take care of
+ // caching the constructed node for future calls
+ ResultSet result = query.executeQuery();
+ try {
+ if(result.next()) {
+ return constructCacheEntry(result);
+ } else {
+ return null;
+ }
+ } finally {
+ result.close();
+ }
+ }
+
+ /**
+ * Store the cache entry passed as argument in the database. In case the passed argument is not an instance of
+ * KiWiCacheEntry, it will first be converted into a KiWiCacheEntry by copying the fields. In this case, the
+ * stored object will not be the same instance as the object passed as argument.
+ *
+ * @param entry the cache entry to store
+ * @throws SQLException
+ */
+ public void storeCacheEntry(CacheEntry entry) throws SQLException {
+ KiWiCacheEntry kEntry;
+ if(entry instanceof KiWiCacheEntry) {
+ kEntry = (KiWiCacheEntry) entry;
+ } else {
+ kEntry = new KiWiCacheEntry();
+ kEntry.setExpiryDate(entry.getExpiryDate());
+ kEntry.setLastRetrieved(entry.getLastRetrieved());
+ kEntry.setUpdateCount(entry.getUpdateCount());
+ kEntry.setResource(entry.getResource());
+ }
+
+ if(! (entry.getResource() instanceof KiWiResource) || ((KiWiResource) entry.getResource()).getId() == null) {
+ throw new IllegalStateException("the resource contained in the cache entry is not a KiWiResource!");
+ }
+
+ kEntry.setId(connection.getNextSequence("seq.ldcache"));
+
+ PreparedStatement insertEntry = connection.getPreparedStatement("store.entry");
+ insertEntry.setLong(1, kEntry.getId());
+ insertEntry.setTimestamp(2, new Timestamp(kEntry.getLastRetrieved().getTime()));
+ insertEntry.setTimestamp(3,new Timestamp(kEntry.getExpiryDate().getTime()));
+ insertEntry.setLong(4,((KiWiNode)kEntry.getResource()).getId());
+ insertEntry.setInt(5, kEntry.getUpdateCount());
+ insertEntry.executeUpdate();
+
+ log.debug("persisted ld-cache entry with id {}", kEntry.getId());
+
+ entryIdCache.put(new Element(kEntry.getId(),kEntry));
+ entryResourceCache.put(new Element(kEntry.getResource().stringValue(),kEntry));
+
+ }
+
+ /**
+ * Remove the given cache entry from the database. The cache entry passed as argument must be a persistent instance
+ * of KiWiCacheEntry.
+ * @param entry
+ * @throws SQLException
+ */
+ public void removeCacheEntry(CacheEntry entry) throws SQLException {
+ if(! (entry instanceof KiWiCacheEntry) || ((KiWiCacheEntry) entry).getId() == null) {
+ throw new IllegalStateException("the passed cache entry is not managed by this connection");
+ }
+
+ PreparedStatement deleteEntry = connection.getPreparedStatement("delete.entry");
+ deleteEntry.setLong(1,((KiWiCacheEntry) entry).getId());
+ deleteEntry.executeUpdate();
+
+ entryIdCache.remove(((KiWiCacheEntry) entry).getId());
+ entryResourceCache.remove(entry.getResource().stringValue());
+ }
+
+ /**
+ * Remove the given cache entry from the database. The cache entry passed as argument must be a persistent instance
+ * of KiWiCacheEntry.
+ * @param uri URI of the entry to delete
+ * @throws SQLException
+ */
+ public void removeCacheEntry(String uri) throws SQLException {
+
+ PreparedStatement deleteEntry = connection.getPreparedStatement("delete.entry_by_uri");
+ deleteEntry.setString(1,uri);
+ deleteEntry.executeUpdate();
+
+ Element cached = entryResourceCache.get(uri);
+
+ if(cached != null) {
+ entryResourceCache.remove(uri);
+ entryIdCache.remove(((KiWiCacheEntry) cached.getObjectValue()).getId());
+ }
+ }
+
+
+ /**
+ * List all cache entries with an expiry date older than the current time.
+ *
+ * @return a closeable iteration with KiWiCacheEntries; needs to be released by the caller
+ * @throws SQLException
+ */
+ public CloseableIteration<KiWiCacheEntry,SQLException> listExpired() throws SQLException {
+ PreparedStatement queryExpired = connection.getPreparedStatement("query.entries_expired");
+ final ResultSet result = queryExpired.executeQuery();
+
+ return new ResultSetIteration<KiWiCacheEntry>(result, new ResultTransformerFunction<KiWiCacheEntry>() {
+ @Override
+ public KiWiCacheEntry apply(ResultSet input) throws SQLException {
+ return constructCacheEntry(result);
+ }
+ });
+ }
+
+ /**
+ * List all cache entries in the database, regardless of expiry date.
+ *
+ * @return a closeable iteration with KiWiCacheEntries; needs to be released by the caller
+ * @throws SQLException
+ */
+ public CloseableIteration<KiWiCacheEntry,SQLException> listAll() throws SQLException {
+ PreparedStatement queryExpired = connection.getPreparedStatement("query.entries_all");
+ final ResultSet result = queryExpired.executeQuery();
+
+ return new ResultSetIteration<KiWiCacheEntry>(result, new ResultTransformerFunction<KiWiCacheEntry>() {
+ @Override
+ public KiWiCacheEntry apply(ResultSet input) throws SQLException {
+ return constructCacheEntry(result);
+ }
+ });
+ }
+
+ /**
+ * Makes all changes made since the previous
+ * commit/rollback permanent and releases any database locks
+ * currently held by this <code>Connection</code> object.
+ * This method should be
+ * used only when auto-commit mode has been disabled.
+ *
+ * @exception java.sql.SQLException if a database access error occurs,
+ * this method is called while participating in a distributed transaction,
+ * if this method is called on a closed conection or this
+ * <code>Connection</code> object is in auto-commit mode
+ */
+ public void commit() throws SQLException {
+ connection.commit();
+ }
+
+ /**
+ * Releases this <code>Connection</code> object's database and JDBC resources
+ * immediately instead of waiting for them to be automatically released.
+ * <P>
+ * Calling the method <code>close</code> on a <code>Connection</code>
+ * object that is already closed is a no-op.
+ * <P>
+ * It is <b>strongly recommended</b> that an application explicitly
+ * commits or rolls back an active transaction prior to calling the
+ * <code>close</code> method. If the <code>close</code> method is called
+ * and there is an active transaction, the results are implementation-defined.
+ * <P>
+ *
+ * @exception java.sql.SQLException SQLException if a database access error occurs
+ */
+ public void close() throws SQLException {
+ connection.close();
+ }
+
+ /**
+ * Retrieves whether this <code>Connection</code> object has been
+ * closed. A connection is closed if the method <code>close</code>
+ * has been called on it or if certain fatal errors have occurred.
+ * This method is guaranteed to return <code>true</code> only when
+ * it is called after the method <code>Connection.close</code> has
+ * been called.
+ * <P>
+ * This method generally cannot be called to determine whether a
+ * connection to a database is valid or invalid. A typical client
+ * can determine that a connection is invalid by catching any
+ * exceptions that might be thrown when an operation is attempted.
+ *
+ * @return <code>true</code> if this <code>Connection</code> object
+ * is closed; <code>false</code> if it is still open
+ * @exception java.sql.SQLException if a database access error occurs
+ */
+ public boolean isClosed() throws SQLException {
+ return connection.isClosed();
+ }
+
+ /**
+ * Undoes all changes made in the current transaction
+ * and releases any database locks currently held
+ * by this <code>Connection</code> object. This method should be
+ * used only when auto-commit mode has been disabled.
+ *
+ * @exception java.sql.SQLException if a database access error occurs,
+ * this method is called while participating in a distributed transaction,
+ * this method is called on a closed connection or this
+ * <code>Connection</code> object is in auto-commit mode
+ */
+ public void rollback() throws SQLException {
+ connection.rollback();
+ }
+
+ /**
+ * Store a new node in the database. The method will retrieve a new database id for the node and update the
+ * passed object. Afterwards, the node data will be inserted into the database using appropriate INSERT
+ * statements. The caller must make sure the connection is committed and closed properly.
+ * <p/>
+ * If the node already has an ID, the method will do nothing (assuming that it is already persistent)
+ *
+ * @param node
+ * @throws java.sql.SQLException
+ */
+ public void storeNode(KiWiNode node) throws SQLException {
+ connection.storeNode(node);
+ }
+
+ /**
+ * Return a collection of database tables contained in the database. This query is used for checking whether
+ * the database needs to be created when initialising the system.
+ *
+ *
+ *
+ * @return
+ * @throws java.sql.SQLException
+ */
+ public Set<String> getDatabaseTables() throws SQLException {
+ return connection.getDatabaseTables();
+ }
+
+ /**
+ * Return the KiWi version of the database this connection is operating on. This query is necessary for
+ * checking proper state of a database when initialising the system.
+ *
+ * @return
+ */
+ public int getDatabaseVersion() throws SQLException {
+ return connection.getDatabaseVersion();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/repository/LDCachingSailRepositoryConnection.java
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/repository/LDCachingSailRepositoryConnection.java b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/repository/LDCachingSailRepositoryConnection.java
new file mode 100644
index 0000000..96c6c64
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/repository/LDCachingSailRepositoryConnection.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * 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.apache.marmotta.ldcache.backend.kiwi.repository;
+
+import org.apache.marmotta.kiwi.contextaware.ContextAwareSailConnection;
+import org.apache.marmotta.ldcache.api.LDCachingConnection;
+import org.apache.marmotta.ldcache.backend.kiwi.sail.LDCachingKiWiSailConnection;
+import org.apache.marmotta.ldcache.model.CacheEntry;
+import org.apache.marmotta.ldcache.sail.LDCachingSailConnection;
+import org.openrdf.model.URI;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.repository.sail.SailRepositoryConnection;
+
+/**
+ * This is an extension wrapper around sail repository connections that allows delegating the additional cache entry
+ * methods to the underlying SAIL repository. Otherwise it behaves like any SailRepositoryConnection.
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class LDCachingSailRepositoryConnection extends SailRepositoryConnection implements LDCachingConnection {
+
+ private LDCachingSailConnection cacheConnection;
+
+ public LDCachingSailRepositoryConnection(SailRepository repository, LDCachingKiWiSailConnection sailConnection, String cacheContext) {
+ super(repository, new ContextAwareSailConnection(sailConnection, sailConnection.getValueFactory().createURI(cacheContext)));
+ cacheConnection = sailConnection;
+ }
+
+ /**
+ * Store a cache entry for the passed resource in the backend. Depending on the backend, this can be a
+ * persistent storage or an in-memory storage.
+ *
+ * @param resource
+ * @param entry
+ */
+ @Override
+ public void addCacheEntry(URI resource, CacheEntry entry) throws RepositoryException {
+ try {
+ cacheConnection.addCacheEntry(resource,entry);
+ } catch (org.openrdf.sail.SailException e) {
+ throw new RepositoryException(e);
+ }
+ }
+
+ /**
+ * Get the cache entry for the passed resource, if any. Returns null in case there is no cache entry.
+ *
+ *
+ * @param resource the resource to look for
+ * @return the cache entry for the resource, or null if the resource has never been cached or is expired
+ */
+ @Override
+ public CacheEntry getCacheEntry(URI resource) throws RepositoryException {
+ try {
+ return cacheConnection.getCacheEntry(resource);
+ } catch (org.openrdf.sail.SailException e) {
+ throw new RepositoryException(e);
+ }
+ }
+
+ /**
+ * Remove the currently stored cache entry for the passed resource from the backend.
+ *
+ * @param resource
+ */
+ @Override
+ public void removeCacheEntry(URI resource) throws RepositoryException {
+ try {
+ cacheConnection.removeCacheEntry(resource);
+ } catch (org.openrdf.sail.SailException e) {
+ throw new RepositoryException(e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/sail/LDCachingKiWiSail.java
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/sail/LDCachingKiWiSail.java b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/sail/LDCachingKiWiSail.java
new file mode 100644
index 0000000..eaab2af
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/sail/LDCachingKiWiSail.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * 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.apache.marmotta.ldcache.backend.kiwi.sail;
+
+import org.apache.marmotta.kiwi.sail.KiWiSailConnection;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.helpers.SailWrapper;
+
+/**
+ * Add file description here!
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class LDCachingKiWiSail extends SailWrapper {
+
+ private KiWiStore store;
+
+ /**
+ * Creates a new SailWrapper that wraps the supplied Sail.
+ */
+ public LDCachingKiWiSail(KiWiStore baseSail) {
+ super(baseSail);
+
+ this.store = baseSail;
+ }
+
+ @Override
+ public LDCachingKiWiSailConnection getConnection() throws SailException {
+ return new LDCachingKiWiSailConnection((KiWiSailConnection) store.getConnection());
+ }
+
+ @Override
+ public void initialize() throws SailException {
+ // ignore, because we assume that the wrapped store is already initialized
+ if(!store.isInitialized()) {
+ throw new SailException("the LDCachingKiWiSail is a secondary sail and requires an already initialized store!");
+ }
+ }
+
+ @Override
+ public void shutDown() throws SailException {
+ // ignore, because we assume that the wrapped store will be shutdown by another sail
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/sail/LDCachingKiWiSailConnection.java
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/sail/LDCachingKiWiSailConnection.java b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/sail/LDCachingKiWiSailConnection.java
new file mode 100644
index 0000000..f5a38c2
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/src/main/java/org/apache/marmotta/ldcache/backend/kiwi/sail/LDCachingKiWiSailConnection.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * 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.apache.marmotta.ldcache.backend.kiwi.sail;
+
+import info.aduna.iteration.CloseableIteration;
+import org.apache.marmotta.kiwi.sail.KiWiSailConnection;
+import org.apache.marmotta.kiwi.sail.KiWiValueFactory;
+import org.apache.marmotta.ldcache.backend.kiwi.model.KiWiCacheEntry;
+import org.apache.marmotta.ldcache.backend.kiwi.persistence.LDCachingKiWiPersistenceConnection;
+import org.apache.marmotta.ldcache.model.CacheEntry;
+import org.apache.marmotta.ldcache.sail.LDCachingSailConnection;
+import org.openrdf.model.URI;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.helpers.SailConnectionWrapper;
+
+import java.sql.SQLException;
+
+/**
+ * Add file description here!
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class LDCachingKiWiSailConnection extends SailConnectionWrapper implements LDCachingSailConnection {
+
+ private LDCachingKiWiPersistenceConnection persistence;
+
+ private KiWiSailConnection wrapped;
+
+ public LDCachingKiWiSailConnection(KiWiSailConnection wrappedCon) throws SailException {
+ super(wrappedCon);
+
+ this.wrapped = wrappedCon;
+ try {
+ this.persistence = new LDCachingKiWiPersistenceConnection(wrappedCon.getDatabaseConnection());
+ } catch (SQLException e) {
+ throw new SailException(e);
+ }
+ }
+
+ public KiWiValueFactory getValueFactory() {
+ return wrapped.getValueFactory();
+ }
+
+ /**
+ * Store a cache entry for the passed resource in the backend. Depending on the backend, this can be a
+ * persistent storage or an in-memory storage.
+ *
+ * @param resource
+ * @param entry
+ */
+ @Override
+ public void addCacheEntry(URI resource, CacheEntry entry) throws SailException {
+ try {
+ persistence.storeCacheEntry(entry);
+ } catch (SQLException e) {
+ throw new SailException(e);
+ }
+ }
+
+ /**
+ * Get the cache entry for the passed resource, if any. Returns null in case there is no cache entry.
+ *
+ *
+ * @param resource the resource to look for
+ * @return the cache entry for the resource, or null if the resource has never been cached or is expired
+ */
+ @Override
+ public CacheEntry getCacheEntry(URI resource) throws SailException {
+ try {
+ return persistence.getCacheEntry(resource.stringValue());
+ } catch (SQLException e) {
+ throw new SailException(e);
+ }
+ }
+
+ /**
+ * Remove the currently stored cache entry for the passed resource from the backend.
+ *
+ * @param resource
+ */
+ @Override
+ public void removeCacheEntry(URI resource) throws SailException {
+ try {
+ persistence.removeCacheEntry(resource.stringValue());
+ } catch (SQLException e) {
+ throw new SailException(e);
+ }
+ }
+
+ /**
+ * List all cache entries with an expiry date older than the current time.
+ *
+ * @return a closeable iteration with KiWiCacheEntries; needs to be released by the caller
+ * @throws SQLException
+ */
+ public CloseableIteration<KiWiCacheEntry,SQLException> listExpired() throws SailException {
+ try {
+ return persistence.listExpired();
+ } catch (SQLException e) {
+ throw new SailException(e);
+ }
+ }
+
+
+ /**
+ * List all cache entries in the database, regardless of expiry date.
+ *
+ * @return a closeable iteration with KiWiCacheEntries; needs to be released by the caller
+ * @throws SQLException
+ */
+ public CloseableIteration<KiWiCacheEntry,SQLException> listAll() throws SailException {
+ try {
+ return persistence.listAll();
+ } catch (SQLException e) {
+ throw new SailException(e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_ldcache_tables.sql
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_ldcache_tables.sql b/ldcache/ldcache-backend-kiwi/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_ldcache_tables.sql
new file mode 100644
index 0000000..77f745f
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_ldcache_tables.sql
@@ -0,0 +1,15 @@
+CREATE SEQUENCE seq_ldcache;
+
+CREATE TABLE ldcache_entries (
+ id bigint NOT NULL,
+ retrieved_at timestamp NOT NULL,
+ expires_at timestamp NOT NULL,
+ resource_id bigint NOT NULL REFERENCES nodes(id),
+ update_count int NOT NULL DEFAULT 0,
+ PRIMARY KEY(id)
+);
+
+
+CREATE INDEX idx_ldcache_expires ON ldcache_entries(expires_at);
+CREATE INDEX idx_ldcache_resource ON ldcache_entries(resource_id);
+
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/ldcache/ldcache-backend-kiwi/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_ldcache_tables.sql
----------------------------------------------------------------------
diff --git a/ldcache/ldcache-backend-kiwi/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_ldcache_tables.sql b/ldcache/ldcache-backend-kiwi/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_ldcache_tables.sql
new file mode 100644
index 0000000..45568fe
--- /dev/null
+++ b/ldcache/ldcache-backend-kiwi/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_ldcache_tables.sql
@@ -0,0 +1,6 @@
+DROP INDEX idx_ldcache_expires;
+DROP INDEX idx_ldcache_resource;
+
+DROP TABLE IF EXISTS ldcache_entries;
+
+DROP SEQUENCE IF EXISTS seq_ldcache;