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

org.refcodes.io.FileUtility Maven / Gradle / Ivy

Go to download

Artifact with commonly used I/O functionality and for connection related issues such as receiving or transmitting data in a unified way.

There is a newer version: 3.3.8
Show newest version
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// =============================================================================
// This code is copyright (c) by Siegfried Steiner, Munich, Germany and licensed
// under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// =============================================================================
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// together with the GPL linking exception applied; as being applied by the GNU
// Classpath ("http://www.gnu.org/software/classpath/license.html")
// =============================================================================
// Apache License, v2.0 ("http://www.apache.org/licenses/LICENSE-2.0")
// =============================================================================
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////

package org.refcodes.io;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

import org.refcodes.data.Delimiter;
import org.refcodes.data.Encoding;
import org.refcodes.data.FilenameExtension;
import org.refcodes.data.Scheme;
import org.refcodes.textual.CsvBuilderImpl;
import org.refcodes.textual.CsvEscapeMode;
import org.refcodes.textual.RandomTextGenerartorImpl;
import org.refcodes.textual.RandomTextMode;

/**
 * The {@link FileUtility} provides Various file related utility functionality.
 */
public final class FileUtility {

	// /////////////////////////////////////////////////////////////////////////
	// CONSTANTS:
	// /////////////////////////////////////////////////////////////////////////

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Private empty constructor to prevent instantiation as of being a utility
	 * with just static public methods.
	 */
	private FileUtility() {}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Provides an {@link InputStream} for a resource found at the given path
	 * relative to the given class file (which might be inside a Java archive
	 * such as a JAR file or a WAR file).
	 * 
	 * @param aClass The class relative to which to look for the resource.
	 * 
	 * @param aPath The path which to use relative to the given class.
	 * 
	 * @return The {@link InputStream} for the requested resource.
	 */
	public static InputStream getResourceAsStream( Class aClass, String aPath ) {
		InputStream theInputStream = aClass.getResourceAsStream( aPath );
		if ( theInputStream == null ) {
			theInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( aPath );
		}
		if ( theInputStream == null ) {
			theInputStream = aClass.getClassLoader().getResourceAsStream( aPath );
		}
		return theInputStream;
	}

	/**
	 * Generates a file name for a temporary file consisting if the current time
	 * in milliseconds and a portion of random character to avoid name clashes:
	 * "temp-012345678901234567890123456789-abcdefgh".
	 * 
	 * @return A temp file name.
	 */
	public static String toTempFileName() {
		return "temp-" + "-" + System.currentTimeMillis() + "-" + new RandomTextGenerartorImpl().withColumnWidth( 8 ).withRandomTextMode( RandomTextMode.ASCII ).next().toLowerCase() + FilenameExtension.TEMP.getFilenameExtension();
	}

	/**
	 * Copies a file residing in a nested JAR to the given destination folder.
	 * The provided folder represents the base folder, actually an unambiguous
	 * folder layout is created within that base folder to prevent side effects
	 * with files of the same name residing in different nested JAR archives.
	 * 
	 * @param aJarUrl The URL which points into a (nested) JAR's resource.
	 * 
	 * @param aToDir The base directory into which the (nested) JAR's resource
	 *        will be extracted.
	 * 
	 * @return The file URL (protocol "file:") for the extracted resource
	 *         addressed in the (nested) JAR or null in case we do not have a
	 *         JAR URL. In case we already have a file URL then the URL is
	 *         returned untouched ignoring any passed destination folder (the
	 *         to-dir argument).
	 * 
	 * @throws IOException in case processing the extracted file caused
	 *         problems, e.g. the target folder is not a directory, it is not
	 *         writable, the JAR archive caused problems (currpted) and so on.
	 * 
	 * @see "https://docs.jboss.org/jbossas/javadoc/4.0.2/org/jboss/util/file/JarUtils.java.html"
	 */
	public static URL createNestedJarFileUrl( URL aJarUrl, File aToDir ) throws IOException {
		if ( aJarUrl.getProtocol().equals( Scheme.FILE.getName() ) ) return aJarUrl;
		if ( !aJarUrl.getProtocol().equals( Scheme.JAR.getName() ) ) return null;

		// Determine target path:
		String theJarPath = new CsvBuilderImpl().withCsvEscapeMode( CsvEscapeMode.ESCAPED ).withFields( FileUtility.toJarHierarchy( aJarUrl ) ).withDelimiter( Delimiter.SYSTEM_FILE_PATH.getChar() ).toRecord();
		File theJarDir = new File( aToDir, theJarPath );
		if ( !theJarDir.exists() && !theJarDir.mkdirs() ) {
			throw new IOException( "Failed to create contents directory for archive, path=" + theJarDir.getAbsolutePath() );
		}

		// Process JAR:
		JarURLConnection theJarConnection = (JarURLConnection) aJarUrl.openConnection();
		String theEntyName = theJarConnection.getEntryName();
		File theEntryFile = new File( theJarDir, theEntyName );
		// Do we address a folder?
		if ( theEntyName.endsWith( "" + Delimiter.PATH ) ) {
			theEntryFile.mkdirs();
		}
		// Do we address a folder's entry?
		else {
			File theJarParentDir = theEntryFile.getParentFile();
			if ( !theJarParentDir.exists() && !theJarParentDir.mkdirs() ) {
				throw new IOException( "Failed to create parent directory for archive, path=" + theJarParentDir.getAbsolutePath() );
			}
			try (InputStream theJarInputStream = theJarConnection.getInputStream(); BufferedOutputStream theOutputStream = new BufferedOutputStream( new FileOutputStream( theEntryFile ) )) {
				byte[] theBuffer = new byte[4096];
				int eRead;
				while ( (eRead = theJarInputStream.read( theBuffer )) > 0 ) {
					theOutputStream.write( theBuffer, 0, eRead );
				}

			}
		}
		return theEntryFile.toURI().toURL();
	}

	/**
	 * Determines whether an according destination file already exists for the
	 * file residing in a nested JAR. The provided folder represents the base
	 * folder; actually an unambiguous folder layout as of
	 * {@link #toJarHierarchy(URL)} is assumed within that base folder (as
	 * created by the {@link #createNestedJarFileUrl(URL, File)}) for preventing
	 * side effects with files of the same name residing in different nested JAR
	 * archives.
	 * 
	 * @param aJarUrl The URL which points into a (nested) JAR's resource.
	 * 
	 * @param aToDir The base directory in which the (nested) JAR's resource is
	 *        being expected.
	 * 
	 * @return The file URL (protocol "file:") for the identified (existing)
	 *         resource addressed in the (nested) JAR or null in case there is
	 *         no such file in the expected folder layout. In case we already
	 *         have a file URL then the URL is returned untouched ignoring any
	 *         passed destination folder (the to-dir argument).
	 * 
	 * @throws IOException Thrown in case no details on the referenced entry can
	 *         be retrieved from the addressed JAR archive.
	 * 
	 * @see "https://docs.jboss.org/jbossas/javadoc/4.0.2/org/jboss/util/file/JarUtils.java.html"
	 */
	public static URL getNestedJarFileUrl( URL aJarUrl, File aToDir ) throws IOException {
		if ( aJarUrl.getProtocol().equals( Scheme.FILE.getName() ) ) return aJarUrl;
		if ( !aJarUrl.getProtocol().equals( Scheme.JAR.getName() ) ) return null;
		String theJarPath = new CsvBuilderImpl().withCsvEscapeMode( CsvEscapeMode.ESCAPED ).withFields( FileUtility.toJarHierarchy( aJarUrl ) ).withDelimiter( Delimiter.SYSTEM_FILE_PATH.getChar() ).toRecord();
		File theJarDir = new File( aToDir, theJarPath );
		if ( !theJarDir.exists() ) return null;
		JarURLConnection theJarConnection;
		theJarConnection = (JarURLConnection) aJarUrl.openConnection();
		String theEntyName = theJarConnection.getEntryName();
		File theEntryFile = new File( theJarDir, theEntyName );
		if ( !theEntryFile.exists() ) {
			return null;
		}
		return theEntryFile.toURI().toURL();
	}

	/**
	 * Convenience method testing whether the given JAR file resource already
	 * exists in the expected folder layout .Returns its URL in case it already
	 * exists else it is being created and then the URL is returned.
	 *
	 * @param aJarUrl The URL which points into a (nested) JAR's resource.
	 * @param aToDir The base directory in which the (nested) JAR's resource is
	 *        being expected (created).
	 * @return The parrent's JAR file URL or null if the application does not
	 *         seem to reside in a JAR. The file URL (protocol "file:") for the
	 *         existing (created) resource addressed in the (nested) JAR.
	 * @throws IOException Thrown in case no details on the referenced entry can
	 *         be retrieved from the addressed JAR archive.
	 * @see #getNestedJarFileUrl(URL, File)
	 * @see #createNestedJarFileUrl(URL, File)
	 */
	public static URL toNestedJarFileUrl( URL aJarUrl, File aToDir ) throws IOException {
		URL theUrl = getNestedJarFileUrl( aJarUrl, aToDir );
		return theUrl != null ? theUrl : createNestedJarFileUrl( aJarUrl, aToDir );
	}

	/**
	 * Determines the parent JAR file's URL for your running application (not
	 * including the "!" which is required to address a file within that JAR).
	 * 
	 * @return The parrent's JAR file URL or null if the application does not
	 *         seem to reside in a JAR.
	 */
	public static URL toParentJarUrl() {
		URL theUrl = FileUtility.class.getProtectionDomain().getCodeSource().getLocation();
		if ( !theUrl.getProtocol().equals( Scheme.JAR.getName() ) ) return null;
		try {
			String theJarPath = URLDecoder.decode( theUrl.getFile(), Encoding.UTF_8.getCode() );
			theJarPath = Scheme.JAR.toUrl( theJarPath );
			return new URL( theJarPath );
		}
		catch ( UnsupportedEncodingException | MalformedURLException e ) {
			return null;
		}
	}

	/**
	 * Takes an URL pointing into a (nested) JAR resources and returns a list of
	 * JAR archive names (including the ".jar" suffix) in the order of their
	 * nesting, the first JAR archive being the outermost (parent) archive and
	 * the last JAR archive being the innermost archive.
	 * 
	 * A JAR "path" of an URL might look as follows:
	 * 
	 * "jar:file:/home/steiner/Workspaces/com.fightclub/fightclub-app/target/fightclub-app-0.0.1-SNAPSHOT.jar!/webapp/home.xhtml"
	 * "jar:file:/home/steiner/Workspaces/com.fightclub/fightclub-app/target/fightclub-app-0.0.1-SNAPSHOT.jar!/lib/fightclub-adapter-web-0.0.1-SNAPSHOT.jar!/webapp/home.xhtml"
	 * 
	 * @param aJarUrl The URL for which to get the JAR file hierarchy array.
	 * 
	 * @return The array with the JAR archive hierarchy or null if not being a
	 *         JAR URL.
	 */
	public static String[] toJarHierarchy( URL aJarUrl ) {
		if ( !aJarUrl.getProtocol().equals( Scheme.JAR.getName() ) ) return null;
		List theList = new ArrayList();
		try {
			String theJarPath = URLDecoder.decode( aJarUrl.getFile(), Encoding.UTF_8.getCode() );
			String eJarFile;
			int i = Scheme.JAR.firstMarkerIndex( theJarPath );
			while ( i != -1 ) {
				eJarFile = theJarPath.substring( 0, i );
				int j = eJarFile.lastIndexOf( '/' );
				if ( j != -1 ) {
					eJarFile = eJarFile.substring( j + 1 );
				}
				theList.add( eJarFile );
				theJarPath = theJarPath.substring( i );
				i = Scheme.JAR.firstMarkerIndex( theJarPath );
			}

			return theList.toArray( new String[theList.size()] );
		}
		catch ( UnsupportedEncodingException e ) {
			return null;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy