001/* 002 * (C) Copyright 2006-2018 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 */ 019package org.nuxeo.runtime.tomcat; 020 021import java.io.File; 022import java.io.IOException; 023import java.lang.reflect.Method; 024import java.net.MalformedURLException; 025import java.net.URL; 026import java.net.URLClassLoader; 027import java.nio.file.FileSystems; 028import java.nio.file.Files; 029import java.nio.file.Path; 030import java.nio.file.PathMatcher; 031import java.util.ArrayList; 032import java.util.List; 033import java.util.stream.Stream; 034 035import org.apache.catalina.Container; 036import org.apache.catalina.Lifecycle; 037import org.apache.catalina.LifecycleEvent; 038import org.apache.catalina.LifecycleListener; 039import org.apache.catalina.LifecycleState; 040import org.apache.catalina.core.ContainerBase; 041import org.apache.catalina.core.StandardHost; 042import org.nuxeo.osgi.application.FrameworkBootstrap; 043 044/** 045 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a> 046 */ 047public class NuxeoDeployer implements LifecycleListener { 048 049 protected String home = "nxserver"; 050 051 protected FrameworkBootstrap bootstrap; 052 053 public void setHome(String home) { 054 this.home = home; 055 } 056 057 public String getHome() { 058 return home; 059 } 060 061 @Override 062 public void lifecycleEvent(LifecycleEvent event) { 063 Lifecycle lifecycle = event.getLifecycle(); 064 String type = event.getType(); 065 066 if (lifecycle instanceof Container && Lifecycle.BEFORE_START_EVENT.equals(type)) { 067 Container container = (Container) lifecycle; 068 preprocess(container); 069 } else if (lifecycle instanceof StandardHost && Lifecycle.AFTER_START_EVENT.equals(type)) { 070 StandardHost container = (StandardHost) lifecycle; 071 checkFailures(container); 072 } 073 } 074 075 protected void checkFailures(StandardHost host) { 076 boolean ok = Stream.of(host.findChildren()).map(Lifecycle::getState).allMatch(s -> s == LifecycleState.STARTED); 077 boolean strict = Boolean.getBoolean("nuxeo.start.strict"); 078 079 if (!ok && strict) { 080 // Throws an exception and let Tomcat to handle it via Catalina's shutdown hook. Otherwise, we could call 081 // `#stop()` and `#destroy()` by ourselves, but that means we're making a shutdown in AFTER_STARTED 082 // lifecycle event. It would be misleading. 083 throw new IllegalStateException("Some contexts failed to start."); 084 } 085 } 086 protected void preprocess(Container container) { 087 try { 088 ClassLoader parentCl = container.getParentClassLoader(); 089 File homeDir = resolveHomeDirectory(); 090 File bundles = new File(homeDir, "bundles"); 091 File lib = new File(homeDir, "lib"); 092 File deployerJar = FrameworkBootstrap.findFileStartingWidth(bundles, "nuxeo-runtime-deploy"); 093 File commonJar = FrameworkBootstrap.findFileStartingWidth(bundles, "nuxeo-common"); 094 if (deployerJar == null || commonJar == null) { 095 System.out.println("Deployer and/or common JAR (nuxeo-runtime-deploy* | nuxeo-common*) not found in " 096 + bundles); 097 return; 098 } 099 List<URL> urls = new ArrayList<>(); 100 PathMatcher jar = FileSystems.getDefault().getPathMatcher("glob:**.jar"); 101 try (Stream<Path> stream = Files.list(lib.toPath())) { 102 stream.filter(jar::matches).map(this::toURL).forEach(urls::add); 103 } 104 try (Stream<Path> stream = Files.list(bundles.toPath())) { 105 stream.filter(jar::matches).map(this::toURL).forEach(urls::add); 106 } 107 urls.add(homeDir.toURI().toURL()); 108 urls.add(new File(homeDir, "config").toURI().toURL()); 109 try (URLClassLoader cl = new URLClassLoader(urls.toArray(new URL[0]), parentCl)) { 110 System.out.println("# Running Nuxeo Preprocessor ..."); 111 Class<?> klass = cl.loadClass("org.nuxeo.runtime.deployment.preprocessor.DeploymentPreprocessor"); 112 Method main = klass.getMethod("main", String[].class); 113 main.invoke(null, new Object[] { new String[] { homeDir.getAbsolutePath() } }); 114 System.out.println("# Preprocessing done."); 115 } 116 } catch (IOException | ReflectiveOperationException | IllegalStateException e) { 117 throw new RuntimeException("Failed to handle event", e); 118 } 119 } 120 121 protected URL toURL(Path p) { 122 try { 123 return p.toUri().toURL(); 124 } catch (MalformedURLException e) { 125 throw new IllegalStateException(e); 126 } 127 } 128 129 protected File resolveHomeDirectory() { 130 String path; 131 if (home.startsWith("/")) { 132 path = home; 133 } else { 134 path = getTomcatHome() + '/' + home; 135 } 136 return new File(path); 137 } 138 139 public String getTomcatHome() { 140 String tomcatHome = System.getProperty("catalina.base"); 141 if (tomcatHome == null) { 142 tomcatHome = System.getProperty("catalina.home"); 143 } 144 return tomcatHome; 145 } 146 147 /** 148 * @deprecated Since 10.1, use {@link #preprocess(Container)} instead. 149 */ 150 @Deprecated 151 protected void handleEvent(ContainerBase container, LifecycleEvent event) { 152 if (Lifecycle.BEFORE_START_EVENT.equals(event.getType())) { 153 preprocess(container); 154 } 155 } 156 157}