package com.peterphi.std.util.jaxb;

import org.apache.log4j.Logger;

import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
 * A factory for JAXBSerialiser instances that can optionally be forced to use EclipseLink MOXy (or use the default JAXBContext
 * implementation acquisition rules).<br />
 * Caches JAXBSerialiser instances created using either {@link java.lang.ref.SoftReference}s or direct hard references (based on config - defaults to soft references)
 */
public class JAXBSerialiserFactory
{
	private static final Logger log = Logger.getLogger(JAXBSerialiserFactory.class);

	private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
	private final boolean useMoxy;
	private boolean useSoftReferences;


	public JAXBSerialiserFactory(boolean useMoxy)
	{
		this(useMoxy, true);
	}


	public JAXBSerialiserFactory(boolean useMoxy, boolean useSoftReferences)
	{
		this.useMoxy = useMoxy;
		this.useSoftReferences = useSoftReferences;
	}


	private Object reference(final JAXBSerialiser instance)
	{
		if (instance == null) throw new IllegalArgumentException("Cannot construct null reference for cache!");

		if (useSoftReferences)
			return new SoftReference<>(instance);
		else
			return instance;
	}


	private JAXBSerialiser dereference(final Object obj)
	{
		if (obj == null)
			return null;

		if (useSoftReferences)
			return ((SoftReference<JAXBSerialiser>) obj).get();
		else
			return (JAXBSerialiser) obj;
	}


	protected JAXBSerialiser getInstance(final String key, final Supplier<JAXBSerialiser> provider)
	{
		JAXBSerialiser instance = dereference(cache.get(key));

		if (instance == null)
		{
			log.debug("Cache miss for: " + key);

			instance = provider.get();
			cache.put(key, reference(instance));

			// We just took the penalty to create a JAXBContext, do some maintenance on the map while we're at it
			prune();
		}
		else if (log.isTraceEnabled())
		{
			log.trace("Cache hit for: " + key);
		}

		return instance;
	}


	/**
	 * Remove all items from the cache
	 */
	public void clear()
	{
		cache.clear();
	}


	/**
	 * Finds stale entries in the map
	 */
	private void prune()
	{
		if (useSoftReferences)
		{
			Iterator<Map.Entry<String, Object>> it = cache.entrySet().iterator();

			while (it.hasNext())
			{
				final Map.Entry<String, Object> entry = it.next();

				if (dereference(entry.getValue()) == null)
					it.remove();
			}
		}
	}


	public JAXBSerialiser getInstance(final Class<?> clazz)
	{
		return getInstance(clazz.toString(), () -> construct(clazz));
	}


	public JAXBSerialiser getInstance(final String contextPath)
	{
		return getInstance(contextPath, () -> construct(contextPath));
	}


	JAXBSerialiser construct(String contextPath)
	{
		if (useMoxy)
			return JAXBSerialiser.getMoxy(contextPath);
		else
			return JAXBSerialiser.getInstance(contextPath);
	}


	JAXBSerialiser construct(Class<?> clazz)
	{
		if (useMoxy)
			return JAXBSerialiser.getMoxy(clazz);
		else
			return JAXBSerialiser.getInstance(clazz);
	}
}

@@ -34,41 +34,8 @@
 b;%0A%0A
-import org.apache.log4j.Logger;%0A%0A
 impo
@@ -66,16 +66,16 @@
 erence;%0A
+
 import j
@@ -587,91 +587,8 @@
 y%0A%7B%0A
-%09private static final Logger log = Logger.getLogger(JAXBSerialiserFactory.class);%0A%0A
 %09pri
@@ -736,17 +736,16 @@
 ences;%0A%0A
-%0A
 %09public 
@@ -1640,49 +1640,8 @@
 %09%09%7B%0A
-%09%09%09log.debug(%22Cache miss for: %22 + key);%0A%0A
 %09%09%09i
@@ -1812,16 +1812,16 @@
 e at it%0A
+
 %09%09%09prune
@@ -1831,88 +1831,8 @@
 %0A%09%09%7D
-%0A%09%09else if (log.isTraceEnabled())%0A%09%09%7B%0A%09%09%09log.trace(%22Cache hit for: %22 + key);%0A%09%09%7D
 %0A%0A%09%09

Remove unnecessary cache logging for JAXBSerialiserFactory