CachedListenerObjectRetriever.java

  1. /*
  2.  * Copyright (C) 2012-2025 RRiBbit.org
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  * https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package org.rribbit.retrieval;

  17. import java.util.Collection;
  18. import java.util.Map;
  19. import java.util.concurrent.ConcurrentHashMap;

  20. import org.rribbit.ListenerObject;
  21. import org.rribbit.creation.ListenerObjectCreator;
  22. import org.rribbit.creation.notification.ListenerObjectCreationObserver;
  23. import org.slf4j.Logger;
  24. import org.slf4j.LoggerFactory;

  25. /**
  26.  * This {@link ListenerObjectRetriever} implements caching to be able to retrieve {@link ListenerObject}s more quickly. It keeps a local {@link Map} where requests are
  27.  * mapped to {@link Collection}s of {@link ListenerObject}s. This class extends from {@link DefaultListenerObjectRetriever} and adds caching to the retrieval methods, except
  28.  * {@link #getListenerObjects()}, because that one simply returns all {@link ListenerObject}s.
  29.  *
  30.  * 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
  31.  * that are associated with this {@link CachedListenerObjectRetriever}. Also, if you add a {@link ListenerObjectCreator} to this {@link CachedListenerObjectRetriever}, the cache will
  32.  * be cleared as well.
  33.  *
  34.  * @author G.J. Schouten
  35.  *
  36.  */
  37. public class CachedListenerObjectRetriever extends DefaultListenerObjectRetriever implements ListenerObjectCreationObserver {

  38.     private static final Logger log = LoggerFactory.getLogger(CachedListenerObjectRetriever.class);

  39.     /**
  40.      * The cache of {@link Collection}s of {@link ListenerObject}s.
  41.      */
  42.     protected Map<RetrievalRequest, Collection<ListenerObject>> cache;

  43.     /**
  44.      * Whenever you use this constructor, be sure to set the {@link ListenerObjectCreator} with the setter provided by this class.
  45.      * If you don't, runtime {@link NullPointerException}s will occur.
  46.      */
  47.     public CachedListenerObjectRetriever() {
  48.         this.init();
  49.     }

  50.     /**
  51.      * This constructor is recommended, since it forces you to specify the {@link ListenerObjectCreator}. Passing a null value for this
  52.      * will result in runtime {@link NullPointerException}s.
  53.      *
  54.      * @param listenerObjectCreators
  55.      */
  56.     public CachedListenerObjectRetriever(ListenerObjectCreator... listenerObjectCreators) {
  57.         super(listenerObjectCreators);
  58.         this.init();
  59.     }

  60.     /**
  61.      * Initializes the cache of this {@link CachedListenerObjectRetriever}.
  62.      */
  63.     protected void init() {
  64.         cache = new ConcurrentHashMap<>();
  65.     }

  66.     /**
  67.      * Clears the cache.
  68.      *
  69.      * @param addedClass
  70.      */
  71.     @Override
  72.     public void onClassAdded(Class<?> addedClass) {

  73.         log.debug("Class was added by ListenerObjectCreator, clearing cache...");
  74.         this.clearCache();
  75.     }

  76.     @Override
  77.     public Collection<ListenerObject> getListenerObjects(Class<?> returnType) {

  78.         this.checkReturnType(returnType);

  79.         log.debug("Inspecting cache for matches");
  80.         RetrievalRequest request = new RetrievalRequest(null, returnType);
  81.         Collection<ListenerObject> listenerObjects = cache.get(request);
  82.         if(listenerObjects == null) {
  83.             log.debug("No match found, retrieving ListenerObject from DefaultRetriever and storing in cache");
  84.             listenerObjects = super.getListenerObjects(returnType);
  85.             cache.put(request, listenerObjects);
  86.         }
  87.         log.debug("Found {} ListenerObjects", listenerObjects.size());
  88.         return listenerObjects;
  89.     }

  90.     @Override
  91.     public Collection<ListenerObject> getListenerObjects(String hint) {

  92.         this.checkHint(hint);

  93.         log.debug("Inspecting cache for matches");
  94.         RetrievalRequest request = new RetrievalRequest(hint, null);
  95.         Collection<ListenerObject> listenerObjects = cache.get(request);
  96.         if(listenerObjects == null) {
  97.             log.debug("No match found, retrieving ListenerObject from DefaultRetriever and storing in cache");
  98.             listenerObjects = super.getListenerObjects(hint);
  99.             cache.put(request, listenerObjects);
  100.         }
  101.         log.debug("Found {} ListenerObjects", listenerObjects.size());
  102.         return listenerObjects;
  103.     }

  104.     @Override
  105.     public Collection<ListenerObject> getListenerObjects(Class<?> returnType, String hint) {

  106.         this.checkReturnType(returnType);
  107.         this.checkHint(hint);

  108.         log.debug("Inspecting cache for matches");
  109.         RetrievalRequest request = new RetrievalRequest(hint, returnType);
  110.         Collection<ListenerObject> listenerObjects = cache.get(request);
  111.         if(listenerObjects == null) {
  112.             log.debug("No match found, retrieving ListenerObject from DefaultRetriever and storing in cache");
  113.             listenerObjects = super.getListenerObjects(returnType, hint);
  114.             cache.put(request, listenerObjects);
  115.         }
  116.         log.debug("Found {} ListenerObjects", listenerObjects.size());
  117.         return listenerObjects;
  118.     }

  119.     @Override
  120.     public void addListenerObjectCreator(ListenerObjectCreator listenerObjectCreator) {
  121.         super.addListenerObjectCreator(listenerObjectCreator);
  122.         listenerObjectCreator.registerObserver(this);
  123.         this.clearCache();
  124.     }

  125.     @Override
  126.     public void setListenerObjectCreator(ListenerObjectCreator listenerObjectCreator) {
  127.         super.setListenerObjectCreator(listenerObjectCreator);
  128.         listenerObjectCreator.registerObserver(this);
  129.         this.clearCache();
  130.     }

  131.     /**
  132.      * Checks whether the hint is null, in order to be able to fullfill the {@link ListenerObjectRetriever} contract.
  133.      *
  134.      * @param hint
  135.      */
  136.     protected void checkHint(String hint) {

  137.         if(hint == null) {
  138.             throw new IllegalArgumentException("hint cannot be null!");
  139.         }
  140.     }

  141.     /**
  142.      * Checks whether the returntype is null, in order to be able to fullfill the {@link ListenerObjectRetriever} contract.
  143.      *
  144.      * @param returnType
  145.      */
  146.     protected void checkReturnType(Class<?> returnType) {

  147.         if(returnType == null) {
  148.             throw new IllegalArgumentException("returnType cannot be null!");
  149.         }
  150.     }

  151.     protected void clearCache() {

  152.         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.
  153.             cache.clear();
  154.         }
  155.     }

  156.     /**
  157.      * This class represents the request made to this {@link CachedListenerObjectRetriever}. It is used as a key in the {@link Map} in order to quickly
  158.      * retrieve {@link ListenerObject}s.
  159.      *
  160.      * @author G.J. Schouten
  161.      *
  162.      */
  163.     protected static class RetrievalRequest {

  164.         private String hint;
  165.         private Class<?> returnType;

  166.         public RetrievalRequest(String hint, Class<?> returnType) {
  167.             this.hint = hint;
  168.             this.returnType = returnType;
  169.         }

  170.         @Override
  171.         public int hashCode() {

  172.             int prime = 31;
  173.             int result = 1;
  174.             result = prime * result + ((hint == null) ? 0 : hint.hashCode());
  175.             result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());
  176.             return result;
  177.         }

  178.         @Override
  179.         public boolean equals(Object obj) {

  180.             if(this == obj) {
  181.                 return true;
  182.             }
  183.             if(obj == null) {
  184.                 return false;
  185.             }
  186.             if(this.getClass() != obj.getClass()) {
  187.                 return false;
  188.             }

  189.             RetrievalRequest other = (RetrievalRequest) obj;
  190.             if(hint == null) {
  191.                 if(other.hint != null) {
  192.                     return false;
  193.                 }
  194.             } else if(!hint.equals(other.hint)) {
  195.                 return false;
  196.             }
  197.             if(returnType == null) {
  198.                 if(other.returnType != null) {
  199.                     return false;
  200.                 }
  201.             } else if(!returnType.equals(other.returnType)) {
  202.                 return false;
  203.             }
  204.             return true;
  205.         }
  206.     }
  207. }