All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.helger.commons.io.resource.ClassPathResource Maven / Gradle / Ivy

There is a newer version: 11.1.10
Show newest version
/*
 * Copyright (C) 2014-2024 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.helger.commons.io.resource;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Reader;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.nio.charset.Charset;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;

import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.equals.EqualsHelper;
import com.helger.commons.hashcode.HashCodeGenerator;
import com.helger.commons.io.stream.StreamHelper;
import com.helger.commons.lang.ClassLoaderHelper;
import com.helger.commons.lang.IHasClassLoader;
import com.helger.commons.string.StringHelper;
import com.helger.commons.string.ToStringGenerator;
import com.helger.commons.url.URLHelper;

/**
 * Wraps a class path object as a readable resource.
 *
 * @author Philip Helger
 */
@NotThreadSafe
public class ClassPathResource implements IReadableResource, IHasClassLoader
{
  /** Use this prefix to uniquely identify classpath resources */
  public static final String CLASSPATH_PREFIX_LONG = "classpath:";
  /** Use this prefix to uniquely identify classpath resources - alternative */
  public static final String CLASSPATH_PREFIX_SHORT = "cp:";

  // No logger here!

  private String m_sPath;
  private final WeakReference  m_aClassLoader;
  private boolean m_bURLResolved = false;
  private URL m_aURL;

  /**
   * Create a new class path resource, using the specified URL. Class loader
   * handling is automatic.
   *
   * @param aURL
   *        The URL to be used. May not be null.
   */
  public ClassPathResource (@Nonnull final URL aURL)
  {
    this (aURL, null);
  }

  /**
   * Create a new class path resource using the specified URL and class loader
   * (optional).
   *
   * @param aURL
   *        The URL to be used. May not be null.
   * @param aClassLoader
   *        The class loader to use. May be null indicating that
   *        automatic class loader handling should be applied.
   */
  public ClassPathResource (@Nonnull final URL aURL, @Nullable final ClassLoader aClassLoader)
  {
    this (aURL.toExternalForm (), aClassLoader);
  }

  /**
   * Create a new class path resource, using the specified path. Class loader
   * handling is automatic.
   *
   * @param sPath
   *        The path to be used. May neither be null nor empty.
   */
  public ClassPathResource (@Nonnull @Nonempty final String sPath)
  {
    this (sPath, null);
  }

  /**
   * Create a new class path resource using the specified path and class loader
   * (optional).
   *
   * @param sPath
   *        The path to be used. May neither be null nor empty.
   * @param aClassLoader
   *        The class loader to use. May be null indicating that
   *        automatic class loader handling should be applied.
   */
  public ClassPathResource (@Nonnull @Nonempty final String sPath, @Nullable final ClassLoader aClassLoader)
  {
    ValueEnforcer.notEmpty (sPath, "Path");

    m_sPath = getWithoutClassPathPrefix (sPath);

    // In case something was cut...
    if (StringHelper.hasNoText (m_sPath))
      throw new IllegalArgumentException ("No path specified after prefix: " + sPath);

    // Ensure the ClassLoader can be garbage collected if necessary
    m_aClassLoader = aClassLoader == null ? null : new WeakReference <> (aClassLoader);
  }

  private void writeObject (@Nonnull final ObjectOutputStream aOOS) throws IOException
  {
    if (m_aClassLoader != null)
      throw new IOException ("Cannot serialize a ClassPathResource that has a specific ClassLoader!");
    StreamHelper.writeSafeUTF (aOOS, m_sPath);
    // Don't write the rest! After serialization the URL must be resolved again!
  }

  private void readObject (@Nonnull final ObjectInputStream aOIS) throws IOException
  {
    m_sPath = StreamHelper.readSafeUTF (aOIS);
  }

  /**
   * Remove any leading explicit classpath resource prefixes.
   *
   * @param sPath
   *        The source path to strip the class path prefixes from. May be
   *        null.
   * @return null if the parameter was null.
   * @see #CLASSPATH_PREFIX_LONG
   * @see #CLASSPATH_PREFIX_SHORT
   */
  @Nullable
  public static String getWithoutClassPathPrefix (@Nullable final String sPath)
  {
    if (StringHelper.startsWith (sPath, CLASSPATH_PREFIX_LONG))
      return sPath.substring (CLASSPATH_PREFIX_LONG.length ());
    if (StringHelper.startsWith (sPath, CLASSPATH_PREFIX_SHORT))
      return sPath.substring (CLASSPATH_PREFIX_SHORT.length ());
    return sPath;
  }

  /**
   * Check if the passed resource name is an explicit classpath resource. This
   * is the case, if the name starts either with {@link #CLASSPATH_PREFIX_LONG}
   * or {@link #CLASSPATH_PREFIX_SHORT}.
   *
   * @param sName
   *        The name to check. May be null.
   * @return true if the passed name is not null and
   *         an explicit classpath resource.
   */
  public static boolean isExplicitClassPathResource (@Nullable final String sName)
  {
    return StringHelper.startsWith (sName, CLASSPATH_PREFIX_LONG) || StringHelper.startsWith (sName, CLASSPATH_PREFIX_SHORT);
  }

  @Nullable
  public ClassLoader getClassLoader ()
  {
    return m_aClassLoader == null ? null : m_aClassLoader.get ();
  }

  @Nonnull
  public String getResourceID ()
  {
    final URL aURL = getAsURL ();
    return aURL == null ? m_sPath : aURL.toExternalForm ();
  }

  @Nonnull
  @Nonempty
  public String getPath ()
  {
    return m_sPath;
  }

  @SuppressWarnings ("resource")
  @Nullable
  private static InputStream _getInputStream (@Nonnull @Nonempty final String sPath,
                                              @Nullable final URL aURL,
                                              @Nullable final ClassLoader aClassLoader)
  {
    // Simple version
    InputStream ret = null;
    if (aClassLoader != null)
    {
      // An explicit ClassLoader was provided - use it
      ret = ClassLoaderHelper.getResourceAsStream (aClassLoader, sPath);
    }
    else
      if (aURL != null)
      {
        // Resolve from URL
        ret = URLResource.getInputStream (aURL);
      }

    return ret;
  }

  /**
   * Get the input stream for the specified path using automatic class loader
   * handling. The class loaders are iterated in the following order:
   * 
    *
  1. Default class loader (usually the context class loader)
  2. *
  3. The class loader of this class
  4. *
  5. The system class loader
  6. *
* * @param sPath * The path to be resolved. May neither be null nor empty. * @return null if the path could not be resolved. */ @Nullable public static InputStream getInputStream (@Nonnull @Nonempty final String sPath) { final URL aURL = URLHelper.getClassPathURL (sPath); return _getInputStream (sPath, aURL, (ClassLoader) null); } /** * Get the input stream of the passed resource using the specified class * loader only. * * @param sPath * The path to be resolved. May neither be null nor empty. * @param aClassLoader * The class loader to be used. May not be null. * @return null if the path could not be resolved using the * specified class loader. */ @Nullable public static InputStream getInputStream (@Nonnull @Nonempty final String sPath, @Nonnull final ClassLoader aClassLoader) { final URL aURL = ClassLoaderHelper.getResource (aClassLoader, sPath); return _getInputStream (sPath, aURL, (ClassLoader) null); } /** * Get the input stream for the specified path using automatic class loader * handling. If no class loader was specified in the constructor, the class * loaders are iterated in the following order: *
    *
  1. Default class loader (usually the context class loader)
  2. *
  3. The class loader of this class
  4. *
  5. The system class loader
  6. *
* * @return null if no such resource exists. */ @Nullable public InputStream getInputStream () { final URL aURL = getAsURL (); return _getInputStream (m_sPath, aURL, getClassLoader ()); } public final boolean isReadMultiple () { return true; } /** * Get the input stream to the this resource, using the passed class loader * only. * * @param aClassLoader * The class loader to be used. May not be null. * @return null if the path could not be resolved. */ @Nullable public InputStream getInputStreamNoCache (@Nonnull final ClassLoader aClassLoader) { final URL aURL = getAsURLNoCache (aClassLoader); return _getInputStream (m_sPath, aURL, aClassLoader); } /** * Create a {@link Reader} of this resource, using the specified class loader * only. * * @param aClassLoader * The class loader to be used. May not be null. * @param aCharset * The charset to be used for the {@link Reader}. May not be * null. * @return null if the path could not be resolved. */ @Nullable public Reader getReaderNoCache (@Nonnull final ClassLoader aClassLoader, @Nonnull final Charset aCharset) { return StreamHelper.createReader (getInputStreamNoCache (aClassLoader), aCharset); } public boolean exists () { // Uses a cached already resolved URL return getAsURL () != null; } @Nullable private URL _getAsURL () { final ClassLoader aClassLoader = getClassLoader (); if (aClassLoader == null) return URLHelper.getClassPathURL (m_sPath); final String sPath = m_sPath; return ClassLoaderHelper.getResource (aClassLoader, sPath); } public boolean existsNoCacheUsage () { // Resolve the URL again return _getAsURL () != null; } @Nullable public URL getAsURL () { if (!m_bURLResolved) { m_aURL = _getAsURL (); // Remember that we tried to resolve the URL - avoid retry m_bURLResolved = true; } return m_aURL; } /** * Convert the path to a URL without using caching. Otherwise the resolution * of {@link #getAsURL()} using the constructor supplied class loader would * possibly contradict with this resolution. * * @param aClassLoader * The class loader to be used. May not be null. * @return null if the path could not be resolved to a URL */ @Nullable public URL getAsURLNoCache (@Nonnull final ClassLoader aClassLoader) { final String sPath = m_sPath; return ClassLoaderHelper.getResource (aClassLoader, sPath); } /** * Get the file for the specified path using automatic class loader handling. * The class loaders are iterated in the following order: *
    *
  1. Default class loader (usually the context class loader)
  2. *
  3. The class loader of this class
  4. *
  5. The system class loader
  6. *
* * @param sPath * The path to be resolved. May neither be null nor empty. * @return null if the path could not be resolved. */ @Nullable public static File getAsFile (@Nonnull @Nonempty final String sPath) { final URL aURL = URLHelper.getClassPathURL (sPath); return URLHelper.getAsFileOrNull (aURL); } @Nullable public static File getAsFile (@Nonnull @Nonempty final String sPath, @Nonnull final ClassLoader aClassLoader) { final URL aURL = ClassLoaderHelper.getResource (aClassLoader, sPath); return URLHelper.getAsFileOrNull (aURL); } @Nullable public File getAsFile () { // Try to use the cached URL here - avoid double resolution final URL aURL = getAsURL (); return URLHelper.getAsFileOrNull (aURL); } @Nullable public File getAsFileNoCache (@Nonnull final ClassLoader aClassLoader) { final URL aURL = getAsURLNoCache (aClassLoader); return URLHelper.getAsFileOrNull (aURL); } public boolean canRead () { return getAsURL () != null; } public boolean canReadNoCache (@Nonnull final ClassLoader aClassLoader) { return getAsURLNoCache (aClassLoader) != null; } @Nonnull public ClassPathResource getReadableCloneForPath (@Nonnull final String sPath) { return new ClassPathResource (sPath, getClassLoader ()); } @Override public boolean equals (final Object o) { if (o == this) return true; if (o == null || !getClass ().equals (o.getClass ())) return false; final ClassPathResource rhs = (ClassPathResource) o; // URL and URLresolved are state variables return EqualsHelper.equals (m_sPath, rhs.m_sPath) && EqualsHelper.equals (getClassLoader (), rhs.getClassLoader ()); } @Override public int hashCode () { return new HashCodeGenerator (this).append (m_sPath).append (getClassLoader ()).getHashCode (); } @Override public String toString () { return new ToStringGenerator (null).append ("cpPath", m_sPath) .appendIfNotNull ("classLoader", getClassLoader ()) .append ("urlResolved", m_bURLResolved) .append ("URL", m_aURL) .getToString (); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy