blob: c51defaed6df6519e08572c369d8fb91006e52c0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2011 Chair for Applied Software Engineering,
* Technische Universitaet Muenchen.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
******************************************************************************/
package org.eclipse.emf.emfstore.server;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.Timer;
import org.eclipse.core.runtime.ILogListener;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.emfstore.common.ResourceFactoryRegistry;
import org.eclipse.emf.emfstore.common.model.util.FileUtil;
import org.eclipse.emf.emfstore.common.model.util.ModelUtil;
import org.eclipse.emf.emfstore.server.accesscontrol.AccessControlImpl;
import org.eclipse.emf.emfstore.server.connection.ConnectionHandler;
import org.eclipse.emf.emfstore.server.connection.xmlrpc.XmlRpcAdminConnectionHander;
import org.eclipse.emf.emfstore.server.connection.xmlrpc.XmlRpcConnectionHandler;
import org.eclipse.emf.emfstore.server.core.AdminEmfStoreImpl;
import org.eclipse.emf.emfstore.server.core.EmfStoreImpl;
import org.eclipse.emf.emfstore.server.core.helper.HistoryCache;
import org.eclipse.emf.emfstore.server.exceptions.FatalEmfStoreException;
import org.eclipse.emf.emfstore.server.exceptions.StorageException;
import org.eclipse.emf.emfstore.server.model.ModelFactory;
import org.eclipse.emf.emfstore.server.model.ServerSpace;
import org.eclipse.emf.emfstore.server.model.accesscontrol.ACUser;
import org.eclipse.emf.emfstore.server.model.accesscontrol.AccesscontrolFactory;
import org.eclipse.emf.emfstore.server.model.accesscontrol.roles.RolesFactory;
import org.eclipse.emf.emfstore.server.startup.EmfStoreValidator;
import org.eclipse.emf.emfstore.server.startup.ExtensionManager;
import org.eclipse.emf.emfstore.server.startup.MigrationManager;
import org.eclipse.emf.emfstore.server.storage.ResourceStorage;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
/**
* The {@link EmfStoreController} is controlling startup and shutdown of the
* EmfStore.
*
* @author koegel
* @author wesendonk
*/
public class EmfStoreController implements IApplication, Runnable {
private EmfStore emfStore;
private AdminEmfStore adminEmfStore;
private AccessControlImpl accessControl;
private Set<ConnectionHandler<? extends EmfStoreInterface>> connectionHandlers;
private Properties properties;
private ServerSpace serverSpace;
private Resource resource;
private static EmfStoreController instance;
private HistoryCache historyCache;
/**
* The period of time in seconds between executing the clean memory task.
*/
private static final int CLEAN_MEMORY_TASK_PERIOD = 10;
/**
* {@inheritDoc}
*
* @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext)
*/
public Object start(IApplicationContext context) throws FatalEmfStoreException {
run(true);
instance = null;
ModelUtil.logInfo("Server is STOPPED.");
return IApplication.EXIT_OK;
}
/**
* Run the server.
*
* @param waitForTermination
* true if the server should force the calling thread to wait for
* its termination
* @throws FatalEmfStoreException
* if the server fails fatally
*/
public void run(boolean waitForTermination) throws FatalEmfStoreException {
if (instance != null) {
throw new FatalEmfStoreException("Another EmfStore Controller seems to be running already!");
}
instance = this;
serverHeader();
initLogging();
// copy es.properties file to workspace if not existent
copyFileToWorkspace(ServerConfiguration.getConfFile(), "es.properties",
"Couldn't copy es.properties file to config folder.",
"Default es.properties file was copied to config folder.");
properties = initProperties();
new MigrationManager().migrateModel();
this.serverSpace = initServerSpace();
handleStartupListener();
historyCache = initHistoryCache();
accessControl = initAccessControl(serverSpace);
emfStore = new EmfStoreImpl(serverSpace, accessControl);
adminEmfStore = new AdminEmfStoreImpl(serverSpace, accessControl);
// copy keystore file to workspace if not existent
copyFileToWorkspace(ServerConfiguration.getServerKeyStorePath(), ServerConfiguration.SERVER_KEYSTORE_FILE,
"Failed to copy keystore.", "Keystore was copied to server workspace.");
connectionHandlers = initConnectionHandlers();
if (Boolean.parseBoolean(ServerConfiguration.getProperties().getProperty(
ServerConfiguration.PERFORM_CLEAN_MEMORY_TASK, ServerConfiguration.PERFORM_CLEAN_MEMORY_TASK_DEFAULT))) {
new Timer().schedule(new CleanMemoryTask(serverSpace.eResource().getResourceSet()),
CLEAN_MEMORY_TASK_PERIOD * 1000, CLEAN_MEMORY_TASK_PERIOD * 1000);
}
handlePostStartupListener();
ModelUtil.logInfo("Initialitation COMPLETE.");
ModelUtil.logInfo("Server is RUNNING...");
if (waitForTermination) {
waitForTermination();
}
}
private void initLogging() {
Platform.getLog(Platform.getBundle("org.eclipse.emf.emfstore.common.model")).addLogListener(new ILogListener() {
public void logging(IStatus status, String plugin) {
if (status.getSeverity() == IStatus.INFO) {
System.out.println(status.getMessage());
} else if (!status.isOK()) {
System.err.println(status.getMessage());
Throwable exception = status.getException();
if (exception != null) {
exception.printStackTrace(System.err);
}
}
}
});
}
private void handleStartupListener() {
String property = ServerConfiguration.getProperties().getProperty(ServerConfiguration.LOAD_STARTUP_LISTENER,
ServerConfiguration.LOAD_STARTUP_LISTENER_DEFAULT);
if (ServerConfiguration.TRUE.equals(property)) {
ModelUtil.logInfo("Notifying startup listener");
ExtensionManager.notifyStartupListener(serverSpace.getProjects());
}
}
private void handlePostStartupListener() {
String property = ServerConfiguration.getProperties().getProperty(
ServerConfiguration.LOAD_POST_STARTUP_LISTENER, ServerConfiguration.LOAD_STARTUP_LISTENER_DEFAULT);
if (ServerConfiguration.TRUE.equals(property)) {
ModelUtil.logInfo("Notifying post startup listener");
ExtensionManager.notifyPostStartupListener(serverSpace, accessControl, connectionHandlers);
}
}
private void copyFileToWorkspace(String target, String source, String failure, String success) {
File keyStore = new File(target);
if (!keyStore.exists()) {
try {
FileUtil.copyFile(getClass().getResourceAsStream(source), keyStore);
} catch (IOException e) {
ModelUtil.logWarning("Copy of file from " + source + " to " + target + " failed", e);
}
}
}
private HistoryCache initHistoryCache() {
HistoryCache cache = new HistoryCache();
cache.initCache(serverSpace.getProjects());
ModelUtil.logInfo("History cache has been initialized.");
return cache;
}
private Set<ConnectionHandler<? extends EmfStoreInterface>> initConnectionHandlers() throws FatalEmfStoreException {
Set<ConnectionHandler<? extends EmfStoreInterface>> connectionHandlers = new HashSet<ConnectionHandler<? extends EmfStoreInterface>>();
// crate XML RPC connection handlers
XmlRpcConnectionHandler xmlRpcConnectionHander = new XmlRpcConnectionHandler();
xmlRpcConnectionHander.init(emfStore, accessControl);
connectionHandlers.add(xmlRpcConnectionHander);
XmlRpcAdminConnectionHander xmlRpcAdminConnectionHander = new XmlRpcAdminConnectionHander();
xmlRpcAdminConnectionHander.init(adminEmfStore, accessControl);
connectionHandlers.add(xmlRpcAdminConnectionHander);
return connectionHandlers;
}
/**
* Returns the history cache.
*
* @return the history cache.
*/
public HistoryCache getHistoryCache() {
return historyCache;
}
private ServerSpace initServerSpace() throws FatalEmfStoreException {
ResourceStorage storage = initStorage();
URI resourceUri = storage.init(properties);
ResourceSet resourceSet = new ResourceSetImpl();
resourceSet.setResourceFactoryRegistry(new ResourceFactoryRegistry());
resourceSet.getLoadOptions().putAll(ModelUtil.getResourceLoadOptions());
resource = resourceSet.createResource(resourceUri);
try {
resource.load(ModelUtil.getResourceLoadOptions());
if (properties.getProperty(ServerConfiguration.VALIDATE_SERVERSPACE_ON_SERVERSTART, "true").equals("true")) {
ModelUtil.logInfo("Validating serverspace ...");
validateServerSpace(resource);
ModelUtil.logInfo("Validation complete.");
}
} catch (IOException e) {
throw new FatalEmfStoreException(StorageException.NOLOAD, e);
}
ServerSpace result = null;
EList<EObject> contents = resource.getContents();
for (EObject content : contents) {
if (content instanceof ServerSpace) {
result = (ServerSpace) content;
break;
}
}
if (result != null) {
result.setResource(resource);
} else {
// if no serverspace can be loaded, create one
ModelUtil.logInfo("Creating initial server space...");
result = ModelFactory.eINSTANCE.createServerSpace();
result.setResource(resource);
resource.getContents().add(result);
try {
result.save();
} catch (IOException e) {
throw new FatalEmfStoreException(StorageException.NOSAVE, e);
}
}
return result;
}
private void validateServerSpace(Resource resource) throws FatalEmfStoreException {
EList<EObject> contents = resource.getContents();
for (EObject object : contents) {
if (object instanceof ServerSpace) {
EmfStoreValidator emfStoreValidator = new EmfStoreValidator((ServerSpace) object);
String[] excludedProjects = ServerConfiguration.getSplittedProperty(
ServerConfiguration.VALIDATION_PROJECT_EXCLUDE,
ServerConfiguration.VALIDATION_PROJECT_EXCLUDE_DEFAULT);
emfStoreValidator.setExcludedProjects(Arrays.asList(excludedProjects));
try {
String level = ServerConfiguration.getProperties().getProperty(
ServerConfiguration.VALIDATION_LEVEL, ServerConfiguration.VALIDATION_LEVEL_DEFAULT);
emfStoreValidator.validate(Integer.parseInt(level));
} catch (NumberFormatException e) {
emfStoreValidator.validate(Integer.parseInt(ServerConfiguration.VALIDATION_LEVEL_DEFAULT));
}
}
}
}
/**
* Return the singleton instance of EmfStoreControler.
*
* @return the instance
*/
public static EmfStoreController getInstance() {
return instance;
}
private ResourceStorage initStorage() throws FatalEmfStoreException {
String className = properties.getProperty(ServerConfiguration.RESOURCE_STORAGE,
ServerConfiguration.RESOURCE_STORAGE_DEFAULT);
ResourceStorage resourceStorage;
final String failMessage = "Failed loading ressource storage!";
try {
ModelUtil.logInfo("Using RessourceStorage \"" + className + "\".");
resourceStorage = (ResourceStorage) Class.forName(className).getConstructor().newInstance();
return resourceStorage;
} catch (IllegalArgumentException e) {
ModelUtil.logException(failMessage, e);
throw new FatalEmfStoreException(failMessage, e);
} catch (SecurityException e) {
ModelUtil.logException(failMessage, e);
throw new FatalEmfStoreException(failMessage, e);
} catch (InstantiationException e) {
ModelUtil.logException(failMessage, e);
throw new FatalEmfStoreException(failMessage, e);
} catch (IllegalAccessException e) {
ModelUtil.logException(failMessage, e);
throw new FatalEmfStoreException(failMessage, e);
} catch (InvocationTargetException e) {
ModelUtil.logException(failMessage, e);
throw new FatalEmfStoreException(failMessage, e);
} catch (NoSuchMethodException e) {
ModelUtil.logException(failMessage, e);
throw new FatalEmfStoreException(failMessage, e);
} catch (ClassNotFoundException e) {
ModelUtil.logException(failMessage, e);
throw new FatalEmfStoreException(failMessage, e);
}
}
private AccessControlImpl initAccessControl(ServerSpace serverSpace) throws FatalEmfStoreException {
setSuperUser(serverSpace);
return new AccessControlImpl(serverSpace);
}
private void setSuperUser(ServerSpace serverSpace) throws FatalEmfStoreException {
String superuser = ServerConfiguration.getProperties().getProperty(ServerConfiguration.SUPER_USER,
ServerConfiguration.SUPER_USER_DEFAULT);
for (ACUser user : serverSpace.getUsers()) {
if (user.getName().equals(superuser)) {
return;
}
}
ACUser superUser = AccesscontrolFactory.eINSTANCE.createACUser();
superUser.setName(superuser);
superUser.setFirstName("super");
superUser.setLastName("user");
superUser.setDescription("default server admin (superuser)");
superUser.getRoles().add(RolesFactory.eINSTANCE.createServerAdmin());
serverSpace.getUsers().add(superUser);
try {
serverSpace.save();
} catch (IOException e) {
throw new FatalEmfStoreException(StorageException.NOSAVE, e);
}
ModelUtil.logInfo("added superuser " + superuser);
}
private Properties initProperties() {
File propertyFile = new File(ServerConfiguration.getConfFile());
Properties properties = new Properties();
try {
FileInputStream fis = new FileInputStream(propertyFile);
properties.load(fis);
ServerConfiguration.setProperties(properties);
fis.close();
ModelUtil.logInfo("Property file read. (" + propertyFile.getAbsolutePath() + ")");
} catch (IOException e) {
ModelUtil.logWarning("Property initialization failed, using default properties.", e);
}
return properties;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.equinox.app.IApplication#stop()
*/
public void stop() {
wakeForTermination();
for (ConnectionHandler<? extends EmfStoreInterface> handler : connectionHandlers) {
handler.stop(false);
}
ModelUtil.logInfo("Server was stopped.");
instance = null;
wakeForTermination();
}
/**
* Shutdown EmfStore due to an fatal exception.
*
* @param exception
* the fatal exception that triggered the shutdown
* @generated NOT
*/
public void shutdown(FatalEmfStoreException exception) {
ModelUtil.logWarning("Stopping all connection handlers...");
for (ConnectionHandler<? extends EmfStoreInterface> handler : connectionHandlers) {
ModelUtil.logWarning("Stopping connection handler \"" + handler.getName() + "\".");
handler.stop(true);
ModelUtil.logWarning("Connection handler \"" + handler.getName() + "\" stopped.");
}
ModelUtil.logException("Server was forcefully stopped.", exception);
ModelUtil.logException("Cause for server shutdown: ", exception.getCause());
wakeForTermination();
}
private synchronized void waitForTermination() {
try {
wait();
} catch (InterruptedException e) {
ModelUtil.logWarning("Waiting for termination was interrupted", e);
}
}
private synchronized void wakeForTermination() {
notify();
}
private void serverHeader() {
InputStream inputStream = getClass().getResourceAsStream("emfstore.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
try {
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
}
System.out.println("*----------*");
System.out.println("| EmfStore |");
System.out.println("*----------*");
}
/**
* {@inheritDoc}
*
* @see java.lang.Runnable#run()
*/
public void run() {
try {
run(false);
} catch (FatalEmfStoreException e) {
e.printStackTrace();
}
}
/**
* starts the server a new thread.
*
* @throws FatalEmfStoreException
* in case of failure
*/
public static void runAsNewThread() throws FatalEmfStoreException {
Thread thread = new Thread(new EmfStoreController());
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}