org.refcodes.io.utilities.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.utilities;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.refcodes.data.consts.DelimeterConsts;
import org.refcodes.data.consts.EncodingConsts;
import org.refcodes.data.consts.FileSystemConsts;
import org.refcodes.data.consts.SystemConsts;
import org.refcodes.textual.utilities.RandomTextUtility;
import org.refcodes.textual.utilities.SeparatedValuesUtility;
/**
* 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;
}
/**
* Returns an {@link InputStream} from the provided {@link File}. In case
* the file points to a ZIP compressed file, then the uncompressed data is
* provided by the {@link InputStream}.
*
* @param aFile The {@link File} for which to get the {@link InputStream}.
*
* @return An {@link InputStream}, in case of a ZIP compressed {@link File},
* an uncompressed {@link InputStream} is returned.
*
* @throws ZipException in case there were problems when accessing the ZIP
* compressed {@link File}.
*
* @throws IOException in case there were problems working with the
* {@link File}.
*
* @throws FileNotFoundException in case there was none such {@link File}
* found.
*/
@SuppressWarnings("resource")
public static InputStream toInputStream( File aFile ) throws ZipException, IOException, FileNotFoundException {
String theUnZipFileName = toFileNameFromZip( aFile.getName() );
if ( theUnZipFileName != null ) {
ZipFile theZipFile = new ZipFile( aFile );
if ( theZipFile.size() != 1 ) { throw new ZipException( "The file \"" + aFile.getAbsolutePath() + "\" has <" + theZipFile.size() + "\" entries, expecting exactly one entry with name \"" + theUnZipFileName + "\"!" ); }
Enumeration> e = theZipFile.entries();
ZipEntry theEntry = (ZipEntry) e.nextElement();
if ( !theUnZipFileName.equals( theEntry.getName() ) ) { throw new ZipException( "The file \"" + aFile.getAbsolutePath() + "\" contains an entry with name \"" + theEntry.getName() + "\", though expecting entry with name \"" + theUnZipFileName + "\"!" ); }
return new BufferedInputStream( theZipFile.getInputStream( theEntry ) );
}
return new BufferedInputStream( new FileInputStream( aFile ) );
}
/**
* Returns an {@link OutputStream} to the provided {@link File}. In case the
* file points to a ZIP compressed file (it has the file suffix ".zip"),
* then the data written to the {@link OutputStream} is being ZIP
* compressed.
*
* @param aFile The {@link File} for which to get the {@link OutputStream}.
*
* @return An {@link OutputStream}, in case of a ZIP compressed {@link File}
* was specified (with suffix ".zip"), then a compressed
* {@link OutputStream} is returned.
*
* @throws ZipException in case there were problems when accessing the ZIP
* compressed {@link File}.
*
* @throws IOException in case there were problems working with the
* {@link File}.
*
* @throws FileNotFoundException in case there was none such {@link File}
* found.
*/
public static OutputStream toOutputStream( File aFile ) throws ZipException, IOException, FileNotFoundException {
String theUnZipFileName = toFileNameFromZip( aFile.getName() );
if ( theUnZipFileName != null ) {
OutputStream theFileOutputStream = new BufferedOutputStream( new FileOutputStream( aFile ) );
ZipOutputStream theZipOutputStream = new ZipOutputStream( theFileOutputStream );
ZipEntry theZipEntry = new ZipEntry( theUnZipFileName );
theZipOutputStream.putNextEntry( theZipEntry );
return theZipOutputStream;
}
return new BufferedOutputStream( new FileOutputStream( aFile ) );
}
/**
* Truncates the ".zip" suffix from the filename and returns the result. For
* example a file with name "log-2023-07-12.txt.zip" results in
* "log-2023-07-12.txt".
*
* @param aZipFileName The file name of the ZIP file for which to get the
* "inner" file name.
*
* @return The "inner" file name if the file suffix was ".zip", else null.
*/
public static String toFileNameFromZip( String aZipFileName ) {
if ( aZipFileName.toLowerCase().endsWith( FileSystemConsts.FILENAME_EXTENSION_ZIP ) ) { return aZipFileName.substring( 0, aZipFileName.length() - FileSystemConsts.FILENAME_EXTENSION_ZIP.length() ); }
return null;
}
/**
* 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() + "-" + RandomTextUtility.toRandomAscii( 8 ).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 = SeparatedValuesUtility.toSeparatedValues( FileUtility.toJarHierarchy( aJarUrl ), SystemConsts.FILE_PATH_DELIMETER );
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 = SeparatedValuesUtility.toSeparatedValues( FileUtility.toJarHierarchy( aJarUrl ), SystemConsts.FILE_PATH_DELIMETER );
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