org.refcodes.io.FileUtility Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refcodes-io Show documentation
Show all versions of refcodes-io Show documentation
Artifact with commonly used I/O functionality and for connection related
issues such as receiving or transmitting data in a unified way.
// /////////////////////////////////////////////////////////////////////////////
// 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.DelimeterConsts;
import org.refcodes.data.EncodingConsts;
import org.refcodes.data.FileSystemConsts;
import org.refcodes.data.SystemConsts;
import org.refcodes.textual.CsvEscapeMode;
import org.refcodes.textual.RandomTextMode;
import org.refcodes.textual.impls.CsvBuilderImpl;
import org.refcodes.textual.impls.RandomTextGenerartorImpl;
/**
* 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() + FileSystemConsts.FILENAME_EXTENSION_TEMP;
}
/**
* 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( FileSystemConsts.FILE_PROTOCOL ) ) return aJarUrl;
if ( !aJarUrl.getProtocol().equals( FileSystemConsts.JAR_PROTOCOL ) ) return null;
// Determine target path:
String theJarPath = new CsvBuilderImpl().withCsvEscapeMode( CsvEscapeMode.ESCAPED ).withFields( FileUtility.toJarHierarchy( aJarUrl ) ).withDelimiterChar( SystemConsts.FILE_PATH_DELIMETER ).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( "" + DelimeterConsts.PATH_DELIMETER ) ) {
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( FileSystemConsts.FILE_PROTOCOL ) ) return aJarUrl;
if ( !aJarUrl.getProtocol().equals( FileSystemConsts.JAR_PROTOCOL ) ) return null;
String theJarPath = new CsvBuilderImpl().withCsvEscapeMode( CsvEscapeMode.ESCAPED ).withFields( FileUtility.toJarHierarchy( aJarUrl ) ).withDelimiterChar( SystemConsts.FILE_PATH_DELIMETER ).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.
*
* @see #getNestedJarFileUrl(URL, File)
* @see #createNestedJarFileUrl(URL, File)
*
* @return The parrent's JAR file URL or null if the application does not
* seem to reside in a JAR.
*
* @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 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.
*/
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( FileSystemConsts.JAR_PROTOCOL ) ) return null;
try {
String theJarPath = URLDecoder.decode( theUrl.getFile(), EncodingConsts.TEXT_ENCODING_UTF_8 );
int i = theJarPath.indexOf( FileSystemConsts.JAR_URL_RESOURCE_MARKER );
if ( i != -1 ) {
i += ((FileSystemConsts.JAR_URL_RESOURCE_MARKER.length()) - 1);
theJarPath = theJarPath.substring( 0, i );
}
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( FileSystemConsts.JAR_PROTOCOL ) ) return null;
List theList = new ArrayList();
try {
String theJarPath = URLDecoder.decode( aJarUrl.getFile(), EncodingConsts.TEXT_ENCODING_UTF_8 );
String eJarFile;
int i = theJarPath.indexOf( FileSystemConsts.JAR_URL_RESOURCE_MARKER );
while ( i != -1 ) {
i += FileSystemConsts.JAR_URL_RESOURCE_MARKER.length();
eJarFile = theJarPath.substring( 0, i - 1 );
int j = eJarFile.lastIndexOf( '/' );
if ( j != -1 ) {
eJarFile = eJarFile.substring( j + 1 );
}
theList.add( eJarFile );
theJarPath = theJarPath.substring( i );
i = theJarPath.indexOf( FileSystemConsts.JAR_URL_RESOURCE_MARKER );
}
return theList.toArray( new String[theList.size()] );
}
catch ( UnsupportedEncodingException e ) {
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy