/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.tomee.catalina;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.core.StandardServer;
import org.apache.catalina.util.ServerInfo;
import org.apache.openejb.classloader.ClassLoaderConfigurer;
import org.apache.openejb.config.QuickJarsTxtParser;
import org.apache.openejb.loader.IO;
import org.apache.openejb.loader.ProvisioningUtil;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.util.OpenEjbVersion;
import org.apache.tomee.loader.TomcatHelper;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.util.Arrays.asList;
// this listener is the real tomee one (the OpenEJBListener is more tomcat oriented)
// so it even changes the server info
public class ServerListener implements LifecycleListener {
private static final Logger LOGGER = Logger.getLogger(ServerListener.class.getName());
private static final AtomicBoolean listenerInstalled = new AtomicBoolean(false);
public void lifecycleEvent(final LifecycleEvent event) {
if (Lifecycle.BEFORE_INIT_EVENT.equals(event.getType()) && StandardServer.class.isInstance(event.getSource())) {
installServerInfo();
}
synchronized (listenerInstalled) {
// only install once
if (listenerInstalled.get() || !Lifecycle.AFTER_INIT_EVENT.equals(event.getType())) {
return;
}
if (!(event.getSource() instanceof StandardServer)) {
return;
}
try {
final StandardServer server = (StandardServer) event.getSource();
TomcatHelper.setServer(server);
final Properties properties = new Properties();
System.getProperties().setProperty("openejb.embedder.source", getClass().getSimpleName());
properties.setProperty("openejb.embedder.source", getClass().getSimpleName());
// if SystemInstance is already initialized, then return
if (SystemInstance.isInitialized()) {
return;
}
// set the openejb.loader property to tomcat-system
properties.setProperty("openejb.loader", "tomcat-system");
// Get the value of catalina.home and set it to openejb.home
final String catalinaHome = System.getProperty("catalina.home");
properties.setProperty("openejb.home", catalinaHome);
//Sets system property for openejb.home
System.setProperty("openejb.home", catalinaHome);
//get the value of catalina.base and set it to openejb.base
final String catalinaBase = System.getProperty("catalina.base");
properties.setProperty("openejb.base", catalinaBase);
//Sets system property for openejb.base
System.setProperty("openejb.base", catalinaBase);
// System.setProperty("tomcat.version", "x.y.z.w");
// System.setProperty("tomcat.built", "mmm dd yyyy hh:mm:ss");
// set the System properties, tomcat.version, tomcat.built
final ClassLoader classLoader = ServerListener.class.getClassLoader();
try {
final Properties tomcatServerInfo = IO.readProperties(classLoader.getResourceAsStream("org/apache/catalina/util/ServerInfo.properties"), new Properties());
String serverNumber = tomcatServerInfo.getProperty("server.number");
if (serverNumber == null) {
// Tomcat5 only has server.info
final String serverInfo = tomcatServerInfo.getProperty("server.info");
if (serverInfo != null) {
final int slash = serverInfo.indexOf('/');
serverNumber = serverInfo.substring(slash + 1);
}
}
if (serverNumber != null) {
System.setProperty("tomcat.version", serverNumber);
}
final String serverBuilt = tomcatServerInfo.getProperty("server.built");
if (serverBuilt != null) {
System.setProperty("tomcat.built", serverBuilt);
}
} catch (final Throwable e) {
// no-op
}
final TomcatLoader loader = new TomcatLoader();
loader.initSystemInstance(properties);
// manage additional libraries
try {
final Collection<URL> files = new ArrayList<>();
for (final File f : ProvisioningUtil.addAdditionalLibraries()) {
files.add(f.toURI().toURL());
}
final ClassLoaderConfigurer configurer = QuickJarsTxtParser.parse(SystemInstance.get().getConf(QuickJarsTxtParser.FILE_NAME));
if (configurer != null) {
files.addAll(asList(configurer.additionalURLs()));
}
if (!files.isEmpty() && URLClassLoader.class.isInstance(classLoader)) {
final URLClassLoader ucl = URLClassLoader.class.cast(classLoader);
try {
final Method addUrl = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
final boolean acc = addUrl.isAccessible();
try {
for (final URL url : files) {
addUrl(ucl, addUrl, url);
}
} finally {
addUrl.setAccessible(acc);
}
} catch (final Exception e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
}
} catch (final IOException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
}
loader.initialize(properties);
listenerInstalled.set(true);
} catch (final Exception e) {
LOGGER.log(Level.SEVERE, "TomEE Listener can't start OpenEJB", e);
// e.printStackTrace(System.err);
}
}
}
private static void addUrl(final URLClassLoader ucl, final Method addUrl, final URL url) throws IllegalAccessException, InvocationTargetException, MalformedURLException {
if (!addUrl.isAccessible()) { // set it lazily
addUrl.setAccessible(true);
}
addUrl.invoke(ucl, url);
}
private synchronized void installServerInfo() {
if (SystemInstance.get().getOptions().get("tomee.keep-server-info", false)) {
return;
}
// force static init
final String value = ServerInfo.getServerInfo();
Field field = null;
boolean acc = true;
try {
field = ServerInfo.class.getDeclaredField("serverInfo");
acc = field.isAccessible();
final int slash = value.indexOf('/');
field.setAccessible(true);
final String tomeeVersion = OpenEjbVersion.get().getVersion();
final int modifiers = field.getModifiers();
if (Modifier.isFinal(modifiers)) { // this is a bit fragile, we can surely drop this feature at some point
final Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, modifiers & ~Modifier.FINAL);
}
field.set(null, value.substring(0, slash) + " (TomEE)" + value.substring(slash) + " (" + tomeeVersion + ")");
} catch (final Exception e) {
// no-op
} finally {
if (field != null) {
field.setAccessible(acc);
}
}
}
}