/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wso2.carbon.launcher;
import org.wso2.carbon.launcher.config.CarbonLaunchConfig;
import org.wso2.carbon.launcher.utils.Utils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.wso2.carbon.launcher.Constants.CARBON_HOME;
import static org.wso2.carbon.launcher.Constants.DEFAULT_PROFILE;
import static org.wso2.carbon.launcher.Constants.ExitCodes;
import static org.wso2.carbon.launcher.Constants.LAUNCH_PROPERTIES_FILE;
import static org.wso2.carbon.launcher.Constants.LOG_LEVEL_WARN;
import static org.wso2.carbon.launcher.Constants.PAX_DEFAULT_SERVICE_LOG_LEVEL;
import static org.wso2.carbon.launcher.Constants.PAX_LOGGING_PROPERTIES_FILE;
import static org.wso2.carbon.launcher.Constants.PAX_LOGGING_PROPERTY_FILE_KEY;
import static org.wso2.carbon.launcher.Constants.PAX_LOG_SERVICE_RANKING_LEVEL;
import static org.wso2.carbon.launcher.Constants.PROFILE;
import static org.wso2.carbon.launcher.Constants.RUNTIME_PATH;
/**
* Starts a Carbon server instance.
*
* @since 5.0.0
*/
public class Main {
private static final Logger logger = Logger.getLogger(Main.class.getName());
/**
* @param args arguments
*/
public static void main(String[] args) {
//get the starting time to calculate the server startup time duration
if (System.getProperty(Constants.START_TIME) == null) {
System.setProperty(Constants.START_TIME, System.currentTimeMillis() + "");
}
// 1) Process command line arguments.
processCmdLineArgs(args);
// 2) Initialize and/or verify System properties
initAndVerifySysProps();
// 3) Load the Carbon start configuration
CarbonLaunchConfig config = loadCarbonLaunchConfig();
CarbonServer carbonServer = new CarbonServer(config);
// 4) Register a shutdown hook to stop the server
registerShutdownHook(carbonServer);
// 5) Write pid to carbon.pid file
writePID(System.getProperty(RUNTIME_PATH));
// 6) Start Carbon server.
try {
// This method launches the OSGi framework, loads all the bundles and starts Carbon server completely.
carbonServer.start();
// Checking whether a server restart is required.
boolean restart = Boolean.parseBoolean(System.getProperty("carbon.server.restart"));
if (restart) {
// Here in this implementation we do not simply restart the OSGi framework. This could lead up to
// memory leaks. Hence we do a complete JVM level restart. Exit state 121 is a special value.
// Once the startup script receives this value, it restarts the JVM with the same arguments.
System.exit(ExitCodes.RESTART_ACTION);
} else {
System.exit(ExitCodes.SUCCESSFUL_TERMINATION);
}
} catch (Throwable e) {
// We need to invoke the stop method of the CarbonServer to allow the server to cleanup itself.
carbonServer.stop();
logger.log(Level.SEVERE, e.getMessage(), e);
System.exit(ExitCodes.UNSUCCESSFUL_TERMINATION);
}
}
/**
* Loads Carbon launch configuration from the launch.properties file.
*
* @return CarbonLaunchConfig
*/
private static CarbonLaunchConfig loadCarbonLaunchConfig() {
File launchPropFile = Utils.getLaunchConfigDirectory().resolve(LAUNCH_PROPERTIES_FILE).toFile();
if (launchPropFile.exists()) {
logger.log(Level.FINE, "Loading the Carbon launch configuration from the file " +
launchPropFile.getAbsolutePath());
return new CarbonLaunchConfig(launchPropFile);
} else {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Loading the Carbon launch configuration from the launch.properties file " +
"in the classpath");
}
return new CarbonLaunchConfig();
}
}
/**
* Registers a new virtual-machine shutdown hook.
*
* @param carbonServer Carbon server to stop
*/
private static void registerShutdownHook(final CarbonServer carbonServer) {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
carbonServer.stop();
}
});
}
/**
* Initialize and verify system properties.
*/
private static void initAndVerifySysProps() {
String carbonHome = System.getProperty(CARBON_HOME);
if (carbonHome == null || carbonHome.length() == 0) {
String msg = "carbon.home system property must be set before starting the server";
logger.log(Level.SEVERE, msg);
throw new RuntimeException(msg);
}
String profileName = System.getProperty(PROFILE);
if (profileName == null || profileName.length() == 0) {
System.setProperty(PROFILE, DEFAULT_PROFILE);
}
// Set log level for Pax logger to WARN and log service ranking to maximum value.
System.setProperty(PAX_DEFAULT_SERVICE_LOG_LEVEL, LOG_LEVEL_WARN);
System.setProperty(PAX_LOG_SERVICE_RANKING_LEVEL, String.valueOf(Integer.MAX_VALUE));
Path paxLoggingPropertiesFile = Paths.get(carbonHome, "conf", "etc", PAX_LOGGING_PROPERTIES_FILE);
if (paxLoggingPropertiesFile.toFile().exists()) {
System.setProperty(PAX_LOGGING_PROPERTY_FILE_KEY, paxLoggingPropertiesFile.toAbsolutePath().toString());
logger.log(Level.FINE, "Setting pax logging properties file path to : " +
paxLoggingPropertiesFile.toAbsolutePath().toString());
} else {
String msg = PAX_LOGGING_PROPERTIES_FILE + " should be available to start the server";
logger.log(Level.SEVERE, msg);
throw new RuntimeException(msg);
}
}
/**
* Process command line arguments and set corresponding system properties.
*
* @param args cmd line args
*/
public static void processCmdLineArgs(String[] args) {
// Set the System properties
Arrays.asList(args)
.stream()
.filter(arg -> arg.startsWith("-D"))
.forEach(arg -> {
int indexOfEq = arg.indexOf('=');
String property;
String value;
if (indexOfEq != -1) {
property = arg.substring(2, indexOfEq);
value = arg.substring(indexOfEq + 1);
} else {
property = arg.substring(2);
value = "true";
}
System.setProperty(property, value);
});
}
/**
* Write the process ID of this process to the file.
*
* @param runtimePath wso2.runtime.path sys property value.
*/
private static void writePID(String runtimePath) {
String[] cmd = {"bash", "-c", "echo $PPID"};
Process p;
String pid = "";
try {
p = Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
//Ignored. We might be invoking this on a Window platform. Therefore if an error occurs
//we simply ignore the error.
return;
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream(),
StandardCharsets.UTF_8))) {
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
pid = builder.toString();
} catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
if (pid.length() != 0) {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(Paths.get(runtimePath, "runtime.pid").toString()),
StandardCharsets.UTF_8))) {
writer.write(pid);
} catch (IOException e) {
logger.log(Level.WARNING, "Cannot write runtime.pid file");
}
}
}
}