/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/**
* structure.
* <p>
* If the directory underlying the FileArchive is created by GlassFish
* then FileArchive filters its contents so only
* those files more recent than the creation of the archive itself are visible to
* consumers.
* <p>
* The main motivation is to hide unwanted "left-over" files
* from previous deployments that might linger, especially on Windows,
* after the previous app had been undeployed. (Deployment uses a FileArchive
* to extract the user's JAR-based archive into the applications directory.)
* Historically such left-over files arise after GlassFish expands an archive
* into its exploded form but then some
* code opens but does not close a file in that exploded directory tree.
* <p>
* An open left-over file can be overwritten-in-place on Windows, and
* this happens when a caller invokes {@link #putNextEntry(java.lang.String) }
* to create a new entry (file) inside the archive. But a
* left-over file that is not in the new app but is
* still open by GlassFish cannot be deleted or renamed on Windows and so it will
* remain in the expansion directory. Such left-over files, if not filtered out,
* can confuse GlassFish and the application. By "stamping" the archive
* creation date we can filter out such old, left-over files.
* <p>
* To support this feature, when FileArchive creates a directory it stores a
* the archive. We cannot just use the lastModified value for the top-level
* directory. Users might legitimately use "touch .reload" in the applications/appName
* directory to trigger a dynamic reload of the app. If .reload does not already
* exist then touch creates it, and this would update the lastModified of the
* directory file.
*
* @author Jerome Dochez
* @author Tim Quinn
*/
// the archive abstraction directory.
// the currently opened entry
private static final Logger logger = LogDomains.getLogger(FileArchive.class, LogDomains.DPL_LOGGER);
/*
* tracks stale files in the archive and filters the archive's contents to
* exclude stale entries
*/
/**
* Open an abstract archive
* @param uri path to the archive
*/
}
}
}
/**
* @see #open(URI)
* @param uri a string representing URI
*/
{
}
/**
* Get the size of the archive
* @return tje the size of this archive or -1 on error
*/
return -1;
}
}
/**
* creates a new abstract archive with the given path
* @param uri path to create the archive
*/
/*
* Get the stale file manager before creating the directories; it's
* slightly faster that way.
*/
}
/**
* Close a previously returned sub archive
*
* @param subArchive output stream to close
* @link Archive.getSubArchive}
*/
subArchive.close();
}
/**
* close the abstract archive
*/
}
/**
* delete the archive
*/
public boolean delete() {
// delete the directory structure...
try {
/*
* Create the stale file marker file, if needed.
*/
return result;
} catch (IOException e) {
return false;
}
}
}
/**
* @return an @see java.util.Enumeration of entries in this abstract
* archive
*/
}
/**
* Returns the enumeration of first level directories in this
* archive
* @return enumeration of directories under the root of this archive
*/
if (f.isDirectory() && isEntryValid(f)) {
}
}
return results;
}
/**
* @return an @see java.util.Enumeration of entries in this abstract
* archive, providing the list of embedded archive to not count their
* entries as part of this archive
*/
while (embeddedArchives.hasMoreElements()) {
}
}
/**
* Returns an enumeration of the module file entries with the
* specified prefix. All elements in the enumeration are of
* type String. Each String represents a file name relative
* to the root of the module.
*
* @param prefix the prefix of entries to be included
* @return an enumeration of the archive file entries.
*/
}
/**
* @return true if this archive exists
*/
public boolean exists() {
}
/**
*
* create or obtain an embedded archive within this abstraction.
*
* @param name name of the embedded archive.
*/
if (result instanceof AbstractReadableArchive) {
}
return result;
logger.log(DEBUG_LEVEL, "FileArchive.getSubArchive for {0} found that it is not a valid entry; it is stale",
}
return null;
}
/**
* create or obtain an embedded archive within this abstraction.
*
* @param name name of the embedded archive.
*/
// time to create a new sub directory
} else {
/*
* This subdirectory already exists, so it might be marked as
* stale. Because this invocation is creating the subarchive in
* the current archive, the subdirectory is no longer stale.
*/
}
if (result instanceof AbstractReadableArchive) {
}
return result;
}
/**
*
* create or obtain an embedded archive within this abstraction.
*
* @param name name of the embedded archive.
*/
// Convert name to native form. See bug #6345029 for more details.
if (file.isAbsolute()) {
} else {
// first we try to see if a sub directory with the right file
// name exist
// now we try to open a sub jar file...
// ok, nothing worked, reassing the name to the
// sub directory one
}
}
}
}
/**
* Returns the existence of the given entry name
* The file name must be relative to the root of the module.
*
* @param name the file name relative to the root of the module.
* @return the existence the given entry name.
*/
}
/**
* @return a @see java.io.InputStream for an existing entry in
* the current abstract archive
* @param name the entry name
*/
|| ! isEntryValid(input)) { // If name corresponds to directory, return null as it can not be opened
return null;
}
try {
return bis;
try {
throw new IOException("Error closing FileInputStream after error opening BufferedInputStream for entry " + name, thr);
}
}
}
}
}
/**
* Returns the entry size for a given entry name or 0 if not known
*
* @param name the entry name
* @return the entry size
*/
return 0;
}
}
/**
* @return the manifest information for this abstract archive
*/
try {
return m;
}
} finally {
}
}
return null;
}
/**
* Returns the URI used to create or open the underlyong archive
*
* @return the URI for this archive.
*/
return uri;
}
/**
* rename the archive
*
* @param name the archive name
*/
}
/**
* Reports whether the entry is valid, in the sense that if this
* archive has been created during this execution then the entry
* requested was created later than the archive itself.
* <p>
* It is possible (for example, on Windows) for GlassFish to want to create
* a new archive in a directory that already exists and contains stale
* "left-over" files from a previous deployment, for example. This method
* causes the FileArchive implementation to hide any files that
* reside in the directory for an archive that was created during this VM
* execution but were not explicitly added to the archive using putNextEntry.
*
* @param entry file to check
* @return
*/
}
}
}
return staleFileManager;
}
if (parent instanceof FileArchive) {
} else {
return null;
}
}
/**
* Reports whether the entry is valid, in the sense that the entry is
* more recent than the archive itself.
* @param entryName name of the entry to check
* @return
*/
}
/**
* utility method for deleting a directory and all its content
*/
if (!directory.isDirectory()) {
}
boolean allDeletesSucceeded = true;
// delete contents
if (entries[i].isDirectory()) {
} else {
if (fileDeleteOK) {
}
}
}
}
// delete self
}
/**
* utility method for getting contents of directory and
* sub directories
*/
}
/**
* Adds the files in the specified directory to the collection of files
* already assembled. Excludes the contents of embedded archives in the current archive which
* appear in the file tree anchored at the given directory.
* @param directory the directory to scan for files
* @param files collection of files already assembled to which this directory's files are to be added
* @param embeddedArchives collection of embedded archives in the current archive
* @param logger logger to which to report inability to get the list of files from the directory
*/
void getListOfFiles(File directory, List<String> files, List embeddedArchives, final Logger logger) {
return;
return;
}
if (!aList.isDirectory()) {
}
if (embeddedArchives != null) {
}
} else {
}
}
}
}
/** @return true if this archive abstraction supports overwriting of elements
*
*/
public boolean supportsElementsOverwriting() {
return true;
}
/** delete an entry in the archive
* @param name the entry name
* @return true if the entry was successfully deleted
*
*/
return deleteEntry(name, true);
}
return false;
}
return result;
}
/**
* Closes the current entry
*/
}
}
/**
* @returns an @see java.io.OutputStream for a new entry in this
* current abstract archive.
* @param name the entry name
*/
"Could not delete file {0} in FileArchive {1} during putNextEntry; continuing",
}
}
// if the entry name contains directory structure, we need
// to create those directories first.
}
return os;
}
/**
* Returns the name portion of the archive's URI.
* <p>
* For FileArhive the name is all of the path that follows
* the last slash (ignoring a slash at the end of the path).
* <p>
* Here are some example archive names for the specified FileArchive paths:
* <ul>
* <li>/a/b/c/d/ -> d
* <li>/a/b/c/d -> d
* </ul>
* @return the name of the archive
*
*/
}
/**
* API which FileArchive methods should use for dealing with the StaleFileManager
* implementation.
*/
public static interface StaleFileManager {
/**
* Returns whether the specified file is valid - that is, is dated
* after the archive was created.
* @param f the file to check
* @param isLogging whether to log a warning about the check of the entry
* @return true if the file is valid; false otherwise
*/
/**
* Returns whether the specified file is for the hidden timestamp file
* which FileArchive uses internally.
* @param f the file to check
* @return true if the File is the hidden timestamp file; false otherwise
*/
void flush();
public class Util {
}
/**
* Creates a marker file in the archive directory - if it still
* exists and contains any stale files.
* @param archive the File for the archive to mark
*/
if ( ! (archive instanceof FileArchive)) {
return;
}
}
/**
* Creates a marker file in the archive directory - if it still
* exists and contains any stale files.
* @param archive the File for the archive to mark
*/
if ( ! archiveFile.exists()) {
return;
}
if ( ! staleFileIt.hasNext()) {
return;
}
try {
} catch (FileNotFoundException ex) {
throw new RuntimeException(ex);
}
for ( ; staleFileIt.hasNext(); ) {
}
}
/**
* Returns an Iterator over the files contained in the directory tree
* anchored at the given directory, excluding any stale file
* marker file.
* <p>
* For efficiency, this implementation avoids creating a list of
* all the files in the directory tree all at once. It traverses
* each directory as it encounters it.
* @param dir root of the directory tree to be traversed
* @return Iterator over the contained files
*/
{
new MarkerExcluderFileFilter())));
}
public boolean hasNext() {
return fileListIt.hasNext();
}
if (result.isDirectory()) {
new MarkerExcluderFileFilter())) {
fileListIt.add(f);
/*
* Back up so the next invocation of this method
* will return the just-added entry.
*/
}
}
return result;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
}
/**
* Factory method for a StaleFileManager.
* <p>
* Callers should invoke this method only after they have finished with
* the FileArchive and have tried to delete it. If the directory
* for the archive remains then it contains one or more stale files
* that could not be deleted, and the factory method returns a
* instance that tracks the stale files. If the directory no longer
* exists then the delete succeeded, there are
* @param archive the directory to contain the archive
* @return StaleFileManager for the FileArchive to use
*/
return new StaleFileManagerImpl(archive);
} else {
return new StaleFileManagerImplNoop();
}
}
}
}
/**
* Acts as a stale file manager but does no real work.
* <p>
* Used as a stale file manager for an archive that was successfully
* deleted and therefore contains no stale files.
*/
return true;
}
return true;
}
return false;
}
}
}
public void flush() {
}
}
/**
* Implements stale file manager that might contain stale files.
*/
}
/**
* Reads entry names of stale files from the marker file, if it exists.
* @param markerFile the marker file to be read
* @return Collection of stale entry names.
* @throws FileNotFoundException if the marker file existed initially but vanished before it could be opened
* @throws IOException in case of errors reading the marker file
*/
private static Collection<String> readStaleEntryNames(final File markerFile) throws FileNotFoundException, IOException {
if ( ! markerFile.exists()) {
return result;
}
try {
// Avoid some work if logging is coarser than FINE.
if (isShowEntriesToBeSkipped) {
}
}
if (isShowEntriesToBeSkipped) {
}
return result;
} finally {
}
}
}
}
final boolean result = ( ! isEntryMarkerFile(f)) &&
}
return result;
}
return markerFile.equals(f);
}
/**
* Records that the specified file is valid.
* <p>
* If the file had previously been marked as stale, it will no longer be
* considered stale.
* @param f the File which is now valid
*/
if (updateStaleEntry(f, "FileArchive.StaleFileManager marking formerly stale entry {0} as active")) {
/*
* Process not only the file itself but the directories from the
* file to the owning archive, since the directories are now
* implicitly valid as well.
*/
do {
f = f.getParentFile();
updateStaleEntry(f, "FileArchive.StaleFileManager marking formerly stale ancestor {0} as active");
} while ( ! f.equals(archiveFile));
flush();
}
}
if (updateStaleEntry(f, "FileArchive.StaleFileManager recording deletion of entry {0}")) {
/*
* If there are no other stale files in the same directory as
* the file just deleted, then remove the directory from
* the stale files collection and check its ancestors.
*/
do {
if (isStaleEntryInDir(f.getParentFile())) {
return;
}
updateStaleEntry(f, "FileArchive.StaleFileManager recording that formerly stale directory {0} is no longer stale");
f = f.getParentFile();
} while ( ! f.equals(archiveFile));
flush();
}
}
logger.log(DEBUG_LEVEL, "FileArchive.StaleFileManager.isStaleEntryInDir found remaining stale entry {0} in {1}",
return true;
}
}
return false;
}
if (staleEntryNames.isEmpty()) {
logger.log(DEBUG_LEVEL, "FileArchive.StaleFileManager.updateStaleEntry finds staleEntryNames is empty; skipping");
return false;
}
if (wasStale) {
} else {
}
return wasStale;
}
public void flush() {
if (staleEntryNames.isEmpty()) {
logger.log(DEBUG_LEVEL, "FileArchive.StaleFileManager.flush deleting marker file; no more stale entries");
return;
}
try {
} catch (FileNotFoundException ex) {
throw new RuntimeException(ex);
}
}
logger.log(DEBUG_LEVEL, "FileArchive.StaleFileManager.flush rewrote on-disk file {0} containing {1}",
}
}
}