/* * #%L * Gravia :: Resource * %% * Copyright (C) 2010 - 2014 JBoss by Red Hat * %% * 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. * #L% */ package org.jboss.gravia.resource.spi; import static org.jboss.gravia.resource.spi.ResourceLogger.LOGGER; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.jboss.gravia.resource.Capability; import org.jboss.gravia.resource.MatchPolicy; import org.jboss.gravia.resource.Requirement; import org.jboss.gravia.resource.Resource; import org.jboss.gravia.resource.ResourceIdentity; import org.jboss.gravia.resource.ResourceStore; import org.jboss.gravia.utils.IllegalArgumentAssertion; /** * An abstract {@link ResourceStore} * * @author thomas.diesler@jboss.com * @since 02-Jul-2010 */ public abstract class AbstractResourceStore implements ResourceStore { private final String storeName; private final Map<ResourceIdentity, Resource> resources = new LinkedHashMap<ResourceIdentity, Resource>(); private final Map<CacheKey, Set<Capability>> capabilityCache = new ConcurrentHashMap<CacheKey, Set<Capability>>(); private final MatchPolicy matchPolicy; public AbstractResourceStore(String storeName, MatchPolicy matchPolicy) { IllegalArgumentAssertion.assertNotNull(storeName, "storeName"); IllegalArgumentAssertion.assertNotNull(matchPolicy, "matchPolicy"); this.storeName = storeName; this.matchPolicy = matchPolicy; } @Override public String getName() { return storeName; } @Override public MatchPolicy getMatchPolicy() { return matchPolicy; } @Override public Iterator<Resource> getResources() { final Iterator<Resource> itres; synchronized (resources) { Set<Resource> snapshot = new LinkedHashSet<Resource>(resources.values()); itres = snapshot.iterator(); } return new Iterator<Resource>() { @Override public boolean hasNext() { synchronized (resources) { return itres.hasNext(); } } @Override public Resource next() { synchronized (resources) { return itres.next(); } } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public Resource addResource(Resource res) { synchronized (resources) { if (getResourceInternal(res.getIdentity()) != null) throw new IllegalArgumentException("Resource already added: " + res); LOGGER.debug("Add to {}: {}", storeName, res); // Add resource capabilites for (Capability cap : res.getCapabilities(null)) { CacheKey cachekey = CacheKey.create(cap); getCachedCapabilities(cachekey).add(cap); } // Log cap/req details if (LOGGER.isDebugEnabled()) { for (Capability cap : res.getCapabilities(null)) { LOGGER.debug(" {}", cap); } for (Requirement req : res.getRequirements(null)) { LOGGER.debug(" {}", req); } } resources.put(res.getIdentity(), res); return res; } } @Override public Resource removeResource(ResourceIdentity resid) { synchronized (resources) { Resource res = resources.remove(resid); if (res != null) { LOGGER.debug("Remove from {}: {}", storeName, res); // Remove resource capabilities for (Capability cap : res.getCapabilities(null)) { CacheKey cachekey = CacheKey.create(cap); Set<Capability> cachecaps = getCachedCapabilities(cachekey); cachecaps.remove(cap); if (cachecaps.isEmpty()) { capabilityCache.remove(cachekey); } } } return res; } } @Override public Resource getResource(ResourceIdentity resid) { return getResourceInternal(resid); } private Resource getResourceInternal(ResourceIdentity resid) { synchronized (resources) { return resources.get(resid); } } @Override public Set<Capability> findProviders(Requirement req) { CacheKey cachekey = CacheKey.create(req); Set<Capability> result = new HashSet<Capability>(); for (Capability cap : findCachedCapabilities(cachekey)) { if (matchPolicy.match(cap, req)) { result.add(cap); } } return Collections.unmodifiableSet(result); } private synchronized Set<Capability> getCachedCapabilities(CacheKey key) { Set<Capability> capset = capabilityCache.get(key); if (capset == null) { capset = new LinkedHashSet<Capability>(); capabilityCache.put(key, capset); } return capset; } private synchronized Set<Capability> findCachedCapabilities(CacheKey key) { Set<Capability> capset = capabilityCache.get(key); if (capset == null) { capset = new LinkedHashSet<Capability>(); // do not add this to the capabilityCache } if (capset.isEmpty() && (key.value == null)) { for (Entry<CacheKey, Set<Capability>> entry : capabilityCache.entrySet()) { CacheKey auxkey = entry.getKey(); if (auxkey.namespace.equals(key.namespace)) { capset.addAll(entry.getValue()); } } } return Collections.unmodifiableSet(capset); } @Override public String toString() { String prefix = getClass() != AbstractResourceStore.class ? getClass().getSimpleName() : ResourceStore.class.getSimpleName(); return prefix + "[" + storeName + "]"; } private static class CacheKey { private final String namespace; private final String value; private final String keyspec; static CacheKey create(Capability cap) { String namespace = cap.getNamespace(); String nsvalue = (String) cap.getAttributes().get(namespace); return new CacheKey(namespace, nsvalue); } static CacheKey create(Requirement req) { String namespace = req.getNamespace(); String nsvalue = (String) req.getAttributes().get(namespace); return new CacheKey(namespace, nsvalue); } private CacheKey(String namespace, String value) { this.namespace = namespace; this.value = value; this.keyspec = namespace + ":" + value; } @Override public int hashCode() { return keyspec.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof CacheKey)) return false; CacheKey other = (CacheKey) obj; return keyspec.equals(other.keyspec); } @Override public String toString() { return "[" + keyspec + "]"; } } }