/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* See LICENSE.txt included in this distribution for the specific
* language governing permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
*/
package org.opensolaris.opengrok.management;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.rmi.registry.LocateRegistry;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.NotificationFilter;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.timer.Timer;
import org.opensolaris.opengrok.Info;
import org.opensolaris.opengrok.OpenGrokLogger;
import org.opensolaris.opengrok.configuration.Configuration;
// PMD thinks this import is unused (confused because it's static?)
import org.opensolaris.opengrok.util.IOUtils;
import static org.opensolaris.opengrok.management.Constants.*; // NOPMD
/**
* OG Agent main class.
* Class for starting the basic components:
* Monitor and JMX and HTTP Connectors.
* @author Jan S Berg
*/
final public class OGAgent {
Properties props;
private static final Logger log = Logger.getLogger(OGAgent.class.getName());
private MBeanServer server = null;
@SuppressWarnings({ "PMD.SystemPrintln", "resource" })
private static boolean loadProperties(File file, InputStream in, Properties props) {
boolean ret = false;
InputStream stream = in;
try {
if (file != null) {
stream = new FileInputStream(file);
}
props.load(stream);
ret = true;
} catch (IOException e) {
log.warning("Failed to read configuration: " + e.getMessage());
log.log(Level.FINE, "loadProperties", e);
ret = false;
} finally {
IOUtils.close(stream);
}
return ret;
}
/**
* Startup method. Uses {@code /etc/opengrok/opengrok.properties} as
* defaults if available.
* @param args .. --agent=... --config=...
*/
@SuppressWarnings("PMD.SystemPrintln")
public static void main(final String args[]) {
Properties props = new Properties();
// Load default values
boolean success = loadProperties(null,
OGAgent.class.getResourceAsStream("oga.properties"), props);
File file = new File("/etc/opengrok/opengrok.properties");
if (file.exists()) {
success = loadProperties(file, null, props);
}
// System properties should override default properties
props.putAll(System.getProperties());
// @todo Add support for longopts!!
for (int i = 0; success && i < args.length; i++) {
if (args[i].startsWith("--agent=")) {
props.setProperty("agent", args[i].substring("--agent=".length()));
} else if (args[i].startsWith("--config=")) {
file = new File(args[i].substring("--config=".length()));
if (file.exists()) {
success = loadProperties(file, null, props);
} else {
success = false;
log.severe("Cannot load file '" + file.getAbsolutePath()
+ "': No such file");
}
}
}
URI uri = null;
if (success) {
try {
uri = new URI(props.getProperty("agent"));
} catch (URISyntaxException ex) {
success = false;
log.severe("Failed to decode agent url: " + ex.getMessage());
log.log(Level.FINE, "main", ex);
}
}
if (uri != null) {
setIfNotSet(props, LOG_PATH, uri.getPath() + "/log");
setIfNotSet(props, CONFIG_FILE,
uri.getPath() + "/etc/configuration.xml");
setIfNotSet(props, JMX_HOST, uri.getHost());
setIfNotSet(props, JMX_PORT, String.valueOf(uri.getPort()));
setIfNotSet(props, RMI_PORT, String.valueOf(uri.getPort() + 1));
setIfNotSet(props, JMX_URL,
"service:jmx:rmi://" + props.getProperty(JMX_HOST) + ":" +
props.getProperty(JMX_PORT) + "/jndi/rmi://" +
props.getProperty(JMX_HOST) + ":" +
props.getProperty(RMI_PORT) + "/opengrok");
success = createLogger(props);
}
if (success) {
OGAgent oga = new OGAgent(props);
try {
oga.runOGA();
} catch (MalformedURLException e) {
log.severe("Could not create connector server: " + e.getMessage());
log.log(Level.FINE, "main", e);
System.exit(1);
} catch (IOException e) {
log.severe("Could not start connector server: " + e.getMessage());
log.log(Level.FINE, "main", e);
System.exit(2);
} catch (Exception ex) {
log.severe(ex.getMessage());
log.log(Level.FINE, "main", ex);
System.exit(1);
}
} else {
System.exit(1);
}
}
private OGAgent(Properties props) {
this.props = props;
}
/**
* Set a property if it is not already set to some value.
*/
private static void setIfNotSet(Properties props, String key, String val) {
if (!props.keySet().contains(key)) {
props.setProperty(key, val);
}
}
@SuppressWarnings("boxing")
private void runOGA() throws MalformedURLException, IOException, JMException {
String javaver = System.getProperty("java.version");
log.info("Starting " + Info.getFullVersion() +
" JMX Agent, with java version " + javaver);
//create mbeanserver
ArrayList<MBeanServer> mbservs = MBeanServerFactory.findMBeanServer(null);
log.fine("Finding MBeanservers, size " + mbservs.size());
if (mbservs.isEmpty()) {
server = MBeanServerFactory.createMBeanServer();
} else {
server = mbservs.get(0);
}
//instantiate and register OGAManagement
ObjectName manager = new ObjectName("OGA:name=Management");
server.registerMBean(Management.getInstance(props), manager);
//instantiate and register OGA:JMXConfiguration
ObjectName config = new ObjectName("OGA:name=JMXConfiguration");
JMXConfiguration jc = new JMXConfiguration();
server.registerMBean(jc, config);
//instantiate and register Timer service and resource purger
createIndexTimer(props);
log.info("MBeans registered");
// Create and start connector server
String urlString = props.getProperty(JMX_URL);
HashMap<String, Object> env = new HashMap<String, Object>();
JMXServiceURL url = new JMXServiceURL(urlString);
// If the protocol is RMI we need to have an RMI registry running.
// Start an embedded registry if so requested.
if (url.getProtocol().equals(RMI_PROTOCOL) &&
Boolean.parseBoolean(props.getProperty(RMI_START))) {
int rmiport = Integer.parseInt(props.getProperty(RMI_PORT));
log.log(Level.FINE, "Starting RMI registry on port {0}", rmiport);
LocateRegistry.createRegistry(rmiport);
}
log.log(Level.FINE, "Starting JMX connector on {0}", urlString);
JMXConnectorServer connectorServer =
JMXConnectorServerFactory.newJMXConnectorServer(url, env, server);
connectorServer.start();
log.info("OGA is ready and running...");
}
private void createIndexTimer(Properties properties) throws JMException {
//instantiate, register and start the Timer service
ObjectName timer = new ObjectName("service:name=timer");
server.registerMBean(new Timer(), timer);
server.invoke(timer, "start", null, null);
log.info("Started timer service");
boolean enabled = Boolean.parseBoolean(properties
.getProperty(Configuration.PROPERTY_KEY_PREFIX + "management.indexer.enabled"));
int period = Integer.parseInt(properties
.getProperty(Configuration.PROPERTY_KEY_PREFIX + "management.indexer.sleeptime"));
log.fine("Indexer enabled: " + enabled);
log.fine("Indexer period: " + period + " seconds");
//instantiate and register resource purger
ObjectName indexRunner = new ObjectName("OGA:name=AgentIndexRunner," + "source=timer");
server.registerMBean(AgentIndexRunner.getInstance(enabled), indexRunner);
// Add index notification to timer (read from
// Configuration.PROPERTY_KEY_PREFIX + management.indexer.sleeptime property).
Date date = new Date(System.currentTimeMillis() + Timer.ONE_SECOND * 5);
Long longPeriod = Long.valueOf(period * Timer.ONE_SECOND);
Integer id = (Integer) server.invoke(timer, "addNotification",
new Object[]{"timer.notification", // Type
"Time to index again", // Message
null, // user data
date, // Start time
longPeriod, // Period
},
new String[]{String.class.getName(),
String.class.getName(),
Object.class.getName(),
Date.class.getName(),
"long",});
// Add indexer as listener to index notifications
NotificationFilter filter = new TimerFilter(id);
server.addNotificationListener(timer, indexRunner, filter, null);
}
@SuppressWarnings("PMD.SystemPrintln")
private static boolean createLogger(Properties props) {
boolean ret = true;
String OGAlogpath = props
.getProperty(Configuration.PROPERTY_KEY_PREFIX + "management.logging.path");
Level loglevel = null;
try {
loglevel = Level.parse(props
.getProperty(Configuration.PROPERTY_KEY_PREFIX + "management.logging.filelevel"));
} catch (Exception exll) {
loglevel = Level.FINE;
}
Level consoleloglevel = null;
try {
consoleloglevel = Level.parse(props
.getProperty(Configuration.PROPERTY_KEY_PREFIX + "management.logging.consolelevel"));
} catch (Exception excl) {
consoleloglevel = Level.INFO;
}
try {
OpenGrokLogger.setupLogger(OGAlogpath, loglevel, consoleloglevel);
} catch (IOException ex) {
log.warning("OGAgent failed set up logging: " + ex.getMessage());
ret = false;
}
return ret;
}
}