--- /dev/null
+/*
+ * JCarder -- cards Java programs to keep threads disentangled
+ *
+ * Copyright (C) 2006-2007 Enea AB
+ * Copyright (C) 2007 Ulrik Svensson
+ * Copyright (C) 2007 Joel Rosdahl
+ *
+ * This program is made available under the GNU GPL version 2, with a special
+ * exception for linking with JUnit. See the accompanying file LICENSE.txt for
+ * details.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+package com.enea.jcarder.agent;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.instrument.Instrumentation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+
+import com.enea.jcarder.agent.instrument.ClassTransformer;
+import com.enea.jcarder.agent.instrument.InstrumentConfig;
+import com.enea.jcarder.util.BuildInformation;
+import com.enea.jcarder.util.logging.AppendableHandler;
+import com.enea.jcarder.util.logging.Handler;
+import com.enea.jcarder.util.logging.Logger;
+import dstm2.Init;
+
+/**
+ * This is the main class of the JCarder Java agent. It will initialize JCarder
+ * and register a ClassTransformer that is called by the JVM each time a class
+ * is loaded.
+ */
+public final class JavaAgent {
+
+ private static final String DUMP_PROPERTY = "jcarder.dump";
+ private static final String LOGLEVEL_PROPERTY = "jcarder.loglevel";
+ private static final String LOG_FILENAME = "jcarder.log";
+
+ private final InstrumentConfig mConfig = new InstrumentConfig();
+ private Logger mLogger;
+ PrintWriter mLogWriter;
+ private File mOutputDir;
+ private Logger.Level mLogLevel;
+ private static final String OUTPUTDIR_PROPERTY = "jcarder.outputdir";
+
+ private JavaAgent() { }
+
+ /**
+ * This method is called by the JVM when the JVM is started with the
+ * -javaagent command line parameter.
+ */
+ public static void premain(final String args,
+ final Instrumentation instrumentation)
+ throws Exception {
+ Init.init();
+ JavaAgent javaAgent = new JavaAgent();
+ javaAgent.init(instrumentation);
+ }
+
+ private void init(Instrumentation instrumentation)
+ throws Exception {
+ handleProperties();
+ initLogger();
+ mLogger.info("Starting " + BuildInformation.getShortInfo() + " agent");
+ logJvmInfo();
+ EventListener listener = EventListener.create(mLogger, mOutputDir);
+ ClassTransformer classTransformer =
+ new ClassTransformer(mLogger, mOutputDir, mConfig);
+ instrumentation.addTransformer(classTransformer);
+ StaticEventListener.setListener(listener);
+ mLogger.info("JCarder agent initialized\n");
+ }
+
+ private void initLogger() {
+ File logFile = new File(mOutputDir, LOG_FILENAME);
+ if (logFile.exists()) {
+ logFile.delete();
+ }
+ FileWriter fileWriter;
+ try {
+ fileWriter = new FileWriter(logFile);
+ } catch (IOException e) {
+ System.err.println("Failed to open log file \""
+ + logFile + "\": " + e.getMessage());
+ return;
+ }
+ mLogWriter = new PrintWriter(new BufferedWriter(fileWriter));
+ AppendableHandler fileHandler = new AppendableHandler(mLogWriter);
+ AppendableHandler consoleHandler =
+ new AppendableHandler(System.err, Logger.Level.INFO, "{message}\n");
+
+ Thread hook = new Thread() {
+ public void run() {
+ mLogWriter.flush();
+ }
+ };
+ Runtime.getRuntime().addShutdownHook(hook);
+
+ Collection<Handler> handlers = new ArrayList<Handler>();
+ handlers.add(fileHandler);
+ handlers.add(consoleHandler);
+ mLogger = new Logger(handlers, mLogLevel);
+ }
+
+ private void logJvmInfo() {
+ Enumeration<?> properties = System.getProperties().propertyNames();
+ while (properties.hasMoreElements()) {
+ String key = (String) properties.nextElement();
+ if (key.startsWith("java.vm.")) {
+ mLogger.config(key + ": " + System.getProperty(key));
+ }
+ }
+ }
+
+ private void handleProperties() throws IOException {
+ handleDumpProperty();
+ handleLogLevelProperty();
+ handleOutputDirProperty();
+ }
+
+ private void handleDumpProperty() {
+ mConfig.setDumpClassFiles(Boolean.getBoolean(DUMP_PROPERTY));
+ }
+
+ private void handleLogLevelProperty() {
+ String logLevelValue = System.getProperty(LOGLEVEL_PROPERTY, "fine");
+ Logger.Level logLevel = Logger.Level.fromString(logLevelValue);
+ if (logLevel != null) {
+ mLogLevel = logLevel;
+ } else {
+ System.err.print("Bad loglevel; should be one of ");
+ System.err.println(Logger.Level.getEnumeration());
+ System.err.println();
+ System.exit(1);
+ }
+ }
+
+ private void handleOutputDirProperty() throws IOException {
+ final String property = System.getProperty(OUTPUTDIR_PROPERTY, ".");
+ mOutputDir = new File(property).getCanonicalFile();
+ if (!mOutputDir.isDirectory()) {
+ mOutputDir.mkdirs();
+ }
+ }
+}