-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed 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.argeo.cms.internal.kernel;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.spi.LoggingEvent;
-import org.argeo.ArgeoException;
-import org.argeo.ArgeoLogListener;
-import org.argeo.ArgeoLogger;
+import org.argeo.api.ArgeoLogListener;
+import org.argeo.api.ArgeoLogger;
+import org.argeo.api.NodeConstants;
+import org.argeo.cms.CmsException;
import org.argeo.cms.auth.CurrentUser;
+import org.argeo.osgi.useradmin.UserAdminConf;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.log.LogEntry;
+import org.osgi.service.log.LogLevel;
import org.osgi.service.log.LogListener;
import org.osgi.service.log.LogReaderService;
-import org.osgi.service.log.LogService;
/** Not meant to be used directly in standard log4j config */
class NodeLogger implements ArgeoLogger, LogListener {
+ /** Internal debug for development purposes. */
+ private static Boolean debug = false;
+
+ // private final static Log log = LogFactory.getLog(NodeLogger.class);
private Boolean disabled = false;
}
};
- @SuppressWarnings("unchecked")
public NodeLogger(LogReaderService lrs) {
- Enumeration<LogEntry> logEntries = lrs.getLog();
- while (logEntries.hasMoreElements())
- logged(logEntries.nextElement());
- lrs.addLogListener(this);
+ if (lrs != null) {
+ Enumeration<LogEntry> logEntries = lrs.getLog();
+ while (logEntries.hasMoreElements())
+ logged(logEntries.nextElement());
+ lrs.addLogListener(this);
+
+ // configure log4j watcher
+ String log4jConfiguration = KernelUtils.getFrameworkProp("log4j.configuration");
+ if (log4jConfiguration != null && log4jConfiguration.startsWith("file:")) {
+ if (log4jConfiguration.contains("..")) {
+ if (log4jConfiguration.startsWith("file://"))
+ log4jConfiguration = log4jConfiguration.substring("file://".length());
+ else if (log4jConfiguration.startsWith("file:"))
+ log4jConfiguration = log4jConfiguration.substring("file:".length());
+ }
+ try {
+ Path log4jconfigPath;
+ if (log4jConfiguration.startsWith("file:"))
+ log4jconfigPath = Paths.get(new URI(log4jConfiguration));
+ else
+ log4jconfigPath = Paths.get(log4jConfiguration);
+ Thread log4jConfWatcher = new Log4jConfWatcherThread(log4jconfigPath);
+ log4jConfWatcher.start();
+ } catch (Exception e) {
+ stdErr("Badly formatted log4j configuration URI " + log4jConfiguration + ": " + e.getMessage());
+ }
+ }
+ }
}
public void init() {
logDispatcherThread = new LogDispatcherThread();
logDispatcherThread.start();
} catch (Exception e) {
- throw new ArgeoException("Cannot initialize log4j");
+ throw new CmsException("Cannot initialize log4j");
}
}
// this.layout = layout;
// }
+ public String toString() {
+ return "Node Logger";
+ }
+
//
// OSGi LOGGER
//
@Override
public void logged(LogEntry status) {
Log pluginLog = LogFactory.getLog(status.getBundle().getSymbolicName());
- Integer severity = status.getLevel();
- if (severity == LogService.LOG_ERROR) {
+ LogLevel severity = status.getLogLevel();
+ if (severity.equals(LogLevel.ERROR) && pluginLog.isErrorEnabled()) {
// FIXME Fix Argeo TP
if (status.getException() instanceof SignatureException)
return;
- pluginLog.error(status.getMessage(), status.getException());
- } else if (severity == LogService.LOG_WARNING)
- pluginLog.warn(status.getMessage(), status.getException());
- else if (severity == LogService.LOG_INFO && pluginLog.isDebugEnabled())
- pluginLog.debug(status.getMessage(), status.getException());
- else if (severity == LogService.LOG_DEBUG && pluginLog.isTraceEnabled())
- pluginLog.trace(status.getMessage(), status.getException());
+ pluginLog.error(msg(status), status.getException());
+ } else if (severity.equals(LogLevel.WARN) && pluginLog.isWarnEnabled()) {
+ if (pluginLog.isTraceEnabled())
+ pluginLog.warn(msg(status), status.getException());
+ else
+ pluginLog.warn(msg(status));
+ } else if (severity.equals(LogLevel.INFO) && pluginLog.isDebugEnabled())
+ pluginLog.debug(msg(status), status.getException());
+ else if (severity.equals(LogLevel.DEBUG) && pluginLog.isTraceEnabled())
+ pluginLog.trace(msg(status), status.getException());
+ else if (severity.equals(LogLevel.TRACE) && pluginLog.isTraceEnabled())
+ pluginLog.trace(msg(status), status.getException());
+ }
+
+ private String msg(LogEntry status) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(status.getMessage());
+ Bundle bundle = status.getBundle();
+ if (bundle != null) {
+ sb.append(" '" + bundle.getSymbolicName() + "'");
+ }
+ ServiceReference<?> sr = status.getServiceReference();
+ if (sr != null) {
+ sb.append(' ');
+ String[] objectClasses = (String[]) sr.getProperty(Constants.OBJECTCLASS);
+ if (isSpringApplicationContext(objectClasses)) {
+ sb.append("{org.springframework.context.ApplicationContext}");
+ Object symbolicName = sr.getProperty(Constants.BUNDLE_SYMBOLICNAME);
+ if (symbolicName != null)
+ sb.append(" " + Constants.BUNDLE_SYMBOLICNAME + ": " + symbolicName);
+ } else {
+ sb.append(arrayToString(objectClasses));
+ }
+ Object cn = sr.getProperty(NodeConstants.CN);
+ if (cn != null)
+ sb.append(" " + NodeConstants.CN + ": " + cn);
+ Object factoryPid = sr.getProperty(ConfigurationAdmin.SERVICE_FACTORYPID);
+ if (factoryPid != null)
+ sb.append(" " + ConfigurationAdmin.SERVICE_FACTORYPID + ": " + factoryPid);
+ // else {
+ // Object servicePid = sr.getProperty(Constants.SERVICE_PID);
+ // if (servicePid != null)
+ // sb.append(" " + Constants.SERVICE_PID + ": " + servicePid);
+ // }
+ // servlets
+ Object whiteBoardPattern = sr.getProperty(KernelConstants.WHITEBOARD_PATTERN_PROP);
+ if (whiteBoardPattern != null) {
+ if (whiteBoardPattern instanceof String) {
+ sb.append(" " + KernelConstants.WHITEBOARD_PATTERN_PROP + ": " + whiteBoardPattern);
+ } else {
+ sb.append(" " + KernelConstants.WHITEBOARD_PATTERN_PROP + ": "
+ + arrayToString((String[]) whiteBoardPattern));
+ }
+ }
+ // RWT
+ Object contextName = sr.getProperty(KernelConstants.CONTEXT_NAME_PROP);
+ if (contextName != null)
+ sb.append(" " + KernelConstants.CONTEXT_NAME_PROP + ": " + contextName);
+
+ // user directories
+ Object baseDn = sr.getProperty(UserAdminConf.baseDn.name());
+ if (baseDn != null)
+ sb.append(" " + UserAdminConf.baseDn.name() + ": " + baseDn);
+
+ }
+ return sb.toString();
+ }
+
+ private String arrayToString(Object[] arr) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ for (int i = 0; i < arr.length; i++) {
+ if (i != 0)
+ sb.append(',');
+ sb.append(arr[i]);
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ private boolean isSpringApplicationContext(String[] objectClasses) {
+ for (String clss : objectClasses) {
+ if (clss.equals("org.eclipse.gemini.blueprint.context.DelegatedExecutionOsgiBundleApplicationContext")) {
+ return true;
+ }
+ }
+ return false;
}
//
// ARGEO LOGGER
//
- public synchronized void register(ArgeoLogListener listener,
- Integer numberOfPreviousEvents) {
+ public synchronized void register(ArgeoLogListener listener, Integer numberOfPreviousEvents) {
String username = CurrentUser.getUsername();
if (username == null)
- throw new ArgeoException(
- "Only authenticated users can register a log listener");
+ throw new CmsException("Only authenticated users can register a log listener");
if (!userListeners.containsKey(username)) {
- List<ArgeoLogListener> lst = Collections
- .synchronizedList(new ArrayList<ArgeoLogListener>());
+ List<ArgeoLogListener> lst = Collections.synchronizedList(new ArrayList<ArgeoLogListener>());
userListeners.put(username, lst);
}
userListeners.get(username).add(listener);
- List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username,
- numberOfPreviousEvents);
+ List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username, numberOfPreviousEvents);
for (LogEvent evt : lastEvents)
dispatchEvent(listener, evt);
}
- public synchronized void registerForAll(ArgeoLogListener listener,
- Integer numberOfPreviousEvents, boolean everything) {
+ public synchronized void registerForAll(ArgeoLogListener listener, Integer numberOfPreviousEvents,
+ boolean everything) {
if (everything)
everythingListeners.add(listener);
else
allUsersListeners.add(listener);
- List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(null,
- numberOfPreviousEvents);
+ List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(null, numberOfPreviousEvents);
for (LogEvent evt : lastEvents)
if (everything || evt.getUsername() != null)
dispatchEvent(listener, evt);
if (username == null)// FIXME
return;
if (!userListeners.containsKey(username))
- throw new ArgeoException("No user listeners " + listener
- + " registered for user " + username);
+ throw new CmsException("No user listeners " + listener + " registered for user " + username);
if (!userListeners.get(username).contains(listener))
- throw new ArgeoException("No user listeners " + listener
- + " registered for user " + username);
+ throw new CmsException("No user listeners " + listener + " registered for user " + username);
userListeners.get(username).remove(listener);
if (userListeners.get(username).isEmpty())
userListeners.remove(username);
}
/** For development purpose, since using regular logging is not easy here */
- static void stdOut(Object obj) {
+ private static void stdOut(Object obj) {
System.out.println(obj);
}
+ private static void stdErr(Object obj) {
+ System.err.println(obj);
+ }
+
+ private static void debug(Object obj) {
+ if (debug)
+ System.out.println(obj);
+ }
+
+ private static boolean isInternalDebugEnabled() {
+ return debug;
+ }
+
// public void setPattern(String pattern) {
// this.pattern = pattern;
// }
return configuration;
}
- /** Reloads configuration (if the configuration {@link Properties} is set) */
+ /**
+ * Reloads configuration (if the configuration {@link Properties} is set)
+ */
protected void reloadConfiguration() {
if (configuration != null) {
LogManager.resetConfiguration();
try {
log4jLevel = Level.toLevel(level);
} catch (Exception e) {
- System.err
- .println("Log4j level could not be set for level '"
- + level + "', resetting it to null.");
+ System.err.println("Log4j level could not be set for level '" + level + "', resetting it to null.");
e.printStackTrace();
level = null;
}
- if (log4jLevel != null
- && !event.getLoggingEvent().getLevel()
- .isGreaterOrEqual(log4jLevel)) {
+ if (log4jLevel != null && !event.getLoggingEvent().getLevel().isGreaterOrEqual(log4jLevel)) {
return;
}
}
try {
// admin listeners
- Iterator<ArgeoLogListener> everythingIt = everythingListeners
- .iterator();
+ Iterator<ArgeoLogListener> everythingIt = everythingListeners.iterator();
while (everythingIt.hasNext())
dispatchEvent(everythingIt.next(), event);
if (event.getUsername() != null) {
- Iterator<ArgeoLogListener> allUsersIt = allUsersListeners
- .iterator();
+ Iterator<ArgeoLogListener> allUsersIt = allUsersListeners.iterator();
while (allUsersIt.hasNext())
dispatchEvent(allUsersIt.next(), event);
if (userListeners.containsKey(event.getUsername())) {
- Iterator<ArgeoLogListener> userIt = userListeners.get(
- event.getUsername()).iterator();
+ Iterator<ArgeoLogListener> userIt = userListeners.get(event.getUsername()).iterator();
while (userIt.hasNext())
dispatchEvent(userIt.next(), event);
}
protected void dispatchEvent(ArgeoLogListener logListener, LogEvent evt) {
LoggingEvent event = evt.getLoggingEvent();
- logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event
- .getLevel().toString(), event.getLoggerName(), event
- .getThreadName(), event.getMessage(), event
- .getThrowableStrRep());
+ logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event.getLevel().toString(),
+ event.getLoggerName(), event.getThreadName(), event.getMessage(), event.getThrowableStrRep());
}
private class AppenderImpl extends AppenderSkeleton {
lastEvents.add(loggingEvent);
}
- public synchronized List<LogEvent> getLastEvents(String username,
- Integer maxCount) {
+ public synchronized List<LogEvent> getLastEvents(String username, Integer maxCount) {
LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
- ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents
- .size());
+ ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents.size());
int count = 0;
while (it.hasPrevious() && (count < maxCount)) {
LogEvent evt = it.previous();
}
}
+
+ private class Log4jConfWatcherThread extends Thread {
+ private Path log4jConfigurationPath;
+
+ public Log4jConfWatcherThread(Path log4jConfigurationPath) {
+ super("Log4j Configuration Watcher");
+ try {
+ this.log4jConfigurationPath = log4jConfigurationPath.toRealPath();
+ } catch (IOException e) {
+ this.log4jConfigurationPath = log4jConfigurationPath.toAbsolutePath();
+ stdOut("Cannot determine real path for " + log4jConfigurationPath + ": " + e.getMessage());
+ }
+ }
+
+ public void run() {
+ Path parentDir = log4jConfigurationPath.getParent();
+ try (final WatchService watchService = FileSystems.getDefault().newWatchService()) {
+ parentDir.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
+ WatchKey wk;
+ watching: while ((wk = watchService.take()) != null) {
+ for (WatchEvent<?> event : wk.pollEvents()) {
+ final Path changed = (Path) event.context();
+ if (log4jConfigurationPath.equals(parentDir.resolve(changed))) {
+ if (isInternalDebugEnabled())
+ debug(log4jConfigurationPath + " has changed, reloading.");
+ PropertyConfigurator.configure(log4jConfigurationPath.toUri().toURL());
+ }
+ }
+ // reset the key
+ boolean valid = wk.reset();
+ if (!valid) {
+ break watching;
+ }
+ }
+ } catch (IOException | InterruptedException e) {
+ stdErr("Log4j configuration watcher failed: " + e.getMessage());
+ }
+ }
+ }
}