/* * Copyright 2013 eXo Platform SAS * * 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 juzu.impl.fs.spi.url; import juzu.impl.common.Resource; import juzu.impl.common.Timestamped; import juzu.impl.common.Tools; import juzu.impl.fs.spi.PathType; import juzu.impl.fs.spi.ReadFileSystem; import javax.inject.Inject; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.nio.charset.Charset; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */ public class URLFileSystem extends ReadFileSystem<Node> { /** . */ private final Node root; public URLFileSystem() { this.root = new Node(); } /** * Add the resources from the specified classloader only. * * @param loader the loader * @return this url file system * @throws IOException any io exception * @throws URISyntaxException any uri syntax exception */ public URLFileSystem add(ClassLoader loader) throws IOException, URISyntaxException { return add(loader, loader.getParent()); } /** * Add the resources from the <code>from</code> classloader up to the <code>to</code> classloader * that is excluded. * * @param from the classloader from which resources are included * @param to the classloader from which resources are excluded * @return this url file system * @throws IOException any io exception * @throws URISyntaxException any uri syntax exception */ public URLFileSystem add(ClassLoader from, ClassLoader to) throws IOException, URISyntaxException { // Get file urls from loader HashSet<URL> urls = Tools.set(from.getResources("")); // Get jar urls from loader for (Enumeration<URL> e = from.getResources("META-INF/MANIFEST.MF"); e.hasMoreElements();) { URL url = e.nextElement(); if ("jar".equals(url.getProtocol())) { urls.add(url); } } // Remove URLs from extension classloader and above (bootstrap) if (to != null) { // Remove file urls we don't need for (Enumeration<URL> e = to.getResources("");e.hasMoreElements();) { urls.remove(e.nextElement()); } // Remove jar urls we don't need for (Enumeration<URL> e = to.getResources("META-INF/MANIFEST.MF"); e.hasMoreElements();) { URL url = e.nextElement(); if ("jar".equals(url.getProtocol())) { urls.remove(url); } } } else { } // Add manually this one (fucked up jar: no META-INF/MANIFEST.MF) ClassLoader injectCL = Inject.class.getClassLoader(); URL injectURL = from.getResource(Inject.class.getName().replace('.', '/') + ".class"); if (injectURL != null) { if (to != null) { for (ClassLoader current = from;current != to;current = current.getParent()) { if (current == injectCL) { urls.add(injectURL); break; } } } else { urls.add(injectURL); } } // Now handle urls for (URL url : urls) { if (url.getProtocol().equals("jar")) { // Correct URL to get root and not manifest String s = url.toString(); int pos = s.lastIndexOf("!/"); add(new URL(s.substring(0, pos + 2))); } else { add(url); } } // return this; } public URLFileSystem add(URL url) throws IOException, URISyntaxException { String protocol = url.getProtocol(); if ("file".equals(protocol)) { File file = new File(url.toURI()); if (file.isDirectory()) { root.merge(file); } else { JarFile jar = new JarFile(file, false); for (JarEntry entry : Tools.iterable(jar.entries())) { root.merge("jar:" + url + "!/", entry.getName()); } } } else if ("jar".equals(protocol)) { String path = url.getPath(); int pos = path.lastIndexOf("!/"); if (pos == -1) { throw new MalformedURLException("Malformed URL " + url); } URL inner = new URL(path.substring(0, pos)); if (inner.getProtocol().equals("file")) { File file = new File(inner.toURI()); if (file.isDirectory()) { throw new IllegalArgumentException("Wrong jar URL " + url); } else { String prefix = path.substring(pos + 2); if (prefix.length() > 0 && !prefix.endsWith("/")) { throw new IllegalArgumentException("Wrong nested jar URL, should end with a / or be empty" + url); } JarFile jar = new JarFile(file, false); for (JarEntry entry : Tools.iterable(jar.entries())) { String name = entry.getName(); if (name.startsWith(prefix)) { root.merge("jar:" + inner + "!/" + prefix, name.substring(prefix.length())); } } } } else { throw new UnsupportedOperationException("Not yet supported"); } } else { throw new UnsupportedOperationException("Cannot handle url " + url + " yet"); } return this; } @Override public Class<Node> getType() { return null; } @Override public String getDescription() { return "URLFileSystem[]"; } @Override public boolean equals(Node left, Node right) { return left == right; } @Override public Node getRoot() throws IOException { return root; } @Override public Node getChild(Node dir, String name) throws IOException { return dir.get(name); } @Override public long getLastModified(Node path) throws IOException { return 1; } @Override public String getName(Node path) { return path == root ? "" : path.getKey(); } @Override public Iterable<String> getNames(Node path) { return path.getPath(); } @Override public Iterator<Node> getChildren(Node dir) throws IOException { return dir.getEntries(); /* return new Iterator<Node>() { */ /** . *//* private Node next; public boolean hasNext() { while (next == null && entries.hasNext()) { Node next = entries.next(); this.next = next; } return next != null; } public Node next() { if (!hasNext()) { throw new NoSuchElementException(); } try { return next; } finally { next = null; } } public void remove() { throw new UnsupportedOperationException(); } }; */ } @Override public PathType typeOf(Node path) throws IOException { return path.url == null ? PathType.DIR : PathType.FILE; } @Override public Timestamped<Resource> getResource(Node file) throws IOException { if (file.url == null) { throw new IOException("Cannot find file " + file.getPath()); } // URLConnection conn = file.url.openConnection(); long lastModified = conn.getLastModified(); byte[] bytes = Tools.bytes(conn.getInputStream()); return new Timestamped<Resource>(lastModified, new Resource(bytes, Charset.defaultCharset())); } @Override public File getFile(Node path){ return null; } @Override public URL getURL(Node path) throws NullPointerException, IOException { return path.url; } }