package org.jbehave.eclipse; import java.io.IOException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.text.templates.ContextTypeRegistry; import org.eclipse.jface.text.templates.persistence.TemplateStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.ui.editors.text.templates.ContributionContextTypeRegistry; import org.eclipse.ui.editors.text.templates.ContributionTemplateStore; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.jbehave.eclipse.console.JBehaveConsoleAppender; import org.jbehave.eclipse.editor.story.completion.StoryContextType; import org.jbehave.eclipse.preferences.LoggerEntry; import org.jbehave.eclipse.preferences.LoggerPreferences; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.service.prefs.BackingStoreException; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.ConsoleAppender; import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; import ch.qos.logback.core.rolling.RollingFileAppender; import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; import fj.data.List; /** * The activator class controls the plug-in life cycle */ public class Activator extends AbstractUIPlugin { // The plug-in ID public static final String PLUGIN_ID = "org.jbehave.eclipse"; //$NON-NLS-1$ // The shared instance private static Activator plugin; // The Contribution registry and template store private ContributionContextTypeRegistry registry; private ContributionTemplateStore templateStore; private String version; // Key to store custom templates private static final String TEMPLATES_KEY = "org.jbehave.eclipse.templates"; //$NON-NLS-1$ public Activator() { } public void start(BundleContext context) throws Exception { super.start(context); Bundle bundle = context.getBundle(); version = (String) bundle.getHeaders().get(org.osgi.framework.Constants.BUNDLE_VERSION); plugin = this; } public void initLogger () { String logFile = getStateLocation().append("plugin.log").toOSString(); LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); // rolling file PatternLayoutEncoder encoder = createLoggerPattern(loggerContext, "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"); RollingFileAppender<ILoggingEvent> rollingFileAppender = rollingFileLog(logFile, loggerContext, encoder); // console ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<ILoggingEvent>(); consoleAppender.setEncoder(encoder); consoleAppender.start(); // jbehave console PatternLayoutEncoder jbehaveEncoder = createLoggerPattern(loggerContext, "%d{HH:mm:ss.SSS} %-5level %logger{20} - %msg%n"); JBehaveConsoleAppender jbehaveConsoleAppender = new JBehaveConsoleAppender(); jbehaveConsoleAppender.setEncoder(jbehaveEncoder); jbehaveConsoleAppender.start(); // attach the appenders to the root logger Logger logbackLogger = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); logbackLogger.detachAndStopAllAppenders(); logbackLogger.setAdditive(true); logbackLogger.addAppender(consoleAppender); logbackLogger.addAppender(jbehaveConsoleAppender); logbackLogger.addAppender(rollingFileAppender); resetLoggerLevels(); // TODO: investigate why it's not fired on preference flush... getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { resetLoggerLevels(); } }); logInfo("Log file at " + logFile); } protected PatternLayoutEncoder createLoggerPattern(LoggerContext loggerContext, String pattern) { PatternLayoutEncoder encoder = new PatternLayoutEncoder(); encoder.setContext(loggerContext); encoder.setPattern(pattern); encoder.start(); return encoder; } public void resetLoggerLevels() { logInfo("Resetting log levels"); try { LoggerPreferences prefs = new LoggerPreferences(); prefs.load(); LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); for(Logger logger : loggerContext.getLoggerList()) { // The level of the root logger cannot be set to null if(!org.slf4j.Logger.ROOT_LOGGER_NAME.equals(logger.getName())) logger.setLevel(null); } List<LoggerEntry> loggerEntries = prefs.getLoggerEntries(); logInfo("About to define #" + loggerEntries.length() + "loggers"); for(LoggerEntry entry : loggerEntries) { logInfo("Defining logger <" + entry.getLoggerName() + "> at level <" + entry.getLevel() + ">"); loggerContext.getLogger(entry.getLoggerName()).setLevel(entry.getLevel()); } } catch (BackingStoreException e) { logError("Failed to define logger levels", e); } } protected RollingFileAppender<ILoggingEvent> rollingFileLog(String logFile, LoggerContext loggerContext, PatternLayoutEncoder encoder) { RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<ILoggingEvent>(); appender.setContext(loggerContext); appender.setFile(logFile); FixedWindowRollingPolicy policy = new FixedWindowRollingPolicy(); policy.setContext(loggerContext); // rolling policies need to know their parent // it's one of the rare cases, where a sub-component knows about its parent policy.setParent(appender); policy.setFileNamePattern("plugin.%i.log.zip"); policy.start(); SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<ILoggingEvent>(); triggeringPolicy.setMaxFileSize("5MB"); triggeringPolicy.start(); appender.setEncoder(encoder); appender.setRollingPolicy(policy); appender.setTriggeringPolicy(triggeringPolicy); appender.start(); return appender; } public void stop(BundleContext context) throws Exception { plugin = null; super.stop(context); } public KeywordImages getKeywordImages() { return new KeywordImages(getImageRegistry()); } @Override protected void initializeImageRegistry(ImageRegistry registry) { super.initializeImageRegistry(registry); registry.put(ImageIds.STEP_GIVEN, getImageDescriptor("icons/bdd-g-blue.png")); registry.put(ImageIds.STEP_WHEN, getImageDescriptor("icons/bdd-w-orange.png")); registry.put(ImageIds.STEP_THEN, getImageDescriptor("icons/bdd-t-green.png")); registry.put(ImageIds.STEP_AND, getImageDescriptor("icons/bdd-a-lighterblue.png")); // registry.put(ImageIds.META, getImageDescriptor("icons/bdd-m-pink.png")); registry.put(ImageIds.STORY, getImageDescriptor("icons/bdd-s-darkpink.png")); registry.put(ImageIds.IGNORABLE, getImageDescriptor("icons/bdd-i-cacadoie.png")); // registry.put(ImageIds.NARRATIVE, getImageDescriptor("icons/bdd-n-darkred.png")); registry.put(ImageIds.SCENARIO, getImageDescriptor("icons/bdd-s-darkpink.png")); registry.put(ImageIds.EXAMPLE_TABLE, getImageDescriptor("icons/bdd-e-turquoise.png")); // registry.put(ImageIds.FORBIDDEN_OVERLAY, getImageDescriptor("icons/error_ovr16.gif")); } @Override public IPreferenceStore getPreferenceStore() { return super.getPreferenceStore(); } /** * Returns the shared instance * * @return the shared instance */ public static Activator getDefault() { return plugin; } /** * Returns an image descriptor for the image file at the given * plug-in relative path * * @param path the path * @return the image descriptor */ public static ImageDescriptor getImageDescriptor(String path) { return imageDescriptorFromPlugin(PLUGIN_ID, path); } public TemplateStore getTemplateStore() { // this is to avoid recursive call when fContextTypeRegistry is null getContextTypeRegistry(); if (templateStore == null) { templateStore = new ContributionTemplateStore( getContextTypeRegistry(), getPreferenceStore(), TEMPLATES_KEY); try { templateStore.load(); } catch (final IOException e) { getLog().log( new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, "", e)); //$NON-NLS-1$ } } return templateStore; } public ContextTypeRegistry getContextTypeRegistry() { if (registry == null) { // create an configure the contexts available in the template editor registry = new ContributionContextTypeRegistry(); registry.addContextType(StoryContextType.STORY_CONTEXT_TYPE_ID); } return registry; } private AtomicInteger idGen = new AtomicInteger(); private ExecutorService executor = Executors.newFixedThreadPool(4, new ThreadFactory() { private ThreadGroup group = new ThreadGroup("AsyncExecutor") { @Override public void uncaughtException(Thread t, Throwable e) { logError("Uncaught exception in asynchronous executor", e); super.uncaughtException(t, e); } }; public Thread newThread(Runnable r) { Thread thr = new Thread (group, r, "JBehaveWorker#" + idGen.incrementAndGet()); return thr; } }); public Executor getExecutor() { return this.executor; } public static void logInfo(String message) { getDefault().getLog().log(new Status(Status.INFO, PLUGIN_ID, message)); } public static void logError(String message, Throwable e) { getDefault().getLog().log(new Status(Status.ERROR, PLUGIN_ID, message, e)); } public String getVersion() { return version; } }