All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xwiki.classloader.internal.JarExtendedURLStreamHandler Maven / Gradle / Ivy

There is a newer version: 16.10.0-rc-1
Show newest version
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
// In java.net package to be allowed to call URLStreamHandler methods
package org.xwiki.classloader.internal;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.nio.file.Files;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.xwiki.classloader.ExtendedURLStreamHandler;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.manager.ComponentLifecycleException;
import org.xwiki.component.phase.Disposable;
import org.xwiki.component.phase.Initializable;
import org.xwiki.component.phase.InitializationException;
import org.xwiki.environment.Environment;

/**
 * Handler for the "jar" protocol. It handle sub-protocols supported by {@link ExtendedURLStreamHandler} components and
 * redirect everything else to the standard JAR handler.
 * 

* We have to implement our own JAR handler because it's not possible to dynamically add new protocol handlers (in our * case handlers which are implemented as components) that the standard JAR protocol handler can access. * * @version $Id: 0e604523cdef7217282b7d31c3246a439cec8106 $ * @since 12.7 * @since 12.10.10 * @since 13.4.4 */ @Component @Named(JarExtendedURLStreamHandler.PROTOCOL) @Singleton public class JarExtendedURLStreamHandler extends URLStreamHandler implements ExtendedURLStreamHandler, Initializable, Disposable { public static final String PROTOCOL = "jar"; public static final String JARS_FOLDER = "classloader/jars/"; @Inject private URLStreamHandlerFactory handlerFactory; @Inject private Provider environmentProvider; @Inject private Logger logger; private File jarsDirectory; @Override public String getProtocol() { return PROTOCOL; } private URLStreamHandler getURLStreamHandler(String spec) { int index = spec.indexOf(':', PROTOCOL.length() + 1); if (index != -1) { String hint = spec.substring(PROTOCOL.length() + 1, index); return this.handlerFactory.createURLStreamHandler(hint); } return null; } @Override protected URLConnection openConnection(URL u) throws IOException { String spec = u.toExternalForm(); URLStreamHandler subHandler = getURLStreamHandler(spec); // If it's a supported URLStreamHandler created an ExtendedJarURLConnection if (subHandler != null) { return new ExtendedJarURLConnection(u, subHandler, this.jarsDirectory); } // For anything else use standard JAR handler return new URL(null, spec).openConnection(); } @Override public void initialize() throws InitializationException { File temporaryDirectory; try { Environment environment = this.environmentProvider.get(); temporaryDirectory = environment.getTemporaryDirectory(); } catch (Exception e) { try { temporaryDirectory = Files.createTempDirectory("xwiki").toFile(); } catch (IOException e1) { throw new InitializationException(JARS_FOLDER); } } this.jarsDirectory = new File(temporaryDirectory, JARS_FOLDER); } @Override public void dispose() throws ComponentLifecycleException { if (this.jarsDirectory.exists()) { // Get rid of all the temporary jar files in case it's not already the case try { FileUtils.deleteDirectory(this.jarsDirectory); } catch (IOException e) { this.logger.error("Failed to delete folder [{}]", this.jarsDirectory); } } } // @formatter:off ////////////////////////////////////////////////////////////////////////////////////// // Begin copying a few sun.net.www.protocol.jar.Handler methods that we cannot reuse ////////////////////////////////////////////////////////////////////////////////////// private static int indexOfBangSlash(String spec) { int indexOfBang = spec.length(); while((indexOfBang = spec.lastIndexOf('!', indexOfBang)) != -1) { if ((indexOfBang != (spec.length() - 1)) && (spec.charAt(indexOfBang + 1) == '/')) { return indexOfBang + 1; } else { indexOfBang--; } } return -1; } public String checkNestedProtocol(String spec) { if (spec.regionMatches(true, 0, "jar:", 0, 4)) { return "Nested JAR URLs are not supported"; } else { return null; } } @Override @SuppressWarnings("deprecation") protected void parseURL(URL url, String spec, int start, int limit) { String file = null; String ref = null; // first figure out if there is an anchor int refPos = spec.indexOf('#', limit); boolean refOnly = refPos == start; if (refPos > -1) { ref = spec.substring(refPos + 1, spec.length()); if (refOnly) { file = url.getFile(); } } // then figure out if the spec is // 1. absolute (jar:) // 2. relative (i.e. url + foo/bar/baz.ext) // 3. anchor-only (i.e. url + #foo), which we already did (refOnly) boolean absoluteSpec = spec.length() >= 4 ? spec.regionMatches(true, 0, "jar:", 0, 4) : false; spec = spec.substring(start, limit); String exceptionMessage = checkNestedProtocol(spec); if (exceptionMessage != null) { // NPE will be transformed into MalformedURLException by the caller throw new NullPointerException(exceptionMessage); } if (absoluteSpec) { file = parseAbsoluteSpec(spec); } else if (!refOnly) { file = parseContextSpec(url, spec); // Canonize the result after the bangslash int bangSlash = indexOfBangSlash(file); String toBangSlash = file.substring(0, bangSlash); String afterBangSlash = file.substring(bangSlash); afterBangSlash = canonizeString(afterBangSlash); file = toBangSlash + afterBangSlash; } setURL(url, "jar", "", -1, file, ref); } private String parseAbsoluteSpec(String spec) { int index; // check for !/ if ((index = indexOfBangSlash(spec)) == -1) { throw new NullPointerException("no !/ in spec"); } // test the inner URL try { String innerSpec = spec.substring(0, index - 1); new URL(innerSpec); } catch (MalformedURLException e) { throw new NullPointerException("invalid url: " + spec + " (" + e + ")"); } return spec; } private String parseContextSpec(URL url, String spec) { String ctxFile = url.getFile(); // if the spec begins with /, chop up the jar back !/ if (spec.startsWith("/")) { int bangSlash = indexOfBangSlash(ctxFile); if (bangSlash == -1) { throw new NullPointerException("malformed " + "context url:" + url + ": no !/"); } ctxFile = ctxFile.substring(0, bangSlash); } else { // chop up the last component int lastSlash = ctxFile.lastIndexOf('/'); if (lastSlash == -1) { throw new NullPointerException("malformed " + "context url:" + url); } else if (lastSlash < ctxFile.length() - 1) { ctxFile = ctxFile.substring(0, lastSlash + 1); } } return (ctxFile + spec); } /** * Returns a canonical version of the specified string. */ public static String canonizeString(String file) { int len = file.length(); if (len == 0 || (file.indexOf("./") == -1 && file.charAt(len - 1) != '.')) { return file; } else { return doCanonize(file); } } private static String doCanonize(String file) { int i, lim; // Remove embedded /../ while ((i = file.indexOf("/../")) >= 0) { if ((lim = file.lastIndexOf('/', i - 1)) >= 0) { file = file.substring(0, lim) + file.substring(i + 3); } else { file = file.substring(i + 3); } } // Remove embedded /./ while ((i = file.indexOf("/./")) >= 0) { file = file.substring(0, i) + file.substring(i + 2); } // Remove trailing .. while (file.endsWith("/..")) { i = file.indexOf("/.."); if ((lim = file.lastIndexOf('/', i - 1)) >= 0) { file = file.substring(0, lim+1); } else { file = file.substring(0, i); } } // Remove trailing . if (file.endsWith("/.")) file = file.substring(0, file.length() -1); return file; } ////////////////////////////////////////////////////////////////////////////////////// // Finish sun.net.www.protocol.jar.Handler copy ////////////////////////////////////////////////////////////////////////////////////// // @formatter:on }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy