CachedListenerObjectRetriever.java
- /*
- * Copyright (C) 2012-2025 RRiBbit.org
- *
- * 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
- *
- * https://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.rribbit.retrieval;
- import java.util.Collection;
- import java.util.Map;
- import java.util.concurrent.ConcurrentHashMap;
- import org.rribbit.ListenerObject;
- import org.rribbit.creation.ListenerObjectCreator;
- import org.rribbit.creation.notification.ListenerObjectCreationObserver;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- /**
- * This {@link ListenerObjectRetriever} implements caching to be able to retrieve {@link ListenerObject}s more quickly. It keeps a local {@link Map} where requests are
- * mapped to {@link Collection}s of {@link ListenerObject}s. This class extends from {@link DefaultListenerObjectRetriever} and adds caching to the retrieval methods, except
- * {@link #getListenerObjects()}, because that one simply returns all {@link ListenerObject}s.
- *
- * This class implements the {@link ListenerObjectCreationObserver} in order to clear the cache if any new {@link ListenerObject}s are created by any of the {@link ListenerObjectCreator}s
- * that are associated with this {@link CachedListenerObjectRetriever}. Also, if you add a {@link ListenerObjectCreator} to this {@link CachedListenerObjectRetriever}, the cache will
- * be cleared as well.
- *
- * @author G.J. Schouten
- *
- */
- public class CachedListenerObjectRetriever extends DefaultListenerObjectRetriever implements ListenerObjectCreationObserver {
- private static final Logger log = LoggerFactory.getLogger(CachedListenerObjectRetriever.class);
- /**
- * The cache of {@link Collection}s of {@link ListenerObject}s.
- */
- protected Map<RetrievalRequest, Collection<ListenerObject>> cache;
- /**
- * Whenever you use this constructor, be sure to set the {@link ListenerObjectCreator} with the setter provided by this class.
- * If you don't, runtime {@link NullPointerException}s will occur.
- */
- public CachedListenerObjectRetriever() {
- this.init();
- }
- /**
- * This constructor is recommended, since it forces you to specify the {@link ListenerObjectCreator}. Passing a null value for this
- * will result in runtime {@link NullPointerException}s.
- *
- * @param listenerObjectCreators
- */
- public CachedListenerObjectRetriever(ListenerObjectCreator... listenerObjectCreators) {
- super(listenerObjectCreators);
- this.init();
- }
- /**
- * Initializes the cache of this {@link CachedListenerObjectRetriever}.
- */
- protected void init() {
- cache = new ConcurrentHashMap<>();
- }
- /**
- * Clears the cache.
- *
- * @param addedClass
- */
- @Override
- public void onClassAdded(Class<?> addedClass) {
- log.debug("Class was added by ListenerObjectCreator, clearing cache...");
- this.clearCache();
- }
- @Override
- public Collection<ListenerObject> getListenerObjects(Class<?> returnType) {
- this.checkReturnType(returnType);
- log.debug("Inspecting cache for matches");
- RetrievalRequest request = new RetrievalRequest(null, returnType);
- Collection<ListenerObject> listenerObjects = cache.get(request);
- if(listenerObjects == null) {
- log.debug("No match found, retrieving ListenerObject from DefaultRetriever and storing in cache");
- listenerObjects = super.getListenerObjects(returnType);
- cache.put(request, listenerObjects);
- }
- log.debug("Found {} ListenerObjects", listenerObjects.size());
- return listenerObjects;
- }
- @Override
- public Collection<ListenerObject> getListenerObjects(String hint) {
- this.checkHint(hint);
- log.debug("Inspecting cache for matches");
- RetrievalRequest request = new RetrievalRequest(hint, null);
- Collection<ListenerObject> listenerObjects = cache.get(request);
- if(listenerObjects == null) {
- log.debug("No match found, retrieving ListenerObject from DefaultRetriever and storing in cache");
- listenerObjects = super.getListenerObjects(hint);
- cache.put(request, listenerObjects);
- }
- log.debug("Found {} ListenerObjects", listenerObjects.size());
- return listenerObjects;
- }
- @Override
- public Collection<ListenerObject> getListenerObjects(Class<?> returnType, String hint) {
- this.checkReturnType(returnType);
- this.checkHint(hint);
- log.debug("Inspecting cache for matches");
- RetrievalRequest request = new RetrievalRequest(hint, returnType);
- Collection<ListenerObject> listenerObjects = cache.get(request);
- if(listenerObjects == null) {
- log.debug("No match found, retrieving ListenerObject from DefaultRetriever and storing in cache");
- listenerObjects = super.getListenerObjects(returnType, hint);
- cache.put(request, listenerObjects);
- }
- log.debug("Found {} ListenerObjects", listenerObjects.size());
- return listenerObjects;
- }
- @Override
- public void addListenerObjectCreator(ListenerObjectCreator listenerObjectCreator) {
- super.addListenerObjectCreator(listenerObjectCreator);
- listenerObjectCreator.registerObserver(this);
- this.clearCache();
- }
- @Override
- public void setListenerObjectCreator(ListenerObjectCreator listenerObjectCreator) {
- super.setListenerObjectCreator(listenerObjectCreator);
- listenerObjectCreator.registerObserver(this);
- this.clearCache();
- }
- /**
- * Checks whether the hint is null, in order to be able to fullfill the {@link ListenerObjectRetriever} contract.
- *
- * @param hint
- */
- protected void checkHint(String hint) {
- if(hint == null) {
- throw new IllegalArgumentException("hint cannot be null!");
- }
- }
- /**
- * Checks whether the returntype is null, in order to be able to fullfill the {@link ListenerObjectRetriever} contract.
- *
- * @param returnType
- */
- protected void checkReturnType(Class<?> returnType) {
- if(returnType == null) {
- throw new IllegalArgumentException("returnType cannot be null!");
- }
- }
- protected void clearCache() {
- if(cache != null) { //Cache might be null while constructor of superclass is running. This method could be called indirectly from there, so checking for null.
- cache.clear();
- }
- }
- /**
- * This class represents the request made to this {@link CachedListenerObjectRetriever}. It is used as a key in the {@link Map} in order to quickly
- * retrieve {@link ListenerObject}s.
- *
- * @author G.J. Schouten
- *
- */
- protected static class RetrievalRequest {
- private String hint;
- private Class<?> returnType;
- public RetrievalRequest(String hint, Class<?> returnType) {
- this.hint = hint;
- this.returnType = returnType;
- }
- @Override
- public int hashCode() {
- int prime = 31;
- int result = 1;
- result = prime * result + ((hint == null) ? 0 : hint.hashCode());
- result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());
- return result;
- }
- @Override
- public boolean equals(Object obj) {
- if(this == obj) {
- return true;
- }
- if(obj == null) {
- return false;
- }
- if(this.getClass() != obj.getClass()) {
- return false;
- }
- RetrievalRequest other = (RetrievalRequest) obj;
- if(hint == null) {
- if(other.hint != null) {
- return false;
- }
- } else if(!hint.equals(other.hint)) {
- return false;
- }
- if(returnType == null) {
- if(other.returnType != null) {
- return false;
- }
- } else if(!returnType.equals(other.returnType)) {
- return false;
- }
- return true;
- }
- }
- }