<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="org.argeo.cms.dataServletContext">
- <implementation class="org.argeo.cms.servlet.CmsServletContext"/>
+ <implementation class="org.argeo.cms.jcr.internal.servlet.DataServletContext"/>
<service>
<provide interface="org.osgi.service.http.context.ServletContextHelper"/>
</service>
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="org.argeo.cms.filesServletContext">
- <implementation class="org.argeo.cms.servlet.PrivateWwwAuthServletContext"/>
+ <implementation class="org.argeo.cms.jcr.internal.servlet.JcrServletContext"/>
<service>
<provide interface="org.osgi.service.http.context.ServletContextHelper"/>
</service>
<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="JCR Deployment">
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" name="JCR Deployment">
<implementation class="org.argeo.cms.jcr.internal.CmsJcrDeployment"/>
<reference bind="setCmsDeployment" cardinality="1..1" interface="org.argeo.api.cms.CmsDeployment" policy="static"/>
</scr:component>
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="org.argeo.cms.jcrServletContext">
- <implementation class="org.argeo.cms.servlet.PrivateWwwAuthServletContext"/>
+ <implementation class="org.argeo.cms.jcr.internal.servlet.JcrServletContext"/>
<service>
<provide interface="org.osgi.service.http.context.ServletContextHelper"/>
</service>
CmsDeployment cmsDeployment;
public CmsJcrDeployment() {
- dataModels = new DataModels(bc);
// initTrackers();
}
- public void init() {
+ public void start() {
+ dataModels = new DataModels(bc);
ServiceTracker<?, ?> repoContextSt = new RepositoryContextStc();
- // repoContextSt.open();
- KernelUtils.asyncOpen(repoContextSt);
+ repoContextSt.open();
+ //KernelUtils.asyncOpen(repoContextSt);
// nodeDeployment = CmsJcrActivator.getService(NodeDeployment.class);
}
- public void destroy() {
+ public void stop() {
// if (nodeHttp != null)
// nodeHttp.destroy();
import java.util.TreeMap;
import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.CmsException;
import org.argeo.cms.osgi.DataModelNamespace;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
assert requiredDataModelName != null;
DataModel requiredDataModel = dataModels.get(requiredDataModelName);
if (requiredDataModel == null)
- throw new CmsException("No required data model " + requiredDataModelName);
+ throw new IllegalStateException("No required data model " + requiredDataModelName);
req.add(requiredDataModel);
}
required = Collections.unmodifiableList(req);
for (String pid : repositories.keySet()) {
try {
RepositoryContext repositoryContext = repositories.get(pid);
- repositoryContext.getRepository().shutdown();
- if (log.isDebugEnabled())
- log.debug("Shut down repository " + pid
- + (pidToCn.containsKey(pid) ? " (" + pidToCn.get(pid) + ")" : ""));
+ // Must start in another thread otherwise shutdown is interrupted
+ // TODO use an executor?
+ new Thread(() -> {
+ repositoryContext.getRepository().shutdown();
+ if (log.isDebugEnabled())
+ log.debug("Shut down repository " + pid
+ + (pidToCn.containsKey(pid) ? " (" + pidToCn.get(pid) + ")" : ""));
+ }, "Shutdown JCR repository " + pid).start();
} catch (Exception e) {
log.error("Error when shutting down Jackrabbit repository " + pid, e);
}
--- /dev/null
+package org.argeo.cms.jcr.internal.servlet;
+
+import org.argeo.cms.servlet.CmsServletContext;
+
+/** Internal subclass, so that config resources can be loaded from our bundle. */
+public class DataServletContext extends CmsServletContext {
+
+}
--- /dev/null
+package org.argeo.cms.jcr.internal.servlet;
+
+import org.argeo.cms.servlet.PrivateWwwAuthServletContext;
+
+/** Internal subclass, so that config resources can be loaded from our bundle. */
+public class JcrServletContext extends PrivateWwwAuthServletContext {
+
+}
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.argeo.cms.pkgServlet">
- <implementation class="org.argeo.cms.internal.http.PkgServlet"/>
+ <implementation class="org.argeo.cms.servlet.internal.PkgServlet"/>
<service>
<provide interface="javax.servlet.Servlet"/>
</service>
@Override
public URL getResource(String name) {
+ // TODO make it more robust and versatile
+ // if used directly it can only load from within this bundle
return bundle.getResource(name);
}
package org.argeo.cms;
/** Framework agnostic interface for log notifications */
+@Deprecated
public interface ArgeoLogListener {
/**
* Appends a log
* Logging framework agnostic identifying a logging service, to which one can
* register
*/
+@Deprecated
public interface ArgeoLogger {
/**
* Register for events by threads with the same authentication (or all
private LogReaderService logReaderService;
- private NodeLogger logger;
+ private CmsOsgiLogger logger;
// private CmsStateImpl nodeState;
// private CmsDeploymentImpl nodeDeployment;
// private CmsContextImpl nodeInstance;
// this.bc = bundleContext;
if (bundleContext != null)
this.logReaderService = getService(LogReaderService.class);
+ initArgeoLogger();
// this.internalExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//
// try {
}
private void initArgeoLogger() {
- logger = new NodeLogger(logReaderService);
+ logger = new CmsOsgiLogger(logReaderService);
if (bundleContext != null)
bundleContext.registerService(ArgeoLogger.class, logger, null);
}
--- /dev/null
+package org.argeo.cms.internal.osgi;
+
+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 java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.ArgeoLogListener;
+import org.argeo.cms.ArgeoLogger;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.internal.runtime.KernelConstants;
+import org.argeo.cms.internal.runtime.KernelUtils;
+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;
+
+/** Not meant to be used directly in standard log4j config */
+public class CmsOsgiLogger implements ArgeoLogger, LogListener {
+ /** Internal debug for development purposes. */
+ private static Boolean debug = false;
+
+ private Boolean disabled = false;
+
+ private String level = null;
+
+// private Level log4jLevel = null;
+
+ private Properties configuration;
+
+ private AppenderImpl appender;
+
+ private final List<ArgeoLogListener> everythingListeners = Collections
+ .synchronizedList(new ArrayList<ArgeoLogListener>());
+ private final List<ArgeoLogListener> allUsersListeners = Collections
+ .synchronizedList(new ArrayList<ArgeoLogListener>());
+ private final Map<String, List<ArgeoLogListener>> userListeners = Collections
+ .synchronizedMap(new HashMap<String, List<ArgeoLogListener>>());
+
+ private BlockingQueue<LogEvent> events;
+ private LogDispatcherThread logDispatcherThread = new LogDispatcherThread();
+
+ private Integer maxLastEventsCount = 10 * 1000;
+
+ /** Marker to prevent stack overflow */
+ private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
+
+ @Override
+ protected Boolean initialValue() {
+ return false;
+ }
+ };
+
+ public CmsOsgiLogger(LogReaderService lrs) {
+ 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() {
+ try {
+ events = new LinkedBlockingQueue<LogEvent>();
+
+ // if (layout != null)
+ // setLayout(layout);
+ // else
+ // setLayout(new PatternLayout(pattern));
+ appender = new AppenderImpl();
+ reloadConfiguration();
+// Logger.getRootLogger().addAppender(appender);
+
+ logDispatcherThread = new LogDispatcherThread();
+ logDispatcherThread.start();
+ } catch (Exception e) {
+ throw new CmsException("Cannot initialize log4j");
+ }
+ }
+
+ public void destroy() throws Exception {
+// Logger.getRootLogger().removeAppender(appender);
+ allUsersListeners.clear();
+ for (List<ArgeoLogListener> lst : userListeners.values())
+ lst.clear();
+ userListeners.clear();
+
+ events.clear();
+ events = null;
+ logDispatcherThread.interrupt();
+ }
+
+ // public void setLayout(Layout layout) {
+ // this.layout = layout;
+ // }
+
+ public String toString() {
+ return "Node Logger";
+ }
+
+ //
+ // OSGi LOGGER
+ //
+ @Override
+ public void logged(LogEntry status) {
+ CmsLog pluginLog = CmsLog.getLog(status.getBundle().getSymbolicName());
+ LogLevel severity = status.getLogLevel();
+ if (severity.equals(LogLevel.ERROR) && pluginLog.isErrorEnabled()) {
+ // FIXME Fix Argeo TP
+ if (status.getException() instanceof SignatureException)
+ return;
+ 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(CmsConstants.CN);
+ if (cn != null)
+ sb.append(" " + CmsConstants.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) {
+ String username = CurrentUser.getUsername();
+ if (username == null)
+ throw new CmsException("Only authenticated users can register a log listener");
+
+ if (!userListeners.containsKey(username)) {
+ List<ArgeoLogListener> lst = Collections.synchronizedList(new ArrayList<ArgeoLogListener>());
+ userListeners.put(username, lst);
+ }
+ userListeners.get(username).add(listener);
+ List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username, numberOfPreviousEvents);
+ for (LogEvent evt : lastEvents)
+ dispatchEvent(listener, evt);
+ }
+
+ 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);
+ for (LogEvent evt : lastEvents)
+ if (everything || evt.getUsername() != null)
+ dispatchEvent(listener, evt);
+ }
+
+ public synchronized void unregister(ArgeoLogListener listener) {
+ String username = CurrentUser.getUsername();
+ if (username == null)// FIXME
+ return;
+ if (!userListeners.containsKey(username))
+ throw new IllegalStateException("No user listeners " + listener + " registered for user " + username);
+ if (!userListeners.get(username).contains(listener))
+ throw new IllegalStateException("No user listeners " + listener + " registered for user " + username);
+ userListeners.get(username).remove(listener);
+ if (userListeners.get(username).isEmpty())
+ userListeners.remove(username);
+
+ }
+
+ public synchronized void unregisterForAll(ArgeoLogListener listener) {
+ everythingListeners.remove(listener);
+ allUsersListeners.remove(listener);
+ }
+
+ /** For development purpose, since using regular logging is not easy here */
+ 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;
+ // }
+
+ public void setDisabled(Boolean disabled) {
+ this.disabled = disabled;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ public void setConfiguration(Properties configuration) {
+ this.configuration = configuration;
+ }
+
+ public void updateConfiguration(Properties configuration) {
+ setConfiguration(configuration);
+ reloadConfiguration();
+ }
+
+ public Properties getConfiguration() {
+ return configuration;
+ }
+
+ /**
+ * Reloads configuration (if the configuration {@link Properties} is set)
+ */
+ protected void reloadConfiguration() {
+ if (configuration != null) {
+// LogManager.resetConfiguration();
+// PropertyConfigurator.configure(configuration);
+ }
+ }
+
+ protected synchronized void processLoggingEvent(LogEvent event) {
+ if (disabled)
+ return;
+
+ if (dispatching.get())
+ return;
+
+ if (level != null && !level.trim().equals("")) {
+// if (log4jLevel == null || !log4jLevel.toString().equals(level))
+// try {
+// log4jLevel = Level.toLevel(level);
+// } catch (Exception e) {
+// 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)) {
+// return;
+// }
+ }
+
+ try {
+ // admin listeners
+ Iterator<ArgeoLogListener> everythingIt = everythingListeners.iterator();
+ while (everythingIt.hasNext())
+ dispatchEvent(everythingIt.next(), event);
+
+ if (event.getUsername() != null) {
+ 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();
+ while (userIt.hasNext())
+ dispatchEvent(userIt.next(), event);
+ }
+ }
+ } catch (Exception e) {
+ stdOut("Cannot process logging event");
+ e.printStackTrace();
+ }
+ }
+
+ 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());
+ }
+
+ private class AppenderImpl { //extends AppenderSkeleton {
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ public void close() {
+ }
+
+// @Override
+// protected void append(LoggingEvent event) {
+// if (events != null) {
+// try {
+// String username = CurrentUser.getUsername();
+// events.put(new LogEvent(username, event));
+// } catch (InterruptedException e) {
+// // silent
+// }
+// }
+// }
+
+ }
+
+ private class LogDispatcherThread extends Thread {
+ /** encapsulated in order to simplify concurrency management */
+ private LinkedList<LogEvent> lastEvents = new LinkedList<LogEvent>();
+
+ public LogDispatcherThread() {
+ super("Argeo Logging Dispatcher Thread");
+ }
+
+ public void run() {
+ while (events != null) {
+ try {
+ LogEvent loggingEvent = events.take();
+ processLoggingEvent(loggingEvent);
+ addLastEvent(loggingEvent);
+ } catch (InterruptedException e) {
+ if (events == null)
+ return;
+ }
+ }
+ }
+
+ protected synchronized void addLastEvent(LogEvent loggingEvent) {
+ if (lastEvents.size() >= maxLastEventsCount)
+ lastEvents.poll();
+ lastEvents.add(loggingEvent);
+ }
+
+ public synchronized List<LogEvent> getLastEvents(String username, Integer maxCount) {
+ LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
+ ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents.size());
+ int count = 0;
+ while (it.hasPrevious() && (count < maxCount)) {
+ LogEvent evt = it.previous();
+ if (username == null || username.equals(evt.getUsername())) {
+ evts.push(evt);
+ count++;
+ }
+ }
+ return evts;
+ }
+ }
+
+ private class LogEvent {
+ private final String username;
+// private final LoggingEvent loggingEvent;
+
+ public LogEvent(String username) {
+ super();
+ this.username = username;
+// this.loggingEvent = loggingEvent;
+ }
+
+// @Override
+// public int hashCode() {
+// return loggingEvent.hashCode();
+// }
+//
+// @Override
+// public boolean equals(Object obj) {
+// return loggingEvent.equals(obj);
+// }
+//
+// @Override
+// public String toString() {
+// return username + "@ " + loggingEvent.toString();
+// }
+
+ public String getUsername() {
+ return username;
+ }
+
+// public LoggingEvent getLoggingEvent() {
+// return loggingEvent;
+// }
+
+ }
+
+ 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());
+ }
+ }
+ }
+}
package org.argeo.cms.internal.osgi;
-import java.io.IOException;
-
/**
* Workaround for killing Gogo shell by system shutdown.
*
+++ /dev/null
-package org.argeo.cms.internal.osgi;
-
-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 java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.ArgeoLogListener;
-import org.argeo.cms.ArgeoLogger;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.internal.runtime.KernelConstants;
-import org.argeo.cms.internal.runtime.KernelUtils;
-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;
-
-/** Not meant to be used directly in standard log4j config */
-public class NodeLogger implements ArgeoLogger, LogListener {
- /** Internal debug for development purposes. */
- private static Boolean debug = false;
-
- private Boolean disabled = false;
-
- private String level = null;
-
-// private Level log4jLevel = null;
-
- private Properties configuration;
-
- private AppenderImpl appender;
-
- private final List<ArgeoLogListener> everythingListeners = Collections
- .synchronizedList(new ArrayList<ArgeoLogListener>());
- private final List<ArgeoLogListener> allUsersListeners = Collections
- .synchronizedList(new ArrayList<ArgeoLogListener>());
- private final Map<String, List<ArgeoLogListener>> userListeners = Collections
- .synchronizedMap(new HashMap<String, List<ArgeoLogListener>>());
-
- private BlockingQueue<LogEvent> events;
- private LogDispatcherThread logDispatcherThread = new LogDispatcherThread();
-
- private Integer maxLastEventsCount = 10 * 1000;
-
- /** Marker to prevent stack overflow */
- private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
-
- @Override
- protected Boolean initialValue() {
- return false;
- }
- };
-
- public NodeLogger(LogReaderService lrs) {
- 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() {
- try {
- events = new LinkedBlockingQueue<LogEvent>();
-
- // if (layout != null)
- // setLayout(layout);
- // else
- // setLayout(new PatternLayout(pattern));
- appender = new AppenderImpl();
- reloadConfiguration();
-// Logger.getRootLogger().addAppender(appender);
-
- logDispatcherThread = new LogDispatcherThread();
- logDispatcherThread.start();
- } catch (Exception e) {
- throw new CmsException("Cannot initialize log4j");
- }
- }
-
- public void destroy() throws Exception {
-// Logger.getRootLogger().removeAppender(appender);
- allUsersListeners.clear();
- for (List<ArgeoLogListener> lst : userListeners.values())
- lst.clear();
- userListeners.clear();
-
- events.clear();
- events = null;
- logDispatcherThread.interrupt();
- }
-
- // public void setLayout(Layout layout) {
- // this.layout = layout;
- // }
-
- public String toString() {
- return "Node Logger";
- }
-
- //
- // OSGi LOGGER
- //
- @Override
- public void logged(LogEntry status) {
- CmsLog pluginLog = CmsLog.getLog(status.getBundle().getSymbolicName());
- LogLevel severity = status.getLogLevel();
- if (severity.equals(LogLevel.ERROR) && pluginLog.isErrorEnabled()) {
- // FIXME Fix Argeo TP
- if (status.getException() instanceof SignatureException)
- return;
- 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(CmsConstants.CN);
- if (cn != null)
- sb.append(" " + CmsConstants.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) {
- String username = CurrentUser.getUsername();
- if (username == null)
- throw new CmsException("Only authenticated users can register a log listener");
-
- if (!userListeners.containsKey(username)) {
- List<ArgeoLogListener> lst = Collections.synchronizedList(new ArrayList<ArgeoLogListener>());
- userListeners.put(username, lst);
- }
- userListeners.get(username).add(listener);
- List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username, numberOfPreviousEvents);
- for (LogEvent evt : lastEvents)
- dispatchEvent(listener, evt);
- }
-
- 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);
- for (LogEvent evt : lastEvents)
- if (everything || evt.getUsername() != null)
- dispatchEvent(listener, evt);
- }
-
- public synchronized void unregister(ArgeoLogListener listener) {
- String username = CurrentUser.getUsername();
- if (username == null)// FIXME
- return;
- if (!userListeners.containsKey(username))
- throw new CmsException("No user listeners " + listener + " registered for user " + username);
- if (!userListeners.get(username).contains(listener))
- throw new CmsException("No user listeners " + listener + " registered for user " + username);
- userListeners.get(username).remove(listener);
- if (userListeners.get(username).isEmpty())
- userListeners.remove(username);
-
- }
-
- public synchronized void unregisterForAll(ArgeoLogListener listener) {
- everythingListeners.remove(listener);
- allUsersListeners.remove(listener);
- }
-
- /** For development purpose, since using regular logging is not easy here */
- 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;
- // }
-
- public void setDisabled(Boolean disabled) {
- this.disabled = disabled;
- }
-
- public void setLevel(String level) {
- this.level = level;
- }
-
- public void setConfiguration(Properties configuration) {
- this.configuration = configuration;
- }
-
- public void updateConfiguration(Properties configuration) {
- setConfiguration(configuration);
- reloadConfiguration();
- }
-
- public Properties getConfiguration() {
- return configuration;
- }
-
- /**
- * Reloads configuration (if the configuration {@link Properties} is set)
- */
- protected void reloadConfiguration() {
- if (configuration != null) {
-// LogManager.resetConfiguration();
-// PropertyConfigurator.configure(configuration);
- }
- }
-
- protected synchronized void processLoggingEvent(LogEvent event) {
- if (disabled)
- return;
-
- if (dispatching.get())
- return;
-
- if (level != null && !level.trim().equals("")) {
-// if (log4jLevel == null || !log4jLevel.toString().equals(level))
-// try {
-// log4jLevel = Level.toLevel(level);
-// } catch (Exception e) {
-// 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)) {
-// return;
-// }
- }
-
- try {
- // admin listeners
- Iterator<ArgeoLogListener> everythingIt = everythingListeners.iterator();
- while (everythingIt.hasNext())
- dispatchEvent(everythingIt.next(), event);
-
- if (event.getUsername() != null) {
- 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();
- while (userIt.hasNext())
- dispatchEvent(userIt.next(), event);
- }
- }
- } catch (Exception e) {
- stdOut("Cannot process logging event");
- e.printStackTrace();
- }
- }
-
- 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());
- }
-
- private class AppenderImpl { //extends AppenderSkeleton {
- public boolean requiresLayout() {
- return false;
- }
-
- public void close() {
- }
-
-// @Override
-// protected void append(LoggingEvent event) {
-// if (events != null) {
-// try {
-// String username = CurrentUser.getUsername();
-// events.put(new LogEvent(username, event));
-// } catch (InterruptedException e) {
-// // silent
-// }
-// }
-// }
-
- }
-
- private class LogDispatcherThread extends Thread {
- /** encapsulated in order to simplify concurrency management */
- private LinkedList<LogEvent> lastEvents = new LinkedList<LogEvent>();
-
- public LogDispatcherThread() {
- super("Argeo Logging Dispatcher Thread");
- }
-
- public void run() {
- while (events != null) {
- try {
- LogEvent loggingEvent = events.take();
- processLoggingEvent(loggingEvent);
- addLastEvent(loggingEvent);
- } catch (InterruptedException e) {
- if (events == null)
- return;
- }
- }
- }
-
- protected synchronized void addLastEvent(LogEvent loggingEvent) {
- if (lastEvents.size() >= maxLastEventsCount)
- lastEvents.poll();
- lastEvents.add(loggingEvent);
- }
-
- public synchronized List<LogEvent> getLastEvents(String username, Integer maxCount) {
- LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
- ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents.size());
- int count = 0;
- while (it.hasPrevious() && (count < maxCount)) {
- LogEvent evt = it.previous();
- if (username == null || username.equals(evt.getUsername())) {
- evts.push(evt);
- count++;
- }
- }
- return evts;
- }
- }
-
- private class LogEvent {
- private final String username;
-// private final LoggingEvent loggingEvent;
-
- public LogEvent(String username) {
- super();
- this.username = username;
-// this.loggingEvent = loggingEvent;
- }
-
-// @Override
-// public int hashCode() {
-// return loggingEvent.hashCode();
-// }
-//
-// @Override
-// public boolean equals(Object obj) {
-// return loggingEvent.equals(obj);
-// }
-//
-// @Override
-// public String toString() {
-// return username + "@ " + loggingEvent.toString();
-// }
-
- public String getUsername() {
- return username;
- }
-
-// public LoggingEvent getLoggingEvent() {
-// return loggingEvent;
-// }
-
- }
-
- 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());
- }
- }
- }
-}
private StackTraceElement findCallLocation(Level level, Thread thread) {
assert level != null;
assert thread != null;
+ // TODO rather use a StackWalker and make it smarter
StackTraceElement callLocation = null;
if (level.getSeverity() >= callLocationLevel.getSeverity()) {
StackTraceElement[] stack = thread.getStackTrace();
String className = stack[i].getClassName();
switch (className) {
// TODO make it more configurable
+ // FIXME deal with privileges stacks (in Equinox)
case "java.lang.System$Logger":
case "java.util.logging.Logger":
case "org.apache.commons.logging.Log":
case "org.osgi.service.log.Logger":
+ case "org.eclipse.osgi.internal.log.LoggerImpl":
case "org.argeo.api.cms.CmsLog":
case "org.slf4j.impl.ArgeoLogger":
+ case "org.argeo.cms.internal.osgi.CmsOsgiLogger":
case "org.eclipse.jetty.util.log.Slf4jLog":
case "sun.util.logging.internal.LoggingProviderImpl$JULWrapper":
lowestLoggerInterface = i;