You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by da...@apache.org on 2006/04/12 04:48:10 UTC
svn commit: r393370 [2/2] - in /geronimo/sandbox/classloader: ./ src/
src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/geronimo/
src/java/org/apache/geronimo/kernel/
src/java/org/apache/geronimo/kernel/classloader/ src/java/org/apache/g...
Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/CachedJarFile.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/CachedJarFile.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/CachedJarFile.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/CachedJarFile.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,113 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.apache.geronimo.kernel.classloader.jarcache;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Permission;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class CachedJarFile extends JarFile {
+ private static File DUMMY_JAR_FILE;
+
+ private static synchronized File getDummyJarFile() {
+ if (DUMMY_JAR_FILE == null || !DUMMY_JAR_FILE.isFile() || !DUMMY_JAR_FILE.canRead()) {
+ try {
+ DUMMY_JAR_FILE = File.createTempFile("geronimo-dummy", ".jar");
+ DUMMY_JAR_FILE.deleteOnExit();
+ new JarOutputStream(new FileOutputStream(DUMMY_JAR_FILE), new Manifest()).close();
+ } catch (IOException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+ return DUMMY_JAR_FILE;
+ }
+
+ private final JarFile actualJarFile;
+ private final Permission permission;
+
+ public CachedJarFile(JarFile jarFile, Permission permission) throws IOException {
+ super(getDummyJarFile());
+ this.actualJarFile = jarFile;
+ this.permission = permission;
+ }
+
+ public Manifest getManifest() throws IOException {
+ Manifest original = actualJarFile.getManifest();
+ if (original == null) {
+ return null;
+ }
+
+ // make sure the original manifest is not modified
+ Manifest copy = new Manifest();
+ copy.getMainAttributes().putAll(original.getMainAttributes());
+ for (Iterator itr = original.getEntries().entrySet().iterator(); itr.hasNext();) {
+ Map.Entry entry = (Map.Entry) itr.next();
+ copy.getEntries().put(entry.getKey(), new Attributes((Attributes) entry.getValue()));
+ }
+ return copy;
+ }
+
+ public Permission getPermission() {
+ return permission;
+ }
+
+ public JarFile getActualJarFile() {
+ return actualJarFile;
+ }
+
+ public String getName() {
+ return actualJarFile.getName();
+ }
+
+ public int size() {
+ return actualJarFile.size();
+ }
+
+ public JarEntry getJarEntry(String name) {
+ return actualJarFile.getJarEntry(name);
+ }
+
+ public ZipEntry getEntry(String name) {
+ return actualJarFile.getEntry(name);
+ }
+
+ public Enumeration entries() {
+ return actualJarFile.entries();
+ }
+
+ public InputStream getInputStream(ZipEntry ze) throws IOException {
+ return actualJarFile.getInputStream(ze);
+ }
+
+ public void close() throws IOException {
+ // no op; do NOT close file while still in cache
+ }
+}
Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/CachingJarOpener.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/CachingJarOpener.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/CachingJarOpener.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/CachingJarOpener.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,55 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.apache.geronimo.kernel.classloader.jarcache;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.jar.JarFile;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class CachingJarOpener implements JarOpener {
+ private final JarCache jarCache;
+
+ public CachingJarOpener(JarCache jarCache) {
+ this.jarCache = jarCache;
+ }
+
+ public JarFile openJarFile(java.net.JarURLConnection jarUrlConnection, JarUrlStreamHandler jarUrlStreamHandler) throws IOException {
+ URL baseUrl = getBaseUrl(jarUrlConnection, jarUrlStreamHandler);
+ return jarCache.getJarFile(baseUrl, jarUrlConnection);
+ }
+
+ public static URL getBaseUrl(java.net.JarURLConnection jarUrlConnection, JarUrlStreamHandler jarUrlStreamHandler) throws MalformedURLException {
+ URL url = jarUrlConnection.getURL();
+ String baseText = url.getPath();
+
+ int bangLoc = baseText.lastIndexOf("!");
+ String baseResourceText = baseText.substring(0, bangLoc);
+
+ // if this is a nested jar entry
+ if (baseResourceText.indexOf("!") >= 0) {
+ URL baseResource = new URL("jar", null, -1, baseResourceText, jarUrlStreamHandler);
+ return baseResource;
+ }
+
+ // normal non-nested jar entry
+ return jarUrlConnection.getJarFileURL();
+ }
+}
Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/Handler.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/Handler.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/Handler.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/Handler.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,23 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.apache.geronimo.kernel.classloader.jarcache;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class Handler extends JarUrlStreamHandler {
+}
Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarCache.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarCache.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarCache.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarCache.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,33 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.apache.geronimo.kernel.classloader.jarcache;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.jar.JarFile;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface JarCache {
+ void destroy();
+
+ JarFile getJarFile(URL baseUrl) throws IOException;
+
+ JarFile getJarFile(URL baseUrl, URLConnection urlConnectionProperties) throws IOException;
+}
Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarOpener.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarOpener.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarOpener.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarOpener.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,41 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.apache.geronimo.kernel.classloader.jarcache;
+
+import java.io.IOException;
+import java.util.jar.JarFile;
+
+/**
+ * Abstraction of JAR opener which allows to implement various caching
+ * policies. The opener receives URL pointing to the JAR file, along
+ * with other meta-information, as a JarURLConnection instance. Then it has
+ * to download the file (if it is remote) and open it.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface JarOpener {
+ /**
+ * Given the URL connection (not yet connected), return JarFile
+ * representing the resource. This method is invoked as a part of
+ * the connect method in JarURLConnection.
+ *
+ * @param connection the connection for which the JAR file is required
+ * @return opened JAR file
+ * @throws IOException if I/O error occurs
+ */
+ public JarFile openJarFile(java.net.JarURLConnection connection, JarUrlStreamHandler jarUrlStreamHandler) throws IOException;
+}
Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarUrlConnection.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarUrlConnection.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarUrlConnection.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarUrlConnection.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,104 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.apache.geronimo.kernel.classloader.jarcache;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.Permission;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * Alternative implementation of {@link java.net.JarURLConnection} which
+ * supports a customizable policies for opening the JarFile. This policy can
+ * be used to implement support for nested jars and caching.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JarUrlConnection extends java.net.JarURLConnection {
+ private final JarUrlStreamHandler jarUrlStreamHandler;
+ private boolean connected;
+ private JarFile jarFile;
+ private JarEntry jarEntry;
+ private String entryName;
+
+ /**
+ * Creates JarUrlConnection for a given URL, using specified JAR opener.
+ *
+ * @param url the URL to open connection to
+ */
+ public JarUrlConnection(URL url, JarUrlStreamHandler jarUrlStreamHandler) throws IOException {
+ super(url);
+ this.jarUrlStreamHandler = jarUrlStreamHandler;
+ }
+
+ public synchronized void connect() throws IOException {
+ if (connected) return;
+
+ JarOpener opener = jarUrlStreamHandler.getOpener();
+ jarFile = opener.openJarFile(this, jarUrlStreamHandler);
+ if (jarFile != null) {
+ URL url = getURL();
+ String baseText = url.getPath();
+
+ int bangLoc = baseText.lastIndexOf("!");
+ if (bangLoc <= (baseText.length() - 2) && baseText.charAt(bangLoc + 1) == '/') {
+ if (bangLoc + 2 == baseText.length()) {
+ entryName = null;
+ } else {
+ entryName = baseText.substring(bangLoc + 2);
+ }
+ } else {
+ throw new MalformedURLException("No !/ in url: " + url.toExternalForm());
+ }
+
+ if (entryName != null) {
+ jarEntry = jarFile.getJarEntry(entryName);
+ if (jarEntry == null) {
+ throw new FileNotFoundException("Entry " + entryName + " not found in " + jarFile.getName());
+ }
+ }
+ }
+ connected = true;
+ }
+
+ public synchronized String getEntryName() {
+ return entryName;
+ }
+
+ public synchronized JarFile getJarFile() throws IOException {
+ connect();
+ return jarFile;
+ }
+
+ public synchronized JarEntry getJarEntry() throws IOException {
+ connect();
+ return jarEntry;
+ }
+
+ public synchronized InputStream getInputStream() throws IOException {
+ connect();
+ return jarFile.getInputStream(jarEntry);
+ }
+
+ public Permission getPermission() throws IOException {
+ return getJarFileURL().openConnection().getPermission();
+ }
+}
Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarUrlStreamHandler.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarUrlStreamHandler.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarUrlStreamHandler.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarUrlStreamHandler.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,114 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.apache.geronimo.kernel.classloader.jarcache;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+/**
+ * Alternative implementation of URLStreamHandler for JAR files which
+ * supports a customizable policies for opening the JarFile. This policy can
+ * be used to implement support for nested jars and caching.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JarUrlStreamHandler extends URLStreamHandler {
+ private final JarOpener opener;
+
+ /**
+ * Create new JarUrlStreamHandler that will use its separate URL cache
+ * managed by a newly created {@link CachingJarOpener} instance.
+ */
+ public JarUrlStreamHandler() {
+ this(new CachingJarOpener(new TempJarCache()));
+ }
+
+ /**
+ * Create new JarUrlStreamHandler that will use specified
+ * JarOpener.
+ *
+ * @param opener JAR opener that handles file download and caching
+ */
+ public JarUrlStreamHandler(JarOpener opener) {
+ this.opener = opener;
+ }
+
+ public JarOpener getOpener() {
+ return opener;
+ }
+
+ public URLConnection openConnection(URL url) throws IOException {
+ try {
+ String urlPath = url.getPath();
+ if (urlPath.startsWith("jar:")) {
+ urlPath = urlPath.substring("jar:".length());
+ setURL(url, "jar", "", -1, null, null, urlPath, null, null);
+ }
+ return new JarUrlConnection(url, this);
+ } catch (IOException e) {
+ throw e;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ protected void parseURL(URL url, String spec, int start, int limit) {
+ String specPath = spec.substring(start, limit);
+
+ String urlPath = null;
+
+ if (specPath.charAt(0) == '/') {
+ urlPath = specPath;
+ } else if (specPath.charAt(0) == '!') {
+ String relPath = url.getFile();
+
+ int bangLoc = relPath.lastIndexOf("!");
+
+ if (bangLoc < 0) {
+ urlPath = relPath + specPath;
+ } else {
+ urlPath = relPath.substring(0,
+ bangLoc) + specPath;
+ }
+ } else {
+ String relPath = url.getFile();
+
+ if (relPath != null) {
+ int lastSlashLoc = relPath.lastIndexOf("/");
+
+ if (lastSlashLoc < 0) {
+ urlPath = "/" + specPath;
+ } else {
+ urlPath = relPath.substring(0, lastSlashLoc + 1) + specPath;
+ }
+ } else {
+ urlPath = specPath;
+ }
+ }
+
+ if (urlPath.startsWith("jar:")) {
+ urlPath = urlPath.substring("jar:".length());
+ }
+ setURL(url, "jar", "", -1, null, null, urlPath, null, null);
+ }
+}
Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/TempJarCache.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/TempJarCache.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/TempJarCache.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/TempJarCache.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,272 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.apache.geronimo.kernel.classloader.jarcache;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.jar.JarFile;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class TempJarCache implements JarCache {
+ private final LinkedHashMap cache = new LinkedHashMap();
+ private final ReferenceQueue referenceQueue = new ReferenceQueue();
+ private final File cacheDir;
+
+ public TempJarCache() {
+ String tmpdir = System.getProperty("org.apache.geronimo.tmpdir", System.getProperty("java.io.tmpdir"));
+ this.cacheDir = new File(new File(tmpdir, "geronimo"), "urlcache");
+ verifyCacheDirectory();
+ }
+
+ public TempJarCache(File cacheDir) {
+ this.cacheDir = cacheDir;
+ verifyCacheDirectory();
+ }
+
+ protected void verifyCacheDirectory() {
+ cacheDir.mkdirs();
+ if (!cacheDir.exists()) {
+ throw new IllegalArgumentException("Could not create jar cache directory: " + cacheDir.getAbsolutePath());
+ }
+ if (!cacheDir.isDirectory()) {
+ throw new IllegalArgumentException("Cache directory is not a directory: " + cacheDir.getAbsolutePath());
+ }
+ }
+
+ public void destroy() {
+ Map cache;
+ synchronized (this.cache) {
+ cache = new HashMap(this.cache);
+ this.cache.clear();
+ }
+
+ for (Iterator i = cache.values().iterator(); i.hasNext();) {
+ WeakReference weakReference = (WeakReference) i.next();
+ weakReference.clear();
+ }
+ }
+
+ public JarFile getJarFile(URL baseUrl) throws IOException {
+ return getJarFile(baseUrl, null);
+ }
+
+ public JarFile getJarFile(URL baseUrl, URLConnection urlConnectionProperties) throws IOException {
+ // first you get to do some cleaning
+ reapFiles();
+
+ // first check the cache
+ CachedJarFile cachedJarFile = null;
+ synchronized (cache) {
+ WeakReference reference = (WeakReference) cache.get(baseUrl);
+ if (reference != null) {
+ cachedJarFile = (CachedJarFile) reference.get();
+ }
+ }
+ if (cachedJarFile != null) {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkPermission(cachedJarFile.getPermission());
+ }
+ return cachedJarFile;
+ }
+
+ // check if the baseUrl is a jar on the local file system
+ try {
+ URI uri = new URI(baseUrl.toString());
+ if (isLocalFile(uri)) {
+ File file = new File(uri);
+ Permission perm = new FilePermission(file.getAbsolutePath(), "read");
+ cachedJarFile = new CachedJarFile(new JarFile(file), perm);
+ }
+ }
+ catch (URISyntaxException e) {
+ // apparently not a local file
+ }
+
+ // if not a local jar, download to the cache
+ if (cachedJarFile == null) {
+ URLConnection urlConnection = baseUrl.openConnection();
+
+ if (urlConnectionProperties != null) {
+ // set up the properties based on the JarURLConnection
+ urlConnection.setAllowUserInteraction(urlConnectionProperties.getAllowUserInteraction());
+ urlConnection.setDoInput(urlConnectionProperties.getDoInput());
+ urlConnection.setDoOutput(urlConnectionProperties.getDoOutput());
+ urlConnection.setIfModifiedSince(urlConnectionProperties.getIfModifiedSince());
+
+ Map map = urlConnectionProperties.getRequestProperties();
+ for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) {
+ Map.Entry entry = (Map.Entry) itr.next();
+ urlConnection.setRequestProperty((String) entry.getKey(), (String) entry.getValue());
+ }
+
+ urlConnection.setUseCaches(urlConnectionProperties.getUseCaches());
+ }
+
+ cachedJarFile = cacheFile(urlConnection);
+ }
+
+ // if no input came (e.g. due to NOT_MODIFIED), do not cache
+ if (cachedJarFile == null) return null;
+
+ // optimistic locking
+ synchronized (cache) {
+ WeakReference reference = (WeakReference) cache.get(baseUrl);
+ if (reference != null) {
+ CachedJarFile asyncResult = (CachedJarFile) reference.get();
+ if (asyncResult != null) {
+ // some other thread already retrieved the file; return w/o
+ // security check since we already succeeded in getting past it
+ cachedJarFile.getActualJarFile().close();
+ return asyncResult;
+ }
+ }
+ cache.put(baseUrl, new WeakReference(cachedJarFile, referenceQueue));
+ return cachedJarFile;
+ }
+ }
+
+ public void reapFiles() {
+ for (Reference reference = referenceQueue.poll(); reference != null; reference = referenceQueue.poll()) {
+ reference.clear();
+ }
+ }
+
+ private CachedJarFile cacheFile(final URLConnection urlConnection) throws IOException {
+ CachedJarFile cachedJarFile = null;
+ final InputStream in = urlConnection.getInputStream();
+
+ try {
+ cachedJarFile = (CachedJarFile) AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ public Object run() throws IOException {
+ File file = File.createTempFile("jar_cache", "", cacheDir);
+ FileOutputStream out = null;
+ try {
+ out = new FileOutputStream(file);
+ writeAll(in, out);
+ safeClose(out);
+ out = null;
+
+ JarFile jarFile = new JarFile(file, true, JarFile.OPEN_READ | JarFile.OPEN_DELETE);
+ return new CachedJarFile(jarFile, urlConnection.getPermission());
+ } catch (IOException e) {
+ safeClose(out);
+ file.delete();
+ throw e;
+ }
+ }
+ });
+ }
+ catch (PrivilegedActionException pae) {
+ throw (IOException) pae.getException();
+ } finally {
+ safeClose(in);
+ }
+ return cachedJarFile;
+ }
+
+ public class CacheJarFileReference extends WeakReference {
+ private final JarFile actualJarFile;
+
+ public CacheJarFileReference(CachedJarFile cachedJarFile, ReferenceQueue q) {
+ super(cachedJarFile, q);
+ actualJarFile = cachedJarFile.getActualJarFile();
+ }
+
+ public void clear() {
+ try {
+ actualJarFile.close();
+ } catch (IOException ignroed) {
+ }
+ super.clear();
+ }
+
+ public JarFile getActualJarFile() {
+ return actualJarFile;
+ }
+ }
+
+ private static void safeClose(OutputStream thing) {
+ if (thing != null) {
+ try {
+ thing.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ private static void safeClose(InputStream thing) {
+ if (thing != null) {
+ try {
+ thing.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ private static void writeAll(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[4096];
+ int count;
+ while ((count = in.read(buffer)) > 0) {
+ out.write(buffer, 0, count);
+ }
+ out.flush();
+ }
+
+ private static boolean isLocalFile(URI uri) {
+ if (!uri.isAbsolute()) {
+ return false;
+ }
+ if (uri.isOpaque()) {
+ return false;
+ }
+ if (!"file".equalsIgnoreCase(uri.getScheme())) {
+ return false;
+ }
+ if (uri.getAuthority() != null) {
+ return false;
+ }
+ if (uri.getFragment() != null) {
+ return false;
+ }
+ if (uri.getQuery() != null) {
+ return false;
+ }
+ return uri.getPath().length() > 0;
+ }
+}
Added: geronimo/sandbox/classloader/src/test/org/apache/geronimo/kernel/classloader/jar/JarUrlTest.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/test/org/apache/geronimo/kernel/classloader/jar/JarUrlTest.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/test/org/apache/geronimo/kernel/classloader/jar/JarUrlTest.java (added)
+++ geronimo/sandbox/classloader/src/test/org/apache/geronimo/kernel/classloader/jar/JarUrlTest.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,147 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.apache.geronimo.kernel.classloader.jar;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.zip.ZipEntry;
+
+import junit.framework.TestCase;
+import org.apache.geronimo.kernel.classloader.jarcache.CachingJarOpener;
+import org.apache.geronimo.kernel.classloader.jarcache.JarOpener;
+import org.apache.geronimo.kernel.classloader.jarcache.JarUrlConnection;
+import org.apache.geronimo.kernel.classloader.jarcache.JarUrlStreamHandler;
+import org.apache.geronimo.kernel.classloader.jarcache.TempJarCache;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class JarUrlTest extends TestCase {
+ private static final String TEXT = "This is a test";
+ private JarOpener jarOpener;
+ private File outerFile;
+ private File nestedFile;
+ private File cacheDir;
+ private File doubleNestedFile;
+
+ public void testUnnestedEntry() throws Exception {
+ URL url = new URL("jar", null, -1, outerFile.toURL().toString() + "!/foo", new JarUrlStreamHandler(jarOpener));
+ URLConnection urlConnection = url.openConnection();
+ assertTrue(urlConnection instanceof JarUrlConnection);
+
+ String text = readLine(urlConnection);
+
+ assertEquals(TEXT + "-foo", text);
+ }
+
+ public void testNestedEntry() throws Exception {
+ URL url = new URL("jar", null, -1, outerFile.toURL().toString() + "!/nested.jar!/bar", new JarUrlStreamHandler(jarOpener));
+ URLConnection urlConnection = url.openConnection();
+ assertTrue(urlConnection instanceof JarUrlConnection);
+
+ String text = readLine(urlConnection);
+
+ assertEquals(TEXT + "-bar", text);
+ }
+
+ public void testDoubleNestedEntry() throws Exception {
+ URL url = new URL("jar", null, -1, outerFile.toURL().toString() + "!/nested.jar!/doubleNested.jar!/baz", new JarUrlStreamHandler(jarOpener));
+ URLConnection urlConnection = url.openConnection();
+ assertTrue(urlConnection instanceof JarUrlConnection);
+
+ String text = readLine(urlConnection);
+
+ assertEquals(TEXT + "-baz", text);
+ }
+
+ private String readLine(URLConnection urlConnection) throws IOException {
+ urlConnection.connect();
+ InputStream in = urlConnection.getInputStream();
+ LineNumberReader reader = new LineNumberReader(new InputStreamReader(in));
+ String text = reader.readLine();
+ in.close();
+ return text;
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ cacheDir = new File("target/jarcache");
+ jarOpener = new CachingJarOpener(new TempJarCache(cacheDir));
+
+ doubleNestedFile = new File("target/doubleNested.jar");
+ JarFile doubleNestedJarFile = createJarFile(doubleNestedFile, "baz", null);
+ assertNotNull(doubleNestedJarFile.getEntry("baz"));
+
+ nestedFile = new File("target/nested.jar");
+ JarFile nestedJarFile = createJarFile(nestedFile, "bar", doubleNestedFile);
+ assertNotNull(nestedJarFile.getEntry("bar"));
+ assertNotNull(nestedJarFile.getEntry("doubleNested.jar"));
+
+ outerFile = new File("target/outer.jar");
+ JarFile outterJarFile = createJarFile(outerFile, "foo", nestedFile);
+ assertNotNull(outterJarFile.getEntry("foo"));
+ assertNotNull(outterJarFile.getEntry("nested.jar"));
+ }
+
+ private JarFile createJarFile(File file, String name, File nestedFile) throws IOException {
+ JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(file));
+ jarOutputStream.putNextEntry(new ZipEntry(name));
+ jarOutputStream.write((TEXT + "-" + name).getBytes());
+ jarOutputStream.closeEntry();
+
+ if (nestedFile != null) {
+ jarOutputStream.putNextEntry(new ZipEntry(nestedFile.getName()));
+ FileInputStream in = new FileInputStream(nestedFile);
+ writeAll(in, jarOutputStream);
+ in.close();
+ jarOutputStream.closeEntry();
+ }
+
+ jarOutputStream.flush();
+ jarOutputStream.close();
+
+ return new JarFile(file);
+ }
+
+ private static void writeAll(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[4096];
+ int count;
+ while ((count = in.read(buffer)) > 0) {
+ out.write(buffer, 0, count);
+ }
+ out.flush();
+ }
+
+ protected void tearDown() throws Exception {
+ doubleNestedFile.delete();
+ nestedFile.delete();
+ outerFile.delete();
+ cacheDir.delete();
+ super.tearDown();
+ }
+}