30N/A/*
30N/A * CDDL HEADER START
30N/A *
30N/A * The contents of this file are subject to the terms of the
30N/A * Common Development and Distribution License (the "License").
30N/A * You may not use this file except in compliance with the License.
30N/A *
30N/A * See LICENSE.txt included in this distribution for the specific
30N/A * language governing permissions and limitations under the License.
30N/A *
30N/A * When distributing Covered Code, include this CDDL HEADER in each
30N/A * file and include the License file at LICENSE.txt.
30N/A * If applicable, add the following below this CDDL HEADER, with the
30N/A * fields enclosed by brackets "[]" replaced with your own identifying
30N/A * information: Portions Copyright [yyyy] [name of copyright owner]
30N/A *
30N/A * CDDL HEADER END
30N/A */
30N/A
30N/A/*
1393N/A * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
30N/A */
30N/Apackage org.opensolaris.opengrok.configuration;
30N/A
58N/Aimport java.beans.XMLDecoder;
58N/Aimport java.beans.XMLEncoder;
58N/Aimport java.io.BufferedInputStream;
1185N/Aimport java.io.ByteArrayInputStream;
1185N/Aimport java.io.ByteArrayOutputStream;
30N/Aimport java.io.File;
30N/Aimport java.io.IOException;
58N/Aimport java.net.InetAddress;
58N/Aimport java.net.ServerSocket;
58N/Aimport java.net.Socket;
58N/Aimport java.net.SocketAddress;
58N/Aimport java.net.UnknownHostException;
320N/Aimport java.util.logging.Level;
320N/Aimport java.util.logging.Logger;
1185N/A
664N/Aimport org.opensolaris.opengrok.history.HistoryGuru;
570N/Aimport org.opensolaris.opengrok.util.Executor;
1195N/Aimport org.opensolaris.opengrok.util.IOUtils;
30N/A
30N/A/**
30N/A * The RuntimeEnvironment class is used as a placeholder for the current
58N/A * configuration this execution context (classloader) is using.
30N/A */
418N/Apublic final class RuntimeEnvironment {
1470N/A private Configuration config;
456N/A private final ThreadLocal<Configuration> threadConfig;
1190N/A
1470N/A private static final Logger log =
1470N/A Logger.getLogger(RuntimeEnvironment.class.getName());
1190N/A
30N/A private static RuntimeEnvironment instance = new RuntimeEnvironment();
1467N/A private static Boolean oldVM;
1190N/A
77N/A /**
77N/A * Get the one and only instance of the RuntimeEnvironment
77N/A * @return the one and only instance of the RuntimeEnvironment
77N/A */
30N/A public static RuntimeEnvironment getInstance() {
30N/A return instance;
30N/A }
1190N/A
30N/A /**
77N/A * Creates a new instance of RuntimeEnvironment. Private to ensure a
77N/A * singleton pattern.
30N/A */
30N/A private RuntimeEnvironment() {
1470N/A config = new Configuration();
145N/A threadConfig = new ThreadLocal<Configuration>() {
1461N/A @SuppressWarnings("synthetic-access")
145N/A @Override protected Configuration initialValue() {
1470N/A return config;
1190N/A }
145N/A };
58N/A }
77N/A
1463N/A /**
1463N/A * Makes testing easier by being able to drop the current env and replace it
1463N/A * with a virgin one.
1463N/A */
1463N/A RuntimeEnvironment resetForTest() {
1463N/A threadConfig.remove();
1463N/A instance = new RuntimeEnvironment();
1463N/A return instance;
1463N/A }
1467N/A
1467N/A /**
1467N/A * Check, whether the running JVM is older than version 1.7 .
1467N/A * @return {@code true} if older than 1.7
1467N/A */
1467N/A public static final boolean isOldJVM() {
1467N/A if (oldVM == null) {
1467N/A String[] s = System.getProperty("java.specification.version", "1.6")
1467N/A .split("\\.");
1467N/A int minor = 0;
1467N/A int major = 0;
1467N/A try {
1467N/A major = Integer.parseInt(s[0], 10);
1467N/A minor = Integer.parseInt(s[1], 10);
1467N/A
1467N/A } catch (Exception e) {
1467N/A // ignore
1467N/A }
1467N/A oldVM = Boolean.valueOf(major <= 1 && minor < 7);
1467N/A }
1467N/A return oldVM.booleanValue();
1467N/A }
1467N/A
77N/A /**
77N/A * Register this thread in the thread/configuration map (so that all
77N/A * subsequent calls to the RuntimeEnvironment from this thread will use
77N/A * the same configuration
1185N/A * @return this instance
77N/A */
1470N/A public static RuntimeEnvironment register() {
1470N/A instance.threadConfig.set(instance.config);
1470N/A return instance;
58N/A }
99N/A
1461N/A /**
1470N/A * Validate that I have a Exuberant ctags program I may use.
1470N/A * @return {@code true} if Exuberant ctags could be run.
1461N/A */
1470N/A public static boolean validateExuberantCtags() {
571N/A boolean ret = true;
1470N/A Executor executor = new Executor(new String[] {
1470N/A getConfig().getCtags(), "--version"});
1190N/A
570N/A executor.exec(false);
570N/A String output = executor.getOutputString();
570N/A if (output == null || output.indexOf("Exuberant Ctags") == -1) {
1327N/A log.warning("No Exuberant Ctags found in PATH!\n" +
1470N/A "(tried running '" + getConfig().getCtags() + "')\n" +
1327N/A "Please use option -c to specify path to a good Exuberant Ctags program\n"+
1327N/A "Or set it in java system property "
1327N/A + Configuration.CTAGS_CMD_PROPERTY_KEY);
571N/A ret = false;
345N/A }
570N/A
571N/A return ret;
99N/A }
1190N/A
77N/A /**
77N/A * Read an configuration file and set it as the current configuration.
77N/A * @param file the file to read
77N/A * @throws IOException if an error occurs
77N/A */
1470N/A public static void readConfig(File file) throws IOException {
1470N/A setConfig(Configuration.read(file));
58N/A }
1190N/A
77N/A /**
77N/A * Write the current configuration to a file
77N/A * @param file the file to write the configuration into
77N/A * @throws IOException if an error occurs
77N/A */
1470N/A public static void writeConfig(File file) throws IOException {
1470N/A getConfig().write(file);
58N/A }
1190N/A
77N/A /**
77N/A * Write the current configuration to a socket
77N/A * @param host the host address to receive the configuration
77N/A * @param port the port to use on the host
77N/A * @throws IOException if an error occurs
77N/A */
1470N/A public static void writeConfig(InetAddress host, int port) throws IOException {
58N/A Socket sock = new Socket(host, port);
58N/A XMLEncoder e = new XMLEncoder(sock.getOutputStream());
1470N/A e.writeObject(getConfig());
58N/A e.close();
1195N/A IOUtils.close(sock);
58N/A }
234N/A
1461N/A /**
1461N/A * Send the current configuration to the web application [server].
1461N/A * @throws IOException
1461N/A */
1470N/A protected void writeConfig() throws IOException {
1470N/A writeConfig(configServerSocket.getInetAddress(),
1461N/A configServerSocket.getLocalPort());
471N/A }
471N/A
1461N/A /**
1461N/A * Set the current configuration for this instance to the given parameter.
1461N/A * Involves invalidating known repositories of the {@link HistoryGuru}.
1470N/A * @param config configuration to set.
1461N/A */
1470N/A public static void setConfig(Configuration config) {
1470N/A instance.config = config;
1088N/A register();
1461N/A HistoryGuru.getInstance()
1470N/A .invalidateRepositories(config.getRepositories());
234N/A }
639N/A
1461N/A /**
1461N/A * Get the current configuration for this instance.
1461N/A * @return the current configuration.
1461N/A */
1470N/A public static Configuration getConfig() {
1470N/A return instance.threadConfig.get();
639N/A }
1190N/A
58N/A private ServerSocket configServerSocket;
1190N/A
77N/A /**
77N/A * Try to stop the configuration listener thread
77N/A */
1470N/A public static void stopConfigListenerThread() {
1470N/A IOUtils.close(instance.configServerSocket);
30N/A }
1190N/A
77N/A /**
77N/A * Start a thread to listen on a socket to receive new configurations
77N/A * to use.
77N/A * @param endpoint The socket address to listen on
77N/A * @return true if the endpoint was available (and the thread was started)
77N/A */
1470N/A public static boolean startConfigListenerThread(SocketAddress endpoint) {
58N/A boolean ret = false;
1190N/A
58N/A try {
1470N/A instance.configServerSocket = new ServerSocket();
1470N/A instance.configServerSocket.bind(endpoint);
58N/A ret = true;
1470N/A final ServerSocket sock = instance.configServerSocket;
58N/A Thread t = new Thread(new Runnable() {
1461N/A @SuppressWarnings("synthetic-access")
1054N/A @Override
58N/A public void run() {
1185N/A ByteArrayOutputStream bos = new ByteArrayOutputStream(1<<13);
58N/A while (!sock.isClosed()) {
471N/A Socket s = null;
1185N/A BufferedInputStream in = null;
58N/A try {
58N/A s = sock.accept();
1185N/A bos.reset();
1327N/A log.info("Re-configure request from " +
1185N/A s.getInetAddress().getHostAddress());
1185N/A in = new BufferedInputStream(s.getInputStream());
1185N/A byte[] buf = new byte[1024];
1185N/A int len;
1185N/A while ((len = in.read(buf)) != -1) {
1185N/A bos.write(buf, 0, len);
1185N/A }
1185N/A buf = bos.toByteArray();
1185N/A if (log.isLoggable(Level.FINE)) {
1327N/A log.fine("New config: \n" + new String(buf));
1185N/A }
1185N/A XMLDecoder d = new XMLDecoder(new ByteArrayInputStream(buf));
58N/A Object obj = d.readObject();
58N/A d.close();
1190N/A
58N/A if (obj instanceof Configuration) {
1470N/A setConfig((Configuration)obj);
1470N/A log.log(Level.INFO, "Configuration updated: {0}",
1470N/A getConfig().getSourceRoot());
58N/A }
58N/A } catch (IOException e) {
1327N/A log.warning("Error reading config file: " + e.getMessage());
1327N/A log.log(Level.FINE, "run", e);
688N/A } catch (RuntimeException e) {
1327N/A log.warning("Error parsing config file: " + e.getMessage());
1327N/A log.log(Level.FINE, "run", e);
58N/A } finally {
1195N/A IOUtils.close(s);
1195N/A IOUtils.close(in);
58N/A }
58N/A }
58N/A }
58N/A });
58N/A t.start();
58N/A } catch (UnknownHostException ex) {
1327N/A log.log(Level.FINE,"Problem resolving sender", ex);
1327N/A } catch (IOException ex) {
1327N/A log.log(Level.FINE,"I/O error when waiting for config", ex);
58N/A }
1190N/A
1470N/A if (!ret && instance.configServerSocket != null) {
1470N/A IOUtils.close(instance.configServerSocket);
58N/A }
1190N/A
58N/A return ret;
30N/A }
30N/A}