org.openide.filesystems.JarArchiveRootProvider Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.openide.filesystems;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.filesystems.spi.ArchiveRootProvider;
import org.openide.util.Exceptions;
/**
*
* @author Tomas Zezula
*/
final class JarArchiveRootProvider implements ArchiveRootProvider {
private static final String PROTOCOL = "jar"; //NOI18N
/** Normal header for ZIP files. */
private static byte[] ZIP_HEADER_1 = {0x50, 0x4b, 0x03, 0x04};
/** Also seems to be used at least in apisupport/project/test/unit/data/example-external-projects/suite3/nbplatform/random/modules/ext/stuff.jar; not known why */
private static byte[] ZIP_HEADER_2 = {0x50, 0x4b, 0x05, 0x06};
private static final Logger LOG = Logger.getLogger(JarArchiveRootProvider.class.getName());
/** Cache for {@link #isArchiveFile(FileObject)}. */
private static final Map*@GuardedBy("archiveFileCache")*/FileObject, Boolean> archiveFileCache = Collections.synchronizedMap(new WeakHashMap());
@Override
public boolean isArchiveFile(URL url, boolean strict) {
if (PROTOCOL.equals(url.getProtocol())) { //NOI18N
//Already inside archive, return false
return false;
}
FileObject fo = URLMapper.findFileObject(url);
if ((fo != null) && !fo.isVirtual()) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.log(Level.FINEST, "isArchiveFile_FILE_RESOLVED", fo); //NOI18N, used by FileUtilTest.testIsArchiveFileRace
}
return isArchiveFile(fo, strict);
} else {
return isArchiveFile(url.getPath());
}
}
@Override
public boolean isArchiveFile(FileObject fo, boolean strict) {
if (!fo.isValid()) {
return isArchiveFile(fo.getPath());
}
// XXX Special handling of virtual file objects: try to determine it using its name, but don't cache the
// result; when the file is checked out the more correct method can be used
if (fo.isVirtual()) {
return isArchiveFile(fo.getPath());
}
if (fo.isFolder()) {
return false;
}
// First check the cache.
Boolean b = archiveFileCache.get(fo);
if (b == null) {
// Need to check it.
try {
InputStream in = fo.getInputStream();
try {
byte[] buffer = new byte[4];
int len = in.read(buffer, 0, 4);
if (len == 4) {
// Got a header, see if it is a ZIP file.
b = Boolean.valueOf(Arrays.equals(ZIP_HEADER_1, buffer) || Arrays.equals(ZIP_HEADER_2, buffer));
} else {
//If the length is less than 4, it can be either
//broken (empty) archive file or other empty file.
//Return false and don't cache it, when the archive
//file will be written and closed its length will change
return !strict;
}
} finally {
in.close();
}
} catch (IOException ioe) {
// #160507 - ignore exception (e.g. permission denied)
LOG.log(Level.FINE, null, ioe);
}
if (b == null) {
b = isArchiveFile(fo.getPath());
}
archiveFileCache.put(fo, b);
}
return b.booleanValue();
}
@Override
public boolean isArchiveArtifact(URL url) {
return PROTOCOL.equals(url.getProtocol());
}
@Override
public URL getArchiveFile(URL url) {
String protocol = url.getProtocol();
if (PROTOCOL.equals(protocol)) { //NOI18N
String path = url.getPath();
int index = path.indexOf("!/"); //NOI18N
if (index >= 0) {
String jarPath = null;
try {
jarPath = path.substring(0, index);
if (jarPath.indexOf("file://") > -1 && jarPath.indexOf("file:////") == -1) { //NOI18N
/* Replace because JDK application classloader wrongly recognizes UNC paths. */
jarPath = jarPath.replaceFirst("file://", "file:////"); //NOI18N
}
return new URL(jarPath);
} catch (MalformedURLException mue) {
LOG.log(
Level.WARNING,
"Invalid URL ({0}): {1}, jarPath: {2}", //NOI18N
new Object[] {
mue.getMessage(),
url.toExternalForm(),
jarPath
});
}
}
}
return null;
}
@Override
public FileObject getArchiveFile(FileObject fo) {
try {
final FileSystem fs = fo.getFileSystem();
if (fs instanceof JarFileSystem) {
final File jarFile = ((JarFileSystem) fs).getJarFile();
return FileUtil.toFileObject(jarFile);
}
} catch (FileStateInvalidException e) {
Exceptions.printStackTrace(e);
}
return null;
}
@Override
public URL getArchiveRoot(final URL url) {
try {
// XXX TBD whether the url should ever be escaped...
return new URL("jar:" + url + "!/"); // NOI18N
} catch (MalformedURLException e) {
throw new AssertionError(e);
}
}
/**
* Tests if a non existent path represents a file.
* @param path to be tested, separated by '/'.
* @return true if the file has '.' after last '/'.
*/
private static boolean isArchiveFile (final String path) {
int index = path.lastIndexOf('.'); //NOI18N
return (index != -1) && (index > path.lastIndexOf('/') + 1); //NOI18N
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy