package com.robonobo.core;
import java.io.*;
import java.util.*;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.PropertyConfigurator;
import com.robonobo.common.concurrent.SafetyNet;
import com.robonobo.common.serialization.ConfigBeanSerializer;
import com.robonobo.common.util.*;
import com.robonobo.core.api.*;
import com.robonobo.core.api.config.RobonoboConfig;
import com.robonobo.core.itunes.ITunesService;
import com.robonobo.core.metadata.AbstractMetadataService;
import com.robonobo.core.mina.MinaService;
import com.robonobo.core.service.*;
import com.robonobo.core.storage.StorageService;
import com.robonobo.core.update.Updater;
import com.robonobo.core.wang.WangService;
import com.robonobo.mina.external.Application;
import com.robonobo.mina.external.MinaControl;
import com.robonobo.spi.RuntimeService;
/** Central class that controls a robonobo instance; handles startup, and holds references to services that do the actual
* work
*
* @author macavity */
public class RobonoboInstance implements Robonobo {
protected String version = "dev-unknown";
protected Log log;
ScheduledThreadPoolExecutor executor;
Map<String, Object> configs = new HashMap<String, Object>();
ServiceManager serviceMgr;
Application thisApplication;
private File homeDir;
private RobonoboStatus status = RobonoboStatus.Stopped;
public RobonoboInstance(String[] args) throws Exception {
loadVersion();
setHomeDir();
initLogging();
updateHomeDir();
loadApplicationDetails();
loadConfig();
overrideConfigWithEnv();
registerServices();
startExecutor();
Platform.getPlatform().initRobonobo(this);
}
public void start() throws RobonoboException {
setStatus(RobonoboStatus.Starting);
serviceMgr.startup();
}
protected void startExecutor() {
if (executor == null) {
int poolSz = getConfig().getGeneralThreadPoolSize();
log.info("Starting thread pool with " + poolSz + " threads");
executor = new ScheduledThreadPoolExecutor(poolSz);
}
}
protected void registerServices() {
serviceMgr = new ServiceManager(RobonoboInstance.this);
serviceMgr.registerService(new EventService());
serviceMgr.registerService(new DbService());
serviceMgr.registerService(new FormatService());
serviceMgr.registerService(new SearchService());
serviceMgr.registerService(new StorageService());
serviceMgr.registerService(new MinaService());
serviceMgr.registerService(new GatewayService());
serviceMgr.registerService(new ShareService());
serviceMgr.registerService(new DownloadService());
serviceMgr.registerService(new TrackService());
serviceMgr.registerService(new PlaybackService());
serviceMgr.registerService(new UserService());
serviceMgr.registerService(new PlaylistService());
serviceMgr.registerService(new TaskService());
serviceMgr.registerService(new LibraryService());
serviceMgr.registerService(new CommentService());
serviceMgr.registerService(new StreamService());
serviceMgr.registerService(new HttpService());
// Register our metadata service as defined in config
String metadataServiceClass = getConfig().getMetadataServiceClass();
try {
Class<?> cl = Class.forName(metadataServiceClass);
AbstractMetadataService svc = (AbstractMetadataService) cl.newInstance();
serviceMgr.registerService(svc);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not find metadata service class '" + metadataServiceClass + "'", e);
} catch (InstantiationException e) {
throw new RuntimeException("Error instantiating metadata service class '" + metadataServiceClass + "'", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Error instantiating metadata service class '" + metadataServiceClass + "'", e);
} catch (ClassCastException e) {
throw new RuntimeException("Metadata service class '" + metadataServiceClass
+ "' defined in config does not extend com.robonobo.core.metadata.AbstractMetadataServicew", e);
}
if (getConfig().isAgoric())
serviceMgr.registerService(new WangService());
if (Platform.getPlatform().iTunesAvailable())
serviceMgr.registerService(Platform.getPlatform().getITunesService());
// Extra services defined in config
String[] extraServices = getConfig().getExtraServices().split(",");
for (String serviceClass : extraServices) {
if (serviceClass.trim().length() > 0) {
log.info("Instantiating extra service " + serviceClass);
try {
Class<?> cl = Class.forName(serviceClass);
serviceMgr.registerService((RuntimeService) cl.newInstance());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
protected void loadVersion() throws IOException {
InputStream is = getClass().getResourceAsStream("/robonobo-build.properties");
if (is != null) {
Properties props = new Properties();
props.load(is);
version = props.getProperty("version");
is.close();
}
}
protected void loadConfig() throws IOException {
// Load our core configs
Map<String, String> cfgClasses = new HashMap<String, String>();
cfgClasses.put("robo", "com.robonobo.core.api.config.RobonoboConfig");
cfgClasses.put("mina", "com.robonobo.mina.external.MinaConfig");
cfgClasses.put("wang", "com.robonobo.core.wang.RobonoboWangConfig");
cfgClasses.put("gui", "com.robonobo.gui.GuiConfig");
// Load base robonobo config, that contains class names of any other
// configs we have
File cfgDir = new File(homeDir, "config");
if (!cfgDir.exists())
cfgDir.mkdirs();
for (String cfgName : cfgClasses.keySet()) {
String cfgClassName = cfgClasses.get(cfgName);
loadConfig(cfgName, cfgClassName, cfgDir);
}
String extraCfgStr = getConfig().getExtraConfigs();
if (extraCfgStr.length() > 0) {
String[] extraCfgs = extraCfgStr.split(",");
Pattern cfgPat = Pattern.compile("^(.+):(.+)$");
for (String cfgStr : extraCfgs) {
Matcher m = cfgPat.matcher(cfgStr);
if (!m.matches())
throw new IOException("Invalid config string: " + cfgStr);
String cfgName = m.group(1);
String cfgClassName = m.group(2);
loadConfig(cfgName, cfgClassName, cfgDir);
}
}
// First time through, set the default download dir
if (getConfig().getFinishedDownloadsDirectory() == null) {
File dd = Platform.getPlatform().getDefaultDownloadDirectory();
dd.mkdirs();
String ddPath = dd.getAbsolutePath();
getConfig().setFinishedDownloadsDirectory(ddPath);
}
saveConfig();
}
private void loadConfig(String cfgName, String cfgClassName, File cfgDir) {
ConfigBeanSerializer cbs = new ConfigBeanSerializer();
try {
Class<?> cfgClass = Class.forName(cfgClassName);
File cfgFile = new File(cfgDir, cfgName + ".cfg");
if (cfgFile.exists()) {
log.warn("Loading config from " + cfgFile.getAbsolutePath());
configs.put(cfgName, cbs.deserializeConfig(cfgClass, cfgFile));
} else {
log.warn("Config file " + cfgFile.getAbsolutePath() + " not found - creating new config");
configs.put(cfgName, cfgClass.newInstance());
}
} catch (Exception e) {
log.error("Caught exception loading cfg " + cfgName + ":" + cfgClassName, e);
}
}
private void setHomeDir() {
if (System.getenv().containsKey("RBNB_HOME"))
homeDir = new File(System.getenv("RBNB_HOME"));
else
homeDir = Platform.getPlatform().getDefaultHomeDirectory();
homeDir.mkdirs();
}
public File getHomeDir() {
return homeDir;
}
/** Overrides config properties with values specified in 'cfg_<cfgName>_<propName>' environment vars */
protected void overrideConfigWithEnv() {
ConfigBeanSerializer cbs = new ConfigBeanSerializer();
try {
for (String cfgName : configs.keySet()) {
cbs.overrideCfgFromEnv(configs.get(cfgName), cfgName);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void shutdown() {
setStatus(RobonoboStatus.Stopping);
serviceMgr.shutdown();
setStatus(RobonoboStatus.Stopped);
}
protected void loadApplicationDetails() {
thisApplication = new Application();
thisApplication.setVersion(version);
thisApplication.setName("robonobo");
thisApplication.setPublisher("The robonobo project");
thisApplication.setHomeUri("http://robonobo.com");
log.info(thisApplication.getName() + "/" + thisApplication.getVersion() + " (" + thisApplication.getPublisher() + ") " + thisApplication.getHomeUri());
}
public Application getApplication() {
return thisApplication;
}
public ShareService getShareService() {
return (ShareService) serviceMgr.getService("core.shares");
}
public TrackService getTrackService() {
return (TrackService) serviceMgr.getService("core.tracks");
}
public TaskService getTaskService() {
return (TaskService) serviceMgr.getService("core.tasks");
}
public LibraryService getLibraryService() {
return (LibraryService) serviceMgr.getService("core.libraries");
}
public ITunesService getITunesService() {
return (ITunesService) serviceMgr.getService("core.itunes");
}
public UserService getUserService() {
return (UserService) serviceMgr.getService("core.users");
}
public PlaylistService getPlaylistService() {
return (PlaylistService) serviceMgr.getService("core.playlists");
}
public CommentService getCommentService() {
return (CommentService) serviceMgr.getService("core.comments");
}
public PlaybackService getPlaybackService() {
return (PlaybackService) serviceMgr.getService("core.playback");
}
public DownloadService getDownloadService() {
return (DownloadService) serviceMgr.getService("core.downloads");
}
public WangService getWangService() {
return (WangService) serviceMgr.getService("core.wang");
}
public RobonoboConfig getConfig() {
return (RobonoboConfig) getConfig("robo");
}
public Object getConfig(String cfgName) {
return configs.get(cfgName);
}
public ScheduledThreadPoolExecutor getExecutor() {
return executor;
}
public DbService getDbService() {
return (DbService) serviceMgr.getService("core.db");
}
// public MetadataService getMetadataService() {
// return (MetadataService) serviceMgr.getService("core.metadata");
// }
public AbstractMetadataService getMetadataService() {
return (AbstractMetadataService) serviceMgr.getService("core.metadata");
}
public HttpService getHttpService() {
return (HttpService) serviceMgr.getService("core.http");
}
public StreamService getStreamService() {
return (StreamService) serviceMgr.getService("core.streams");
}
public MinaControl getMina() {
return ((MinaService) getServiceMgr().getService("core.mina")).getMina();
}
public ServiceManager getServiceMgr() {
return serviceMgr;
}
public StorageService getStorageService() {
return (StorageService) serviceMgr.getService("core.storage");
}
public void addConfig(String name, Object cfg) {
configs.put(name, cfg);
saveConfig();
}
public void saveConfig() {
File configDir = new File(homeDir, "config");
ConfigBeanSerializer cbs = new ConfigBeanSerializer();
for (String cfgName : configs.keySet()) {
File configFile = new File(configDir, cfgName + ".cfg");
try {
log.info("Saving config '" + cfgName + "' to " + configFile.getAbsolutePath());
cbs.serializeConfig(configs.get(cfgName), configFile);
} catch (Exception e) {
log.warn("NOT saving config back to filesystem: " + e.getMessage());
}
}
}
public void setExecutor(ScheduledThreadPoolExecutor executor) {
this.executor = executor;
}
protected void initLogging() {
// This property gets picked up by the log4j config
File logDir = new File(homeDir, "logs");
if (!logDir.exists())
logDir.mkdir();
// Add the old property as well for updating
System.setProperty("robo.log.dir", logDir.getAbsolutePath());
System.setProperty("rbnb.log.dir", logDir.getAbsolutePath());
// If there isn't a log4j properties file in our homedir, copy one from
// the jar
try {
File log4jCfgFile = new File(homeDir, "log4j.properties");
if (!log4jCfgFile.exists()) {
// Note, if we call the props file in the jar "log4j.properties", then log4j will detect and use it,
// ignoring what we have on the filesystem
InputStream is = getClass().getResourceAsStream("/log4j.props.skel");
OutputStream os = new FileOutputStream(log4jCfgFile);
ByteUtil.streamDump(is, os);
}
PropertyConfigurator.configureAndWatch(log4jCfgFile.getAbsolutePath());
log = LogFactory.getLog(getClass());
log.fatal("O HAI! robonobo starting using homedir " + homeDir.getAbsolutePath());
// Log uncaught exceptions in other threads
SafetyNet.addListener(new ExceptionListener() {
public void onException(ExceptionEvent e) {
log.error("Uncaught exception", e.getException());
}
});
} catch (Exception e) {
System.err.println("Error: Unable to initialize log4j logging (caught " + e.getClass().getName() + ": " + e.getMessage() + ")");
}
}
protected void updateHomeDir() throws IOException {
Updater updater = new Updater(homeDir);
updater.runUpdate();
}
public FormatService getFormatService() {
return (FormatService) serviceMgr.getService("core.format");
}
public SearchService getSearchService() {
return (SearchService) serviceMgr.getService("core.search");
}
public String getVersion() {
return version;
}
public EventService getEventService() {
return (EventService) serviceMgr.getService("core.events");
}
public RobonoboStatus getStatus() {
return status;
}
public void setStatus(RobonoboStatus status) {
log.debug("New status: " + status);
this.status = status;
}
}