Java tutorial
/* Copyright (C) 2012 Alasdair Mercer, http://neocotic.com/ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.neocotic.bloggaer.common; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.ClassUtils; import com.neocotic.bloggaer.common.exception.BloggaerRuntimeException; import com.neocotic.bloggaer.common.logging.Logger; /** * A {@code LocatorHelper} should be used by {@link Locator} children as it already provides the expected caching and * lookup functionality. * * @author Alasdair Mercer * @param <I> * the {@code class} whose implementations are to be located */ public class LocatorHelper<I> { /** The {@link Logger} instance for {@link LocatorHelper}. */ private static final Logger logger = Logger.getLogger(LocatorHelper.class); /** Cache containing all previously instantiated implementations. */ private final Map<String, I> cache = new HashMap<String, I>(); /** The {@link Locator} using this {@link LocatorHelper}. */ private final Locator<I> locator; /** * Creates a new {@link LocatorHelper} for the {@code locator} provided. * * @param locator * the {@link Locator} that will be using the {@code LocatorHelper} * @throws IllegalArgumentException * If {@code locator} is {@code null}. */ public LocatorHelper(Locator<I> locator) { if (locator == null) throw new IllegalArgumentException("locator is null"); this.locator = locator; } /** * Caches the {@code implementation} provided. * * @param cls * the {@code class} whose POJO {@code implementation} is to be cached * @param implementation * the POJO implementation to be cached * @return The cached {@code implementation}. * @throws IllegalArgumentException * If either {@code cls} or {@code implementation} are {@code null}. */ public <T extends I> T cache(Class<T> cls, T implementation) { logger.entering(new Object[] { cls, implementation }); if (cls == null) throw new IllegalArgumentException("cls is null"); String className = locator.getImplementationClassName(cls); T impl = cache(className, implementation); logger.exiting(impl); return impl; } /** * Caches the {@code implementation} provided. * * @param className * the name of the {@code class} whose POJO {@code implementation} is to be cached * @param implementation * the POJO implementation to be cached * @return The cached {@code implementation}. * @throws IllegalArgumentException * If either {@code className} or {@code implementation} are {@code null}. */ public <T extends I> T cache(String className, T implementation) { logger.entering(new Object[] { className, implementation }); if (className == null) throw new IllegalArgumentException("className is null"); if (implementation == null) throw new IllegalArgumentException("implementation is null"); synchronized (cache) { cache.put(className, implementation); } logger.exiting(implementation); return implementation; } /** * Removes all cached implementations. */ public void clear() { logger.entering(); synchronized (cache) { cache.clear(); } logger.exiting(); } /** * Attempts to return the {@code ClassLoader} of the current {@code Thread} but will fall back to the * {@code ClassLoader} of this {@code class}. * * @return The default {@code ClassLoader}. */ private ClassLoader getDefaultClassLoader() { logger.entering(); ClassLoader classLoader = null; try { classLoader = Thread.currentThread().getContextClassLoader(); } catch (Exception e) { } if (classLoader == null) classLoader = LocatorHelper.class.getClassLoader(); logger.exiting(classLoader); return classLoader; } /** * Retrieves the POJO implementation of the {@code class} provided from the specified sub-package where the name of * the {@code class} is wrapped in the given {@link String Strings}. * <p> * Reflection is used to lookup the POJO implementation and delegate calls to it. * <p> * Previous lookups are cached to improve performance when making multiple calls to an implementation. * * @param cls * the {@code class} whose POJO implementation is to be located * @param packageName * the name of the sub-package containing the POJO implementations * @param prefix * the {@code String} to be prepended to the {@code class} name when locating the POJO implementation * @param suffix * the {@code String} to be appended to the {@code class} name when locating the POJO implementation * @return The POJO implementation of the specified {@code class}. * @throws BloggaerRuntimeException * If an error occurs while loading/instantiating the new {@code class}. * @throws NullPointerException * If {@code cls} is {@code null}. */ @SuppressWarnings("unchecked") public <T extends I> T getImplementation(Class<T> cls, String packageName, String prefix, String suffix) throws BloggaerRuntimeException { logger.entering(new Object[] { cls, packageName, prefix, suffix }); String className = getImplementationClassName(cls, packageName, prefix, suffix); I impl = cache.get(className); if (impl == null) { synchronized (cache) { impl = newImplementation(className); cache.put(className, impl); } } logger.exiting(impl); return (T) impl; } /** * Retrieves the {@code class} name of the POJO implementation. * * @param cls * the {@code class} whose target {@code class} name is to be retrieved * @param packageName * the name of the sub-package to be appended to the package name of the target {@code class} * @param prefix * the {@code String} to be prepended to the name of the target {@code class} * @param suffix * the {@code String} to be appended to the name of the target {@code class} * @return The name of the implementation {@code class}. * @throws NullPointerException * If {@code cls} is {@code null}. */ public <T extends I> String getImplementationClassName(Class<T> cls, String packageName, String prefix, String suffix) { logger.entering(new Object[] { cls, packageName, prefix, suffix }); StringBuilder buffer = new StringBuilder(); buffer.append(cls.getPackage().getName()); buffer.append(ClassUtils.PACKAGE_SEPARATOR); if (packageName != null) { buffer.append(packageName); buffer.append(ClassUtils.PACKAGE_SEPARATOR); } if (prefix != null) buffer.append(prefix); buffer.append(cls.getSimpleName()); if (suffix != null) buffer.append(suffix); String className = buffer.toString(); logger.exiting(className); return className; } /** * Creates a new instance of the implementation {@code class}. * * @param className * the name of the {@code class} to be loaded and instantiated * @return An instance of the specified {@code class}. * @throws BloggaerRuntimeException * If an error occurs while loading/instantiating the new {@code class}. */ @SuppressWarnings("unchecked") private I newImplementation(String className) throws BloggaerRuntimeException { logger.entering(className); I impl = null; try { impl = (I) getDefaultClassLoader().loadClass(className).newInstance(); } catch (Exception e) { e = new BloggaerRuntimeException(e); logger.throwing(e); throw (BloggaerRuntimeException) e; } logger.exiting(impl); return impl; } }