/* * 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.openejb.util; import org.apache.openejb.loader.Options; import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.log.ConsoleColorHandler; import org.apache.openejb.log.SingleLineFormatter; import org.apache.openejb.util.reflection.Reflections; import org.apache.webbeans.logger.WebBeansLoggerFacade; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Constructor; import java.util.logging.ConsoleHandler; import java.util.logging.LogManager; import java.util.logging.Logger; /** * default conf = jre conf * user conf used transparently */ public class JuliLogStreamFactory implements LogStreamFactory { public static final String OPENEJB_LOG_COLOR_PROP = "openejb.log.color"; private static String consoleHandlerClazz; private static boolean useOpenEJBHandler; public LogStream createLogStream(final LogCategory logCategory) { return new JuliLogStream(logCategory); } static { final boolean tomee = is("org.apache.tomee.catalina.TomcatLoader"); final boolean embedded = is("org.apache.tomee.embedded.Container"); // if embedded case enhance a bit logging if not set final Options options = SystemInstance.isInitialized() ? SystemInstance.get().getOptions() : new Options(JavaSecurityManagers.getSystemProperties()); final boolean forceLogs = options.get("openejb.jul.forceReload", false); if ((!tomee || embedded || forceLogs) && JavaSecurityManagers.getSystemProperty("java.util.logging.manager") == null) { consoleHandlerClazz = options.get("openejb.jul.consoleHandlerClazz", (String) null); if (consoleHandlerClazz == null) { if (options.get(OPENEJB_LOG_COLOR_PROP, false) && isNotIDE()) { consoleHandlerClazz = ConsoleColorHandler.class.getName(); } else { consoleHandlerClazz = OpenEJBSimpleLayoutHandler.class.getName(); } } try { // check it will not fail later (case when a framework change the JVM classloading) ClassLoader.getSystemClassLoader().loadClass(consoleHandlerClazz); } catch (final ClassNotFoundException e) { consoleHandlerClazz = ConsoleHandler.class.getName(); } if (forceLogs) { useOpenEJBHandler = options.get("openejb.jul.forceReload.use-openejb-handler", true); try { final OpenEJBLogManager value = new OpenEJBLogManager(); Reflections.set(LogManager.class, null, "manager", value); value.forceReset(); setRootLogger(value); value.readConfiguration(); // re-read the config to ensure we have a parent logger } catch (final Exception e) { // no-op } } // do it last since otherwise it can lock JavaSecurityManagers.setSystemProperty("java.util.logging.manager", OpenEJBLogManager.class.getName()); } try { if (options.get("openjpa.Log", (String) null) == null) { JuliLogStreamFactory.class.getClassLoader().loadClass("org.apache.openjpa.lib.log.LogFactoryAdapter"); JavaSecurityManagers.setSystemProperty("openjpa.Log", "org.apache.openejb.openjpa.JULOpenJPALogFactory"); } } catch (final Exception ignored) { // no-op: openjpa is not at the classpath so don't trigger it loading with our logger } try { JavaSecurityManagers.setSystemProperty(WebBeansLoggerFacade.class.getName(), "org.apache.openejb.cdi.logging.ContainerJULLoggerFactory"); } catch (final Throwable th) { // ignored, surely arquillian remote only so OWB is not here } } private static void setRootLogger(final OpenEJBLogManager value) { try { // if we don't do it - which is done in static part of the LogManager - we couldn't log user info when force-reload is to true final Class<?> rootLoggerClass = ClassLoader.getSystemClassLoader().loadClass("java.util.logging.LogManager$RootLogger"); final Constructor<?> cons = rootLoggerClass.getDeclaredConstructor(LogManager.class); final boolean acc = cons.isAccessible(); if (!acc) { cons.setAccessible(true); } final Logger rootLogger = Logger.class.cast(cons.newInstance(value)); try { Reflections.set(value, "rootLogger", rootLogger); } finally { cons.setAccessible(acc); } value.addLogger(rootLogger); Reflections.invokeByReflection(Reflections.get(value, "systemContext"), "addLocalLogger", new Class<?>[]{Logger.class}, new Object[]{rootLogger}); Reflections.invokeByReflection(Logger.getGlobal(), "setLogManager", new Class<?>[]{LogManager.class}, new Object[]{value}); value.addLogger(Logger.getGlobal()); } catch (final Throwable e) { // no-op } } public static boolean isNotIDE() { return !JavaSecurityManagers.getSystemProperty("java.class.path").contains("idea_rt"); // TODO: eclipse, netbeans } // TODO: mange conf by classloader? see tomcat log manager public static class OpenEJBLogManager extends LogManager { static { final LogManager mgr = LogManager.getLogManager(); if (mgr instanceof OpenEJBLogManager) { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { ((OpenEJBLogManager) mgr).forceReset(); } }); } } public void forceReset() { super.reset(); } @Override public void reset() throws SecurityException { // no-op } @Override public String getProperty(final String name) { final String parentValue = super.getProperty(name); if (SystemInstance.isInitialized()) { if (SystemInstance.get().getProperties().containsKey(name)) { return SystemInstance.get().getProperty(name); } final String propertyKeyValue = "logging" + reverseProperty(name); if (SystemInstance.get().getProperties().containsKey(propertyKeyValue)) { return SystemInstance.get().getProperty(propertyKeyValue); } } // if it is one of ours loggers and no value is defined let set our nice logging style if (OpenEJBLogManager.class.getName().equals(JavaSecurityManagers.getSystemProperty("java.util.logging.manager")) // custom logging && isOverridableLogger(name) // managed loggers && parentValue == null) { // not already defined if (name.endsWith(".handlers")) { return consoleHandlerClazz; } else if (name.endsWith(".useParentHandlers")) { return "false"; } } return parentValue; } } private static String reverseProperty(final String name) { if (name.contains(".") && !name.endsWith(".")) { final int idx = name.lastIndexOf('.'); return name.substring(idx) + "." + name.substring(0, idx); } return name; } private static boolean isOverridableLogger(final String name) { return useOpenEJBHandler || name.toLowerCase().contains("openejb") || name.toLowerCase().contains("transaction") || name.toLowerCase().contains("cxf") || name.toLowerCase().contains("timer") || (name.startsWith("org.apache.") && !name.startsWith("org.apache.geronimo.connector.work.WorkerContext.")) || name.startsWith("openjpa.") || name.startsWith("net.sf.ehcache.") || name.startsWith("org.quartz.") || name.startsWith("org.hibernate."); } public static class OpenEJBSimpleLayoutHandler extends ConsoleHandler { public OpenEJBSimpleLayoutHandler() { setFormatter(new SingleLineFormatter()); } @Override protected synchronized void setOutputStream(final OutputStream out) throws SecurityException { super.setOutputStream(new FilterOutputStream(System.out) { // don't close System.out to not loose important things like exceptions @Override public void write(final int b) throws IOException { System.out.write(b); } @Override public void write(final byte[] b) throws IOException { System.out.write(b); } @Override public void write(final byte[] b, final int off, final int len) throws IOException { System.out.write(b, off, len); } @Override public void flush() throws IOException { System.out.flush(); } @Override public void close() throws IOException { flush(); } }); } } private static boolean is(final String classname) { try { JuliLogStreamFactory.class.getClassLoader().loadClass(classname); return true; } catch (final Exception e) { return false; } } }