
com.marklogic.client.impl.XmlFactories Maven / Gradle / Ivy
Show all versions of marklogic-client-api Show documentation
/*
* Copyright © 2024 MarkLogic Corporation. All Rights Reserved.
*/
package com.marklogic.client.impl;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
import java.lang.ref.SoftReference;
public final class XmlFactories {
private static final CachedInstancePerThreadSupplier cachedOutputFactory =
new CachedInstancePerThreadSupplier(new Supplier() {
@Override
public XMLOutputFactory get() {
return makeNewOutputFactory();
}
});
private XmlFactories() {} // preventing instances of utility class
/**
* Returns a new {@link XMLOutputFactory}. This factory will have its
* {@link XMLOutputFactory#IS_REPAIRING_NAMESPACES} property set to {@code true}.
*
* CAUTION: Creating XML factories is potentially a pretty expensive operation. If possible, consider using a shared
* instance ({@link #getOutputFactory()}) to amortize this initialization cost via reuse.
*
* @return a namespace-repairing {@link XMLOutputFactory}
*
* @throws FactoryConfigurationError see {@link XMLOutputFactory#newInstance()}
*
* @see #getOutputFactory()
*/
public static XMLOutputFactory makeNewOutputFactory() {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
return factory;
}
/**
* Returns a shared {@link XMLOutputFactory}. This factory will have its
* {@link XMLOutputFactory#IS_REPAIRING_NAMESPACES} property set to {@code true}.
*
* Creating XML factories is potentially a pretty expensive operation. Using a shared instance helps to amortize
* this initialization cost via reuse.
*
* @return a namespace-repairing {@link XMLOutputFactory}
*
* @throws FactoryConfigurationError see {@link XMLOutputFactory#newInstance()}
*
* @see #makeNewOutputFactory() if you really (really?) need an non-shared instance
*/
public static XMLOutputFactory getOutputFactory() {
return cachedOutputFactory.get();
}
/**
* Represents a supplier of results.
*
*
There is no requirement that a new or distinct result be returned each
* time the supplier is invoked.
*
* @param the type of results supplied by this supplier
*/
// TODO replace with java.util.function.Supplier after Java 8 migration
interface Supplier {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
/**
* A supplier that caches results per thread.
*
* The supplier is thread safe.
*
* Upon first invocation from a certain thread it is guaranteed to invoke the {@code supplier}'s {@code get()}
* method to obtain a thread-specific result.
*
* Cached values are wrapped in a {@link java.lang.ref.SoftReference} to allow them to be garbage collected upon low
* memory. This may lead to multiple calls to the {@code delegate}'s {@code get()} method over the lifetime of a
* certain thread if a previous result was cleared due to low memory.
*
* @param the supplier's value type
*/
private static class CachedInstancePerThreadSupplier implements Supplier {
private final ThreadLocal> cachedInstances = new ThreadLocal>();
/**
* The underlying supplier, invoked to originally retrieve the per-thread result
*/
private final Supplier delegate;
CachedInstancePerThreadSupplier(Supplier delegate) {
this.delegate = delegate;
if (null == delegate) {
throw new IllegalArgumentException("Delegate must not be null");
}
}
/**
* Returns the thread-specific instance, possibly creating a new one if there is none exists.
*
* @return a thread specific instance of {@code }. Never {@literal null}.
*/
@Override
public T get() {
SoftReference cachedInstanceReference = cachedInstances.get();
// careful, either the reference itself may be null (upon first access from a thread), or the referred-to
// instance may be null (after a GC run that cleared it out)
T cachedInstance = (null != cachedInstanceReference) ? cachedInstanceReference.get() : null;
if (null == cachedInstance) {
// no instance for the current thread, create a new one ...
cachedInstance = delegate.get();
if (null == cachedInstance) {
throw new IllegalStateException("Must not return null from " + delegate.getClass().getName()
+ "::get() (" + delegate + ")");
}
// ... and retain it for later re-use
cachedInstances.set(new SoftReference(cachedInstance));
}
return cachedInstance;
}
}
}