Coverage Summary for Class: JAXBContextCache (org.kitodo.utils)

Class Method, % Line, %
JAXBContextCache 100% (5/5) 96,7% (29/30)
JAXBContextCache$ContextDescriptor 100% (3/3) 88,9% (8/9)
Total 100% (8/8) 94,9% (37/39)


 /*
  * (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
  *
  * This file is part of the Kitodo project.
  *
  * It is licensed under GNU General Public License version 3 or later.
  *
  * For the full copyright and license information, please read the
  * GPL3-License.txt file that was distributed with this source code.
  */
 
 package org.kitodo.utils;
 
 import java.io.File;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Unmarshaller;
 
 /**
  * Cache for JAXBContexts. Class contains cache map of already created
  * JAXBContext of object classes.
  */
 public class JAXBContextCache {
 
     private static JAXBContextCache instance;
 
     // ConcurrentHashMap takes care of synchronization in a multi-threaded
     // environment
     private static final Map<Class, JAXBContext> jaxbContextCache = new ConcurrentHashMap();
 
     private static final Map<ContextDescriptor, Object> contextDescriptorObjectCache = new ConcurrentHashMap();
 
     /**
      * The synchronized function singleton() must be used to obtain singleton access
      * to the JAXBContextCache instance.
      *
      * @return the singleton JAXBContextCache instance
      */
     public static JAXBContextCache getInstance() {
         JAXBContextCache localReference = instance;
         if (Objects.isNull(localReference)) {
             synchronized (JAXBContextCache.class) {
                 localReference = instance;
                 if (Objects.isNull(localReference)) {
                     localReference = new JAXBContextCache();
                     instance = localReference;
                 }
             }
         }
         return localReference;
     }
 
     /**
      * Get unmarshalled object. This function caches unmarshalled objects of class
      * and file name. If the file is changed, the object is replaced when the
      * function is called again.
      *
      * @param clazz
      *            The class of object to cache.
      * @param file
      *            The file of object to cache.
      * @param <T>
      *            The generic class type.
      * @return The unmarshalled instance of class
      * @throws JAXBException
      *             The exception while unmarshaller is created or unmarshalling is
      *             being in process
      */
     public static <T> T getUnmarshalled(final Class<T> clazz, final File file) throws JAXBException {
         final ContextDescriptor contextDescriptor = new ContextDescriptor(clazz, file);
         final Object value = contextDescriptorObjectCache.get(contextDescriptor);
         if (Objects.nonNull(value)) {
             return (T) value;
         }
 
         // remove all existing unmarshalled objects for class in conjunction with
         // filename (heap of one unmarshalled object per context descriptor)
         for (ContextDescriptor cachedContextDescriptor : contextDescriptorObjectCache.keySet()) {
             if (cachedContextDescriptor.clazz.equals(clazz.toString())
                     && cachedContextDescriptor.fileName.equals(file.getName())) {
                 contextDescriptorObjectCache.remove(cachedContextDescriptor);
             }
         }
 
         final Unmarshaller unmarshaller = getInstance().getJAXBContext(clazz).createUnmarshaller();
         T unmarshalledFile = (T) unmarshaller.unmarshal(file);
         contextDescriptorObjectCache.put(contextDescriptor, unmarshalledFile);
         return unmarshalledFile;
     }
 
     /**
      * Get the JAXBContext by class from cache.
      *
      * @param clazz
      *            The class to be bound.
      * @return The JAXBContext object.
      * @throws JAXBException
      *             Exception when creating new instance of JAXBContext
      */
     public static JAXBContext getJAXBContext(Class<?> clazz) throws JAXBException {
         if (jaxbContextCache.containsKey(clazz)) {
             return jaxbContextCache.get(clazz);
         }
         JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
         jaxbContextCache.put(clazz, jaxbContext);
         return jaxbContext;
     }
 
     private static class ContextDescriptor {
 
         private final String clazz;
 
         private final long fileLastModified;
 
         private final String fileName;
 
         public boolean equals(Object potentialContextDescriptor) {
             if (potentialContextDescriptor instanceof ContextDescriptor) {
                 final ContextDescriptor contextDescriptor = ((ContextDescriptor) potentialContextDescriptor);
                 return clazz.equals(contextDescriptor.clazz) && this.fileName.equals(contextDescriptor.fileName)
                         && this.fileLastModified == contextDescriptor.fileLastModified;
             }
             return false;
         }
 
         public int hashCode() {
             return (clazz + fileName).hashCode();
         }
 
         ContextDescriptor(Class<?> clazz, File file) {
             this.clazz = clazz.toString();
             this.fileLastModified = file.lastModified();
             this.fileName = file.getName();
         }
     }
 
 }