/*
* WebComponent.java
*
* Created on March 26, 2007, 6:04 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.atomojo.app;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.atomojo.app.admin.AdminApplication;
import org.atomojo.app.admin.ServerAdminApplication;
import org.atomojo.app.auth.AuthException;
import org.atomojo.app.auth.AuthProtocolService;
import org.atomojo.app.auth.AuthService;
import org.atomojo.app.auth.UserGuard;
import org.atomojo.app.client.Entry;
import org.atomojo.app.client.FeedClient;
import org.atomojo.app.client.FeedDestination;
import org.atomojo.app.client.Link;
import org.atomojo.app.client.Term;
import org.atomojo.app.client.Text;
import org.atomojo.app.db.DB;
import org.atomojo.app.db.DBAuthService;
import org.atomojo.app.db.DBInfo;
import org.atomojo.app.edit.EditApplication;
import org.atomojo.app.sync.FeedSynchronizer;
import org.infoset.xml.Document;
import org.infoset.xml.Element;
import org.restlet.Component;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.Server;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.Protocol;
import org.restlet.data.Status;
import org.restlet.routing.Router;
import org.restlet.routing.Template;
import org.restlet.routing.VirtualHost;
/**
*
* @author alex
*/
public class WebComponent extends Component {
public static final String LOG_NAME = "org.atomojo.app.www";
public static final String ATOMOJO_TMP_DIR = "org.atomojo.app.tmp.dir";
static final URI DB_TERM = URI.create("http://www.atomojo.org/O/configuration/db");
static final URI DB_AUTH_TERM = URI.create("http://www.atomojo.org/O/configuration/db/auth");
static final URI DB_NAME_TERM = URI.create("http://www.atomojo.org/O/configuration/db/name");
static final URI DB_GROUP_TERM = URI.create("http://www.atomojo.org/O/configuration/db/group");
static final URI DB_ALIAS_TERM = URI.create("http://www.atomojo.org/O/configuration/db/alias");
static final URI HOST_TERM = URI.create("http://www.atomojo.org/O/configuration/host");
static final URI REALM_TERM = URI.create("http://www.atomojo.org/O/configuration/realm");
static final URI REALM_NAME_TERM = URI.create("http://www.atomojo.org/O/configuration/realm/name");
static class AutoConfiguredHost {
VirtualHost vhost;
ServerConfiguration.Host host;
Date edited;
AutoConfiguredHost(VirtualHost vhost,ServerConfiguration.Host host,Date edited)
{
this.vhost = vhost;
this.host = host;
this.edited = edited;
}
}
class AutoConfiguration {
Link link;
Map<String,AutoConfiguredHost> hosts;
AutoConfiguration(Link link)
{
this.link = link;
this.hosts = new TreeMap<String,AutoConfiguredHost>();
}
public void configure() {
FeedClient client = new FeedClient(link.getLink());
client.setIdentity(link.getUsername(),link.getPassword());
getLogger().info("Loading configuration from "+link.getLink());
try {
boolean changes = false;
final List<Entry> hostsToDo = new ArrayList<Entry>();
final List<Entry> dbToDo = new ArrayList<Entry>();
final Map<String,Boolean> currentAuthList = new TreeMap<String,Boolean>();
final Map<String,Boolean> changedAuth = new TreeMap<String,Boolean>();
client.get(new FeedDestination() {
public void onFeed(Document feedDoc) {
}
public void onEntry(Document entryDoc) {
Entry entry = new Entry(entryDoc);
entry.index();
if (entry.getTerm(HOST_TERM)!=null) {
// store host entry till DB entries are done
hostsToDo.add(entry);
return;
}
if (entry.getTerm(DB_TERM)!=null) {
// store db entry till realms are done
dbToDo.add(entry);
return;
}
if (entry.getTerm(REALM_TERM)==null) {
// ignore non-realm entry
return;
}
Term nameT = entry.getTerm(REALM_NAME_TERM);
if (nameT==null) {
return;
}
String name = nameT.getFirstValue();
List<Link> services = entry.getLinks().get("service");
if (services==null) {
getLogger().warning("Missing 'service' relation link on realm "+name);
} else {
URI service = services.get(0).getLink();
AuthProtocolService auth = (AuthProtocolService)autoServices.get(name);
if (auth!=null) {
if (!auth.getServiceURI().equals(service)) {
// The service URI has changed
autoServices.remove(name);
auth = null;
changedAuth.put(name,Boolean.TRUE);
}
}
if (auth==null) {
getLogger().info("Adding realm "+name+" from service "+service);
auth = new AuthProtocolService();
Properties props = new Properties();
props.setProperty("href",service.toString());
try {
auth.init(props);
autoServices.put(name,auth);
currentAuthList.put(name,Boolean.TRUE);
} catch (AuthException ex) {
getLogger().log(Level.SEVERE,"Cannot initialize auth service for realm "+name,ex);
}
}
}
}
});
// Configure the databases
final Map<String,Boolean> currentDBList = new TreeMap<String,Boolean>();
for (Entry entry : dbToDo) {
Term nameT = entry.getTerm(DB_NAME_TERM);
if (nameT==null) {
getLogger().warning("Ignoring db entry that does not have name term "+DB_NAME_TERM);
return;
}
Term authT = entry.getTerm(DB_AUTH_TERM);
if (authT==null) {
getLogger().warning("Ignoring db entry that does not have auth term "+DB_AUTH_TERM);
return;
}
Term groupT = entry.getTerm(DB_GROUP_TERM);
Term aliasT = entry.getTerm(DB_ALIAS_TERM);
String dbName = nameT.getFirstValue();
String authName = authT.getFirstValue();
if (dbName==null) {
getLogger().warning("Ignoring db entry where name term "+DB_NAME_TERM+" does not have a value.");
return;
}
if (authName==null) {
getLogger().warning("Ignoring db entry where auth term "+DB_NAME_TERM+" does not have a value.");
return;
}
AuthService auth = services.get(authName);
if (auth==null) {
auth = autoServices.get(authName);
}
if (auth==null) {
getLogger().warning("Cannot configure db entry for "+dbName+" as auth service "+authName+" does not exist.");
return;
}
DBInfo dbinfo = autodbList.get(dbName);
Storage storage = null;
if (dbinfo==null) {
getLogger().info("Creating DB "+dbName);
DB db = new DB(getLogger(),new File(dbDir,dbName));
dbinfo = new DBInfo(dbName,db,auth,groupT!=null ? groupT.getFirstValue() : null, aliasT!=null ? aliasT.getFirstValue() : null);
storage = storageFactory.getStorage(db);
try {
getLogger().info("Connecting to DB "+dbName);
db.connect();
getLogger().info("Connected to DB "+dbName);
autodbList.put(dbName,dbinfo);
storage.start();
} catch (SQLException ex) {
getLogger().log(Level.SEVERE,"Cannot connect to database "+dbName,ex);
}
} else {
storage = storageFactory.getStorage(dbinfo.getDB());
}
currentDBList.put(dbName,Boolean.TRUE);
UserGuard adminGuard = new UserGuard(childContext,ChallengeScheme.HTTP_BASIC,"Atom Administrator",auth);
adminGuard.getRequiredGroups().add(AuthService.ADMIN_GROUP);
adminGuard.setNext(new AdminApplication(childContext,dbinfo.getDB(),storage,AtomApplication.RESOURCE_BASE));
admins.put(dbName,adminGuard);
for (Router router : adminRouters) {
router.attach("/admin/database/"+dbName,adminGuard).getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);
}
}
// Remove databases that have been removed from feed
for (String name : autodbList.keySet()) {
if (currentDBList.get(name)==null) {
// the database was removed
DBInfo dbinfo = autodbList.remove(name);
dbinfo.getDB().stop();
Storage storage = storageFactory.getStorage(dbinfo.getDB());
storage.stop();
Restlet admin = admins.remove(name);
if (admin!=null) {
for (Router router : adminRouters) {
router.detach(admin);
}
}
}
}
final Map<String,Boolean> currentEntries = new TreeMap<String,Boolean>();
for (Entry entry : hostsToDo) {
Text text = entry.getContent();
if (text==null || !text.isXML()) {
getLogger().warning("Ignoring host entry with missing or has incorrectly typed content for configuration.");
continue;
}
Element hostE = text.getContent();
if (hostE==null) {
getLogger().warning("Ignoring host entry with empty content.");
continue;
}
Date edited = null;
try {
edited = entry.getEdited();
if (edited==null) {
edited = entry.getUpdated();
}
} catch (ParseException ex) {
getLogger().warning("Ignoring host entry with malformed date in updated or edited element: "+ex.getMessage());
continue;
}
if (edited==null) {
getLogger().warning("Ignoring host entry without updated or edited element.");
continue;
}
currentEntries.put(entry.getId(),Boolean.TRUE);
AutoConfiguredHost autoHost = hosts.get(entry.getId());
if (autoHost!=null && (autoHost.edited.getTime()<edited.getTime() || changedAuth.get(autoHost.host.getDatabaseName())!=null)) {
// the entry has changed, so delete
getLogger().info("Entry changed for host "+autoHost.vhost.getHostDomain()+", reconfiguring.");
getHosts().remove(autoHost.vhost);
autoHost = null;
}
if (autoHost!=null) {
getLogger().info("Nothing has changed for host "+autoHost.vhost.getHostDomain());
continue;
}
// Create configuration
changes = true;
ServerConfiguration.Host host = config.createHost(hostE);
// Configure host
getLogger().info("Adding host "+host.getName());
VirtualHost vhost = createVirtualHost(host);
WebComponent.this.configure(vhost,host);
if (autoHost==null) {
hosts.put(entry.getId(),new AutoConfiguredHost(vhost,host,edited));
} else {
autoHost.vhost = vhost;
autoHost.host = host;
autoHost.edited = edited;
}
getHosts().add(vhost);
}
// remove all entries that have been deleted
List<String> toRemove = new ArrayList<String>();
for (String id : hosts.keySet()) {
if (currentEntries.get(id)==null) {
AutoConfiguredHost autoHost = hosts.get(id);
getLogger().info("Removing host "+autoHost.vhost.getHostDomain());
changes = true;
toRemove.add(id);
getHosts().remove(autoHost.vhost);
}
}
for (String id : toRemove) {
hosts.remove(id);
}
if (changes && WebComponent.this.isStarted()) {
getLogger().info("Updating hosts...");
WebComponent.this.updateHosts();
}
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Fatal error while getting auto configuration feed at "+link.getLink(),ex);
}
getLogger().info("Configuration "+link.getLink()+" loaded.");
}
}
class AutoConfProcess implements Runnable {
boolean run = true;
public void run() {
while (run) {
try {
for (AutoConfiguration autoconf : autoconfs) {
autoconf.configure();
if (!run) {
break;
}
}
getLogger().info("Waiting "+(config.getAutoConfigurationCheckWait()/1000)+"s to check configuration.");
Thread.currentThread().sleep(config.getAutoConfigurationCheckWait());
} catch (InterruptedException ex) {
}
}
getLogger().info("Auto configuration thread exiting.");
}
}
List<AutoConfiguration> autoconfs;
AutoConfProcess autoconfProc;
Thread autoconfThread;
Map<String,DB> dbList;
Map<String,DBInfo> dbConfList;
Map<String,DBInfo> autodbList;
Map<String,Storage> storageList;
Map<String,Storage> autoStorageList;
StorageFactory storageFactory;
FeedSynchronizer sync;
Thread syncThread;
ServerConfiguration config;
//Router adminRouter;
Map<String,AuthService> services;
Map<String,AuthService> autoServices;
Map<String,Restlet> admins;
File dbDir;
List<Router> adminRouters;
AtomicBoolean memoryManagerRunning;
Thread memoryManager;
Map<String,FileWriter> logs;
Context childContext;
Map<String,ComponentModule> modules;
/** Creates a new instance of WebComponent */
public WebComponent(File dbDir,Map<String,DB> dbList,StorageFactory storageFactory,ServerConfiguration serverConf) {
this.dbDir = dbDir;
this.dbList = dbList;
this.dbConfList = new TreeMap<String,DBInfo>();
this.autodbList = new TreeMap<String,DBInfo>();
this.storageList = new TreeMap<String,Storage>();
this.storageList = new TreeMap<String,Storage>();
this.storageFactory = storageFactory;
this.config = serverConf;
this.autoconfs = new ArrayList<AutoConfiguration>();
this.adminRouters = new ArrayList<Router>();
getLogService().setLoggerName(LOG_NAME);
getLogService().setEnabled(false);
this.logs = new TreeMap<String,FileWriter>();
this.childContext = getContext().createChildContext();
this.modules = new HashMap<String,ComponentModule>();
try {
this.storageFactory.init(childContext);
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot initialize storage factory with context.",ex);
}
// TODO: the keystore should be part of the server.conf
getContext().getParameters().add("keystorePath",serverConf.getKeystoreFile().getAbsolutePath());
getContext().getParameters().add("keystorePassword",serverConf.getKeystorePassword());
getContext().getParameters().add("keyPassword",serverConf.getKeyPassword());
for (ServerConfiguration.Interface iface : serverConf.getInterfaces()) {
if (iface.isSecure()) {
getLogger().info("https listening on "+iface.getAddress()+":"+iface.getPort());
Server server = getServers().add(Protocol.HTTPS, iface.getAddress().equals("*") ? null : iface.getAddress(), iface.getPort());
server.getContext().getParameters().add("keystorePath",serverConf.getKeystoreFile().getAbsolutePath());
server.getContext().getParameters().add("keystorePassword",serverConf.getKeystorePassword());
server.getContext().getParameters().add("keyPassword",serverConf.getKeyPassword());
} else {
getLogger().info("http listening on "+iface.getAddress()+":"+iface.getPort());
getServers().add(Protocol.HTTP, iface.getAddress().equals("*") ? null : iface.getAddress(), iface.getPort());
}
}
for (Server server : getServers()) {
try {
server.start();
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot start server.",ex);
}
}
// Add the clients
getClients().add(Protocol.FILE);
for (Protocol protocol : storageFactory.getStorageProtocols()) {
getClients().add(protocol);
}
childContext.getAttributes().put(ATOMOJO_TMP_DIR,serverConf.getTempDirectory());
services = new HashMap<String,AuthService>();
autoServices = new HashMap<String,AuthService>();
for (ServerConfiguration.Auth auth : serverConf.getAuthServices().values()) {
try {
AuthService service = auth.newInstance();
services.put(auth.getName(),service);
} catch (AuthException ex) {
getLogger().log(Level.SEVERE,"Cannot instantiate auth service "+auth.getName(),ex);
}
}
ServerAdminApplication admin = new ServerAdminApplication(childContext,dbConfList,autodbList,storageFactory);
for (ServerConfiguration.AdminHost adminHost : serverConf.getAdminHosts().values()) {
VirtualHost vhost = createVirtualHost(adminHost);
getHosts().add(vhost);
adminRouters.add(vhost);
String authName = adminHost.getAuthName();
if (authName==null) {
getLogger().severe("The admin interface is missing a named auth service.");
continue;
}
AuthService service = services.get(authName);
if (service==null) {
getLogger().severe("Cannot find auth service "+authName+" for admin interface.");
continue;
}
UserGuard adminGuard = new UserGuard(childContext,ChallengeScheme.HTTP_BASIC,"Atom Administrator",service);
adminGuard.getRequiredGroups().add(AuthService.ADMIN_GROUP);
adminGuard.setNext(admin);
vhost.attach("/admin",adminGuard);
}
admins = new HashMap<String,Restlet>();
Restlet lastAdmin = null;
for (final DB adminDB : dbList.values()) {
getLogger().info("Configuring database "+adminDB.getName()+" for administration");
ServerConfiguration.Database databaseConf = serverConf.getDatabases().get(adminDB.getName());
AuthService service = null;
if (databaseConf==null || databaseConf.getAuthName()==null) {
service = new DBAuthService();
Properties props = new Properties();
props.setProperty("database",adminDB.getName());
props.setProperty("dir", adminDB.getDatabaseDir().getAbsolutePath());
try {
service.init(props);
} catch (AuthException ex) {
getLogger().log(Level.SEVERE,"Cannot instantiate auth service for database "+adminDB.getName(),ex);
continue;
}
} else {
service = services.get(databaseConf.getAuthName());
if (service==null) {
getLogger().severe("Cannot find auth service "+databaseConf.getAuthName()+" for database "+adminDB.getName());
continue;
}
}
DBInfo dbinfo = new DBInfo(adminDB.getName(),adminDB,service,databaseConf==null ? null : databaseConf.getGroup(),databaseConf==null ? null : databaseConf.getGroupAlias());
dbConfList.put(dbinfo.getName(),dbinfo);
UserGuard adminGuard = new UserGuard(childContext,ChallengeScheme.HTTP_BASIC,"Atom Administrator",service);
adminGuard.getRequiredGroups().add(AuthService.ADMIN_GROUP);
try {
adminGuard.setNext(new AdminApplication(childContext,adminDB,storageFactory.getStorage(adminDB),AtomApplication.RESOURCE_BASE));
admins.put(adminDB.getName(),adminGuard);
for (Router router : adminRouters) {
router.attach("/admin/database/"+adminDB.getName(),adminGuard).getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);
}
lastAdmin = adminGuard;
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot add admin due to exception.",ex);
}
}
// Add the configured hosts
for (ServerConfiguration.Host hostConf : serverConf.getHosts().values()) {
VirtualHost vhost = createVirtualHost(hostConf);
getHosts().add(vhost);
try {
configure(vhost,hostConf);
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot configure host "+hostConf.getName()+" due to exception.",ex);
}
}
// Add the configured hosts
for (ServerConfiguration.ResourceHost resourceConf : serverConf.getResourceHosts().values()) {
VirtualHost vhost = createVirtualHost(resourceConf);
getHosts().add(vhost);
try {
configureResource(vhost,resourceConf);
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot configure host "+resourceConf.getName()+" due to exception.",ex);
}
}
for (Link link : serverConf.getAutoConfiguration()) {
addAutoConfiguration(link);
}
}
protected VirtualHost createVirtualHost(ServerConfiguration.Host host)
{
VirtualHost vhost = new VirtualHost(childContext);
getLogger().info("Creating virtual route for "+host.getName()+":"+host.getPort());
if (host.getAddress()!=null) {
getLogger().info("Restricting to address "+host.getAddress());
vhost.setServerAddress(host.getAddress());
}
if (host.getPort()>0) {
getLogger().info("Restricting to port "+host.getPort());
vhost.setHostPort(Integer.toString(host.getPort()));
} else {
vhost.setHostPort(".*");
}
if (host.getName()!=null && !host.getName().equals("*")) {
vhost.setHostDomain(host.getName());
}
return vhost;
}
protected void configure(VirtualHost vhost,ServerConfiguration.Host host)
throws Exception
{
DBInfo atomDB = dbConfList.get(host.getDatabaseName());
if (atomDB==null) {
atomDB = autodbList.get(host.getDatabaseName());
}
if (atomDB==null) {
throw new RuntimeException("Database "+host.getDatabaseName()+" does not exist.");
}
AuthService auth = atomDB.getAuthService();
if (auth==null) {
throw new RuntimeException("The auth service for database "+host.getDatabaseName()+" does not exist.");
}
Storage storage = storageFactory.getStorage(atomDB.getDB());
Restlet adminApp = admins.get(atomDB.getName());
if (adminApp==null) {
getLogger().warning("There is no admin restlet for "+atomDB.getName());
}
AtomApplication app = new AtomApplication(childContext,auth,atomDB.getDB(),storage,host.allowQueries());
Router router = new Router(childContext);
router.setDefaultMatchingMode(Template.MODE_STARTS_WITH);
router.attachDefault(app);
router.attach("/admin",adminApp).getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);
if (host.allowEditClient()) {
router.attach("/edit",new EditApplication(childContext,app));
}
Logger hostLog = Logger.getLogger("atomojo.host."+host.getName());
HostLogFilter logFilter = new HostLogFilter(hostLog);
logFilter.setNext(router);
vhost.attach(logFilter);
addHostLog(host.getName(),hostLog,host.getLogFile());
}
protected Restlet makeApp(DBInfo info,boolean allowQueries,boolean allowEditClient,String logName)
throws Exception
{
Storage storage = storageFactory.getStorage(info.getDB());
AtomApplication app = new AtomApplication(childContext,info.getAuthService(),info.getDB(),storage,allowQueries);
Restlet adminApp = admins.get(info.getName());
if (adminApp==null) {
getLogger().warning("There is no admin restlet for "+info.getName());
}
Router router = new Router(childContext);
router.setDefaultMatchingMode(Template.MODE_STARTS_WITH);
router.attachDefault(app);
router.attach("/admin",adminApp);
if (allowEditClient) {
router.attach("/edit",new EditApplication(childContext,app));
}
Logger hostLog = Logger.getLogger("atomojo.host."+logName);
HostLogFilter logFilter = new HostLogFilter(hostLog);
logFilter.setNext(router);
return logFilter;
}
protected void configureResource(VirtualHost vhost,final ServerConfiguration.ResourceHost host)
throws Exception
{
getLogger().info("Creating virtual route for resource host "+host.getName()+":"+host.getPort());
// TODO: this should be more dynamic with autoconf
final Map<String,Restlet> apps = new TreeMap<String,Restlet>();
final boolean checkNames = host.getDatabases().size()>0 ? true : false;
for (DBInfo info : dbConfList.values()) {
if (checkNames && !host.getDatabases().contains(info.getName())) {
continue;
}
String group = info.getGroup();
String alias = info.getAlias();
if (group==null) {
continue;
}
if (alias==null) {
alias = info.getName();
}
String key = group+"."+alias;
getLogger().info("Adding static APP key "+key);
Restlet app = makeApp(info,host.allowQueries(),host.allowEditClient(),key);
apps.put(key,app);
}
vhost.attach("/{group}/{alias}",new Restlet() {
public void handle(Request request, Response response) {
try {
String rgroup = request.getAttributes().get("group").toString();
String ralias = request.getAttributes().get("alias").toString();
String rkey = rgroup+"."+ralias;
Restlet restlet = apps.get(rkey);
if (restlet==null) {
for (DBInfo info : autodbList.values()) {
if (checkNames && !host.getDatabases().contains(info.getName())) {
continue;
}
String group = info.getGroup();
String alias = info.getAlias();
if (group==null) {
continue;
}
if (alias==null) {
alias = info.getName();
}
String key = group+"."+alias;
Restlet app = makeApp(info,host.allowQueries(),host.allowEditClient(),key);
getLogger().info("Adding dynamic APP key "+key);
apps.put(key,app);
restlet = app;
}
}
if (restlet!=null) {
restlet.handle(request, response);
} else {
response.setStatus(Status.CLIENT_ERROR_NOT_FOUND,"Cannot find APP instance for group "+rgroup+" and alias "+ralias);
}
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Exception while checking auto configuration list.",ex);
}
}
});
}
public void addHostLog(final String name,Logger log,File file)
throws IOException
{
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
final FileWriter w = new FileWriter(file,file.exists() ? true : false);
logs.put(name,w);
log.addHandler(new Handler() {
public void publish(LogRecord record) {
if (record.getLevel().intValue()==Level.FINE.intValue()) {
try {
w.write(record.getMessage());
w.write('\n');
w.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public void flush() {
try {
w.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void close() {
if (logs.get(name)!=null) {
try {
w.close();
logs.remove(name);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
});
}
void addAutoConfiguration(Link link)
{
AutoConfiguration autoconf = new AutoConfiguration(link);
autoconfs.add(autoconf);
}
protected void registerModule(Class<? extends ComponentModule> moduleClass)
throws InstantiationException,IllegalAccessException
{
ComponentModule module = moduleClass.newInstance();
if (modules.get(module.getName())!=null) {
getLogger().info("Ignoring module, already mapped: "+module.getName()+" -> "+module.getClass().getName());
return;
}
getLogger().info("Registering module: "+module.getName()+" -> "+module.getClass().getName());
modules.put(module.getName(), module);
}
protected void loadModules()
throws IOException
{
Enumeration<URL> locations = getClass().getClassLoader().getResources("META-INF/services/"+ComponentModule.class.getName());
while (locations.hasMoreElements()) {
URL location = locations.nextElement();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(location.openStream(), "UTF-8"));
String className = reader.readLine();
while (className != null) {
int hashPosition = className.indexOf('#');
if (hashPosition>0) {
className = className.substring(0, hashPosition);
}
className = className.trim();
if (className.length()>0) {
getLogger().fine("Loading module class: "+className);
try {
Class moduleClass = getClass().getClassLoader().loadClass(className);
registerModule(moduleClass);
} catch (ClassNotFoundException ex) {
getLogger().warning("Cannot load module due to missing class: "+ex.getMessage());
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot load module due to exception.",ex);
}
}
className = reader.readLine();
}
} finally {
if (reader != null) {
reader.close();
}
}
}
}
public void start()
throws Exception
{
loadModules();
for (ComponentModule module : modules.values()) {
module.init(getContext(),getInternalRouter());
module.start();
}
for (DBInfo dbinfo : dbConfList.values()) {
Storage storage = storageFactory.getStorage(dbinfo.getDB());
storage.start();
}
super.start();
if (autoconfs.size()>0) {
getLogger().info("Starting auto-configuration process...");
autoconfProc = new AutoConfProcess();
autoconfThread = new Thread(autoconfProc);
autoconfThread.start();
getLogger().info("...started.");
}
memoryManagerRunning = new AtomicBoolean(true);
memoryManager = new Thread(new Runnable() {
long sleepWait = 3*60*1000;
public void run() {
while (memoryManagerRunning.get()) {
try {
Thread.currentThread().sleep(sleepWait);
System.gc();
} catch (InterruptedException ex) {
}
}
}
});
memoryManager.start();
}
public void stop()
throws Exception
{
for (String name : logs.keySet()) {
FileWriter out = logs.get(name);
try {
out.close();
} catch (IOException ex) {
getLogger().log(Level.SEVERE,"Cannot close log file for host "+name,ex);
}
}
if (autoconfs.size()>0) {
getLogger().info("Stopping auto-configuration process...");
autoconfProc.run = false;
synchronized (autoconfThread) {
autoconfThread.interrupt();
}
autoconfThread.join(2000);
getLogger().info("...stopped.");
}
memoryManagerRunning.set(false);
memoryManager.interrupt();
for (DBInfo dbinfo : dbConfList.values()) {
dbinfo.getDB().stop();
Storage storage = storageFactory.getStorage(dbinfo.getDB());
storage.stop();
}
for (DBInfo dbinfo : autodbList.values()) {
dbinfo.getDB().stop();
Storage storage = storageFactory.getStorage(dbinfo.getDB());
storage.stop();
}
for (ComponentModule module : modules.values()) {
module.init(getContext(),getInternalRouter());
module.stop();
}
memoryManager.join(2000);
super.stop();
}
}