/* * eXist Open Source Native XML Database * Copyright (C) 2012-2015 The eXist-db Project * http://exist-db.org * * This program 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 * of the License, or (at your option) any later version. * * This program 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 program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ package org.exist.repo; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.exist.SystemProperties; import org.exist.start.Classpath; import org.exist.start.EXistClassLoader; import org.exist.storage.BrokerPool; import org.exist.storage.BrokerPoolService; import org.exist.storage.BrokerPoolServiceException; import org.expath.pkg.repo.*; import org.expath.pkg.repo.Package; import org.expath.pkg.repo.deps.ProcessorDependency; /** * Helper class to construct classpath for expath modules containing * jar files. Part of start.jar */ public class ClasspathHelper implements BrokerPoolService { private final static Logger LOG = LogManager.getLogger(ClasspathHelper.class); // if no eXist version is specified in the expath-pkg.xml, we assume it is 2.2 or older private final static PackageLoader.Version DEFAULT_VERSION = new PackageLoader.Version("1.4.0", "2.2.1"); @Override public void prepare(final BrokerPool brokerPool) throws BrokerPoolServiceException { final ClassLoader loader = brokerPool.getClassLoader(); if (!(loader instanceof EXistClassLoader)) { return; } final Classpath cp = new Classpath(); scanPackages(brokerPool, cp); ((EXistClassLoader)loader).addURLs(cp); } public static void updateClasspath(BrokerPool pool, org.expath.pkg.repo.Package pkg) throws PackageException { final ClassLoader loader = pool.getClassLoader(); if (!(loader instanceof EXistClassLoader)) {return;} if (!isCompatible(pkg)) { LOG.warn("Package " + pkg.getName() + " is not compatible with this version of eXist. " + "To avoid conflicts, Java libraries shipping with this package are not loaded."); return; } final FileSystemStorage.FileSystemResolver resolver = (FileSystemStorage.FileSystemResolver) pkg.getResolver(); final File packageDir = resolver.resolveResourceAsFile("."); final Classpath cp = new Classpath(); try { scanPackageDir(cp, packageDir); ((EXistClassLoader)loader).addURLs(cp); } catch (final IOException e) { LOG.warn("An error occurred while updating classpath for package " + pkg.getName(), e); } } private static void scanPackages(BrokerPool pool, Classpath classpath) { try { final Optional<ExistRepository> repo = pool.getExpathRepo(); if (repo.isPresent()) { for (final Packages pkgs : repo.get().getParentRepo().listPackages()) { final Package pkg = pkgs.latest(); if (!isCompatible(pkg)) { LOG.warn("Package " + pkg.getName() + " is not compatible with this version of eXist. " + "To avoid conflicts, Java libraries shipping with this package are not loaded."); } else { try { final FileSystemStorage.FileSystemResolver resolver = (FileSystemStorage.FileSystemResolver) pkg.getResolver(); final File packageDir = resolver.resolveResourceAsFile("."); scanPackageDir(classpath, packageDir); } catch (final IOException e) { LOG.warn("An error occurred while updating classpath for package " + pkg.getName(), e); } } } } } catch (final Exception e) { LOG.warn("An error occurred while updating classpath for packages", e); } } private static boolean isCompatible(Package pkg) throws PackageException { // determine the eXistdb version this package is compatible with final Collection<ProcessorDependency> processorDeps = pkg.getProcessorDeps(); final String procVersion = SystemProperties.getInstance().getSystemProperty("product-version", "1.0"); PackageLoader.Version processorVersion = DEFAULT_VERSION; for (ProcessorDependency dependency: processorDeps) { if (Deployment.PROCESSOR_NAME.equals(dependency.getProcessor())) { if (dependency.getSemver() != null) { processorVersion = new PackageLoader.Version(dependency.getSemver(), true); } else if (dependency.getSemverMax() != null || dependency.getSemverMin() != null) { processorVersion = new PackageLoader.Version(dependency.getSemverMin(), dependency.getSemverMax()); } else if (dependency.getVersions() != null) { processorVersion = new PackageLoader.Version(dependency.getVersions(), false); } break; } } return processorVersion.getDependencyVersion().isCompatible(procVersion); } private static void scanPackageDir(Classpath classpath, File module) throws IOException { final Path dotExist = module.toPath().resolve(".exist"); if (Files.exists(dotExist)) { if (!Files.isDirectory(dotExist)) { throw new IOException("The .exist config dir is not a dir: " + dotExist); } final Path cp = dotExist.resolve("classpath.txt"); if (Files.exists(cp)) { try (final BufferedReader reader = Files.newBufferedReader(cp)) { String line; while ((line = reader.readLine()) != null) { classpath.addComponent(line); } } } } } }