--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<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">
+
+ <parent>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>common-parent</artifactId>
+ <version>0.6.2-SNAPSHOT</version>
+ </parent>
+ <packaging>bundle</packaging>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>object-cache-api</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <tasks>
+ <!-- This is necessary to remove the impl package and
+ make static binding work with the implementation -->
+ <delete dir="${project.build.outputDirectory}/org/opendaylight/yangtools/objcache/impl"/>
+ </tasks>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.objcache;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.ProductAwareBuilder;
+
+/**
+ * A cache of objects. Caches are useful for reducing memory overhead
+ * stemming from multiple copies of identical objects -- by putting
+ * a cache in the instantiation path, one can expend some memory on
+ * indexes and spend some CPU cycles on walking the index to potentially
+ * end up with a reused object.
+ *
+ * Note that the cached objects should really be semantically {@link Immutable}.
+ * This interface does not enforce that interface contract simply because
+ * there are third-party objects which fulfill this contract.
+ */
+public interface ObjectCache {
+ /**
+ * Get a reference for an object which is equal to specified object.
+ * The cache is free return either a cached instance, or retain the
+ * object and return it back.
+ *
+ * @param object Requested object, may be null
+ * @return Reference to an object which is equal to the one passed in.
+ * If @object was @null, this method returns @null.
+ */
+ <T> T getReference(@Nullable T object);
+
+ /**
+ * Get a reference to an object equal to the product of a builder.
+ * The builder is expected to remain constant while this method
+ * executes. Unlike {@link #getReference(Object)}, this method has
+ * the potential of completely eliding the product instantiation.
+ *
+ * @param builder Builder instance, may not be null
+ * @return Result of builder's toInstance() product, or an equal
+ * object.
+ */
+ <B extends ProductAwareBuilder<P>, P> P getProduct(@Nonnull B builder);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.objcache;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.yangtools.objcache.impl.StaticObjectCacheBinder;
+import org.opendaylight.yangtools.objcache.spi.IObjectCacheFactory;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Point of entry for acquiring an {@link ObjectCache} instance.
+ */
+public final class ObjectCacheFactory {
+ private static IObjectCacheFactory FACTORY;
+
+ private static synchronized IObjectCacheFactory initialize() {
+ // Double-check under lock
+ if (FACTORY != null) {
+ return FACTORY;
+ }
+
+ final IObjectCacheFactory f = StaticObjectCacheBinder.getInstance().getProductCacheFactory();
+ FACTORY = f;
+ return f;
+ }
+
+ public static synchronized void reset() {
+ FACTORY = null;
+ }
+
+ /**
+ * Get an ObjectCache for caching a particular object class. Note
+ * that it may be shared for multiple classes.
+ *
+ * @param objClass Class of objects which are to be cached
+ * @return Object cache instance.
+ */
+ public static ObjectCache getObjectCache(@Nonnull final Class<?> objClass) {
+ IObjectCacheFactory f = FACTORY;
+ if (f == null) {
+ f = initialize();
+ }
+
+ return f.getObjectCache(Preconditions.checkNotNull(objClass));
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.objcache.impl;
+
+import org.opendaylight.yangtools.objcache.spi.AbstractObjectCacheBinder;
+
+/*
+ * This is a dummy placeholder implementation. The API package is bound to
+ * it at compile-time, but it is not packaged and thus not present at run-time.
+ */
+public final class StaticObjectCacheBinder extends AbstractObjectCacheBinder {
+ private StaticObjectCacheBinder() {
+ super(null);
+ }
+
+ public static StaticObjectCacheBinder getInstance() {
+ throw new IllegalStateException("This class should have been replaced");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+/**
+ * Static binding implementation package. The API package is bound at compile-time
+ * to this package. Implementations are expected to supply this package.
+ */
+package org.opendaylight.yangtools.objcache.impl;
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+/**
+ *
+ */
+package org.opendaylight.yangtools.objcache;
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.objcache.spi;
+
+import org.opendaylight.yangtools.concepts.ProductAwareBuilder;
+import org.opendaylight.yangtools.objcache.ObjectCache;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.FinalizableReferenceQueue;
+import com.google.common.base.FinalizableSoftReference;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+
+/**
+ * Abstract object cache implementation. This implementation takes care
+ * of interacting with the user and manages interaction with the Garbage
+ * Collector (via soft references). Subclasses are expected to provide
+ * a backing {@link Cache} instance and provide the
+ */
+public abstract class AbstractObjectCache implements ObjectCache {
+ /**
+ * Key used when looking up a ProductAwareBuilder product. We assume
+ * the builder is not being modified for the duration of the lookup,
+ * anything else is the user's fault.
+ */
+ private static final class BuilderKey {
+ private final ProductAwareBuilder<?> builder;
+
+ private BuilderKey(final ProductAwareBuilder<?> builder) {
+ this.builder = Preconditions.checkNotNull(builder);
+ }
+
+ @Override
+ public int hashCode() {
+ return builder.productHashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ /*
+ * We can tolerate null objects coming our way, but we need
+ * to be on the lookout for WeakKeys, as we cannot pass them
+ * directly to productEquals().
+ */
+ if (obj != null && obj instanceof SoftKey) {
+ obj = ((SoftKey)obj).get();
+ }
+
+ return builder.productEquals(obj);
+ }
+ }
+
+ /**
+ * Key used in the underlying map. It is essentially a soft reference, with
+ * slightly special properties.
+ *
+ * It acts as a proxy for the object it refers to and essentially delegates
+ * to it. There are three exceptions here:
+ *
+ * 1) This key needs to have a cached hash code. The requirement is that the
+ * key needs to be able to look itself up after the reference to the object
+ * has been cleared (and thus we can no longer look it up from there). One
+ * typical container where we are stored are HashMaps -- and they need it
+ * to be constant.
+ * 2) This key does not tolerate checks to see if its equal to null. While we
+ * could return false, we want to catch offenders who try to store nulls
+ * in the cache.
+ * 3) This key inverts the check for equality, e.g. it calls equals() on the
+ * object which was passed to its equals(). Instead of supplying itself,
+ * it supplies the referent. If the soft reference is cleared, such check
+ * will return false, which is fine as it prevents normal lookup from
+ * seeing the cleared key. Removal is handled by the explicit identity
+ * check.
+ */
+ private static abstract class SoftKey extends FinalizableSoftReference<Object> {
+ private final int hashCode;
+
+ public SoftKey(final Object referent, final FinalizableReferenceQueue q) {
+ super(Preconditions.checkNotNull(referent), q);
+ hashCode = referent.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ Preconditions.checkState(obj != null);
+
+ // Order is important: we do not want to call equals() on ourselves!
+ return this == obj || obj.equals(get());
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+ }
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractObjectCache.class);
+ private final FinalizableReferenceQueue queue;
+ private final Cache<Object, Object> cache;
+
+ protected AbstractObjectCache(final Cache<Object, Object> cache, final FinalizableReferenceQueue queue) {
+ this.queue = Preconditions.checkNotNull(queue);
+ this.cache = Preconditions.checkNotNull(cache);
+ }
+
+ private <T> T put(final T object) {
+ /*
+ * This may look like a race (having a soft reference and not have
+ * it in the cache). In fact this is protected by the fact we still
+ * have a strong reference on the object in our arguments and that
+ * reference survives past method return since we return it.
+ */
+ final Object key = new SoftKey(object, queue) {
+ @Override
+ public void finalizeReferent() {
+ /*
+ * NOTE: while it may be tempting to add "object" into this
+ * trace message, do not ever do that: it would retain
+ * a strong reference, preventing collection.
+ */
+ LOG.trace("Invalidating key {} for object {}", this);
+ cache.invalidate(this);
+ }
+ };
+ cache.put(key, object);
+ LOG.debug("Cached key {} to object {}", key, object);
+ return object;
+ }
+
+ @Override
+ public final <B extends ProductAwareBuilder<P>, P> P getProduct(final B builder) {
+ LOG.debug("Looking up product for {}", builder);
+
+ @SuppressWarnings("unchecked")
+ final P ret = (P) cache.getIfPresent(new BuilderKey(builder));
+ return ret == null ? put(Preconditions.checkNotNull(builder.toInstance())) : ret;
+ }
+
+ @Override
+ public final <T> T getReference(final T object) {
+ LOG.debug("Looking up reference for {}", object);
+ if (object == null) {
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ final T ret = (T) cache.getIfPresent(object);
+ return ret == null ? put(object) : ret;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.objcache.spi;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.base.Preconditions;
+
+public abstract class AbstractObjectCacheBinder implements ObjectCacheFactoryBinder {
+ private final IObjectCacheFactory factory;
+
+ protected AbstractObjectCacheBinder(@Nonnull final IObjectCacheFactory factory) {
+ this.factory = Preconditions.checkNotNull(factory);
+ }
+
+ @Override
+ public final IObjectCacheFactory getProductCacheFactory() {
+ return factory;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.objcache.spi;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.yangtools.objcache.ObjectCache;
+
+public interface IObjectCacheFactory {
+ ObjectCache getObjectCache(@Nonnull Class<?> objClass);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.objcache.spi;
+
+import org.opendaylight.yangtools.concepts.ProductAwareBuilder;
+import org.opendaylight.yangtools.objcache.ObjectCache;
+
+/**
+ * No-operation implementation of an Object Cache. This implementation
+ * does not do any caching, so it only returns the request object.
+ */
+public final class NoopObjectCache implements ObjectCache {
+ private static final NoopObjectCache INSTANCE = new NoopObjectCache();
+
+ private NoopObjectCache() {
+
+ }
+
+ /**
+ * Get the cache instance. Since the cache does not have any state,
+ * this method always returns a singleton instance.
+ *
+ * @return Cache instance.
+ */
+ public static NoopObjectCache getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public <T> T getReference(final T object) {
+ return object;
+ }
+
+ @Override
+ public <B extends ProductAwareBuilder<P>, P> P getProduct(final B builder) {
+ return builder.toInstance();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.objcache.spi;
+
+/**
+ * Interface binding an implementation into ObjectCacheFactory.
+ */
+public interface ObjectCacheFactoryBinder {
+ /**
+ * Get the implementation-specific cache factory.
+ *
+ * @return Implementation-specific factory.
+ */
+ IObjectCacheFactory getProductCacheFactory();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+/**
+ * Service Provider Interface for Object Cache. Object cache implementations
+ * use classes contained in this package to implement their functionality.
+ */
+package org.opendaylight.yangtools.objcache.spi;
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<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">
+
+ <parent>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>common-parent</artifactId>
+ <version>0.6.2-SNAPSHOT</version>
+ </parent>
+ <packaging>bundle</packaging>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>object-cache-noop</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>object-cache-api</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.objcache.impl;
+
+import org.opendaylight.yangtools.objcache.ObjectCache;
+import org.opendaylight.yangtools.objcache.spi.AbstractObjectCacheBinder;
+import org.opendaylight.yangtools.objcache.spi.IObjectCacheFactory;
+import org.opendaylight.yangtools.objcache.spi.NoopObjectCache;
+
+public final class StaticObjectCacheBinder extends AbstractObjectCacheBinder {
+ private static final StaticObjectCacheBinder INSTANCE = new StaticObjectCacheBinder();
+
+ private StaticObjectCacheBinder() {
+ super(new IObjectCacheFactory() {
+ @Override
+ public ObjectCache getObjectCache(final Class<?> objClass) {
+ return NoopObjectCache.getInstance();
+ }
+ });
+ }
+
+ public static StaticObjectCacheBinder getInstance() {
+ return INSTANCE;
+ }
+}
<modules>
<module>concepts</module>
<module>mockito-configuration</module>
+ <module>object-cache-api</module>
+ <module>object-cache-noop</module>
</modules>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>object-cache-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>object-cache-noop</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
</project>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang.version}</version>
</dependency>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <version>2.0.3</version>
+ </dependency>
<!-- Plugin integration -->
<dependency>