/* ********************************************************************** **
** Copyright notice **
** **
** (c) 2005-2009 RSSOwl Development Team **
** http://www.rssowl.org/ **
** **
** 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.rssowl.org/legal/epl-v10.html **
** **
** A copy is found in the file epl-v10.html and important notices to the **
** license from the team is found in the textfile LICENSE.txt distributed **
** in this package. **
** **
** This copyright notice MUST APPEAR in all copies of the file! **
** **
** Contributors: **
** RSSOwl Development Team - initial API and implementation **
** **
** ********************************************************************** */
package org.rssowl.core.internal;
import org.rssowl.core.IApplicationService;
import org.rssowl.core.Owl.StartLevel;
import org.rssowl.core.connection.IConnectionService;
import org.rssowl.core.connection.ICredentialsProvider;
import org.rssowl.core.connection.IProtocolHandler;
import org.rssowl.core.internal.connection.ConnectionServiceImpl;
import org.rssowl.core.internal.interpreter.InterpreterServiceImpl;
import org.rssowl.core.internal.persist.service.PersistenceServiceImpl;
import org.rssowl.core.internal.persist.service.PreferenceServiceImpl;
import org.rssowl.core.interpreter.IElementHandler;
import org.rssowl.core.interpreter.IFormatInterpreter;
import org.rssowl.core.interpreter.IInterpreterService;
import org.rssowl.core.interpreter.INamespaceHandler;
import org.rssowl.core.interpreter.IXMLParser;
import org.rssowl.core.persist.IModelFactory;
import org.rssowl.core.persist.dao.DAOService;
import org.rssowl.core.persist.pref.IPreferenceScope;
import org.rssowl.core.persist.pref.IPreferencesInitializer;
import org.rssowl.core.persist.service.IModelSearch;
import org.rssowl.core.persist.service.IPersistenceService;
import org.rssowl.core.persist.service.IPreferenceService;
import org.rssowl.core.persist.service.PersistenceException;
import org.rssowl.core.util.CoreUtils;
import org.rssowl.core.util.ExtensionUtils;
import org.rssowl.core.util.LongOperationMonitor;
import org.rssowl.core.util.Pair;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.util.List;
/**
* The <code>InternalOwl</code> is being used from the public <code>Owl</code>
* facade.
*
* @author bpasero
*/
public final class InternalOwl {
/* The Singleton Instance */
private static final InternalOwl INSTANCE = new InternalOwl();
/* Extension Point: Factory for Model Types */
private static final String MODEL_TYPESFACTORY_EXTENSION_POINT = "org.rssowl.core.ModelFactory"; //$NON-NLS-1$
/* Extension Point: Persistence Service */
private static final String PERSISTENCE_SERVICE_EXTENSION_POINT = "org.rssowl.core.PersistenceService"; //$NON-NLS-1$
/* ID for Application Service Contribution */
private static final String MODEL_APPLICATION_SERVICE_EXTENSION_POINT = "org.rssowl.core.ApplicationService"; //$NON-NLS-1$
private volatile IPreferenceService fPreferencesService;
private volatile IConnectionService fConnectionService;
private volatile IInterpreterService fInterpreterService;
private volatile IPersistenceService fPersistenceService;
private volatile IApplicationService fApplicationService;
private volatile IModelFactory fModelFactory;
private volatile boolean fShuttingDown;
private volatile boolean fStarted;
private volatile StartLevel fStartLevel = StartLevel.NOT_STARTED;
/** Flag indicating Performance-Tests are running */
public volatile static boolean PERF_TESTING = false;
/** Flag indicating JUnit-Tests are running */
public volatile static boolean TESTING = false;
/** Flag indicating we are running from Eclipse */
public static final boolean IS_ECLIPSE = false;
private InternalOwl() {}
/**
* <em>Never</em> change the ordering of this method's calls!
*
* @param monitor A progress monitor to report progress on long running
* operations (e.g. migration).
* @param emergency if <code>true</code> indicates this startup method is
* called from an emergency situation like restoring a backup.
* @param forRestore if <code>true</code> will open the restore DB as profile
* and <code>false</code> to open the default profile location.
*/
public void startup(LongOperationMonitor monitor, boolean emergency, boolean forRestore) {
/* Increment Start Level */
if (fStartLevel == StartLevel.NOT_STARTED)
fStartLevel = StartLevel.STARTING;
/* Make sure that any error gets logged to the global log */
System.setErr(new PrintStream(new ByteArrayOutputStream()) {
@Override
public void write(byte[] buf, int off, int len) {
if (buf != null && len >= 0 && off >= 0 && off <= buf.length - len)
CoreUtils.appendLogMessage(new String(buf, off, len));
}
});
/* Create Model Factory */
if (fModelFactory == null)
fModelFactory = loadTypesFactory();
/* Create Persistence Service */
if (fPersistenceService == null)
fPersistenceService = loadPersistenceService();
/* Create Application Service */
if (fApplicationService == null)
fApplicationService = loadApplicationService();
/* Persistence Layer has its own startup routine */
fPersistenceService.startup(monitor, emergency, forRestore);
/* Create Connection Service */
if (fConnectionService == null)
fConnectionService = new ConnectionServiceImpl();
/* Create Interpreter Service */
if (fInterpreterService == null)
fInterpreterService = new InterpreterServiceImpl();
/* Create Preferences Service */
if (fPreferencesService == null)
fPreferencesService = new PreferenceServiceImpl();
/* Flag as started */
fStarted = true;
fStartLevel = StartLevel.STARTED;
}
/**
* @param level the new {@link StartLevel}
*/
public void setStartLevel(StartLevel level) {
if (level.ordinal() > fStartLevel.ordinal())
fStartLevel = level;
}
/**
* @return the {@link StartLevel} from the
* {@link #startup(LongOperationMonitor, boolean, boolean)} sequence.
*/
public StartLevel getStartLevel() {
return fStartLevel;
}
/**
* @return Returns the singleton instance of <code>InternalOwl</code>.
*/
public static InternalOwl getDefault() {
return INSTANCE;
}
/**
* @return Returns <code>TRUE</code> if this facade has been started and
* finished initialization.
*/
public boolean isStarted() {
return fStarted;
}
/**
* Shutdown the Services managed by this Facade
*
* @param emergency If set to <code>TRUE</code>, this method is called from a
* shutdown hook that got triggered from a non-normal shutdown (e.g. System
* Shutdown).
*/
public void shutdown(boolean emergency) {
fShuttingDown = true;
/* Shutdown Connection Manager (safely) */
if (!emergency && fConnectionService != null) {
try {
fConnectionService.shutdown();
} catch (Exception e) {
Activator.safeLogError(e.getMessage(), e);
}
}
/* Shutdown Persistence Service */
if (fPersistenceService != null)
fPersistenceService.shutdown(emergency);
fStartLevel = StartLevel.NOT_STARTED;
}
/**
* @return Returns <code>TRUE</code> if {@link InternalOwl#shutdown(boolean)}
* has been called.
*/
public boolean isShuttingDown() {
return fShuttingDown;
}
/**
* <p>
* Get the Implementation of <code>IApplicationService</code> that contains
* special Methods which are used through the Application and access the
* persistence layer. The implementation is looked up using the
* "org.rssowl.core.model.ApplicationService" Extension Point.
* </p>
* Subclasses may override to provide their own implementation.
*
* @return Returns the Implementation of <code>IApplicationService</code> that
* contains special Methods which are used through the Application and access
* the persistence layer.
*/
public IApplicationService getApplicationService() {
return fApplicationService;
}
private IApplicationService loadApplicationService() {
return (IApplicationService) ExtensionUtils.loadSingletonExecutableExtension(MODEL_APPLICATION_SERVICE_EXTENSION_POINT);
}
/**
* <p>
* Provides access to the scoped preferences service in RSSOwl. There is three
* levels of preferences: Default, Global and Entity. Any preference that is
* not set at the one scope will be looked up in the parent scope until the
* Default scope is reached. This allows to easily override the preferences
* for all entities without having to define the preferences per entity.
* </p>
* <p>
* You can define default preferences by using the PreferencesInitializer
* extension point provided by this plugin.
* </p>
*
* @return Returns the IPreferenceService that provides access to the scoped
* preferences system in RSSOwl.
* @see IPreferenceScope
* @see IPreferencesInitializer
*/
public IPreferenceService getPreferenceService() {
return fPreferencesService;
}
/**
* Provides access to ther persistence layer of RSSOwl. This layer is
* contributable via the PersistenceService extension point provided by this
* plugin. The work that is done by the layer includes:
* <ul>
* <li>Controlling the lifecycle of the persistence layer</li>
* <li>Providing the DAOService that contains DAOs for each persistable entity
* </li>
* <li>Providing the model search to perform full-text searching</li>
* </ul>
*
* @return Returns the service responsible for all persistence related tasks.
* @see DAOService
* @see IModelSearch
*/
public IPersistenceService getPersistenceService() {
return fPersistenceService;
}
/* Load the contributed persistence service */
private IPersistenceService loadPersistenceService() {
return (IPersistenceService) ExtensionUtils.loadSingletonExecutableExtension(PERSISTENCE_SERVICE_EXTENSION_POINT);
}
/**
* Provides access to the connection service of RSSOwl. This service provides
* API to load data from the internet (e.g. loading the contents of a feed).
* It is also the central place to ask for credentials if a resource requires
* authentication. Several extension points allow to customize the behavor of
* this service, including the ability to register
* <code>IProtocolHandler</code> to define the lookup process on per protocol
* basis or contributing <code>ICredentialsProvider</code> to define how
* credentials should be stored and retrieved.
*
* @return Returns the service responsible for all connection related tasks.
* @see IProtocolHandler
* @see ICredentialsProvider
*/
public IConnectionService getConnectionService() {
return fConnectionService;
}
/**
* Provides access to the interpreter service of RSSOwl. This service provides
* API to convert a stream of data into a model representation. In the common
* case of a XML stream this involves using a XML-Parser and creating the
* model out of the content. Various extension points allow to customize the
* behavor of the interpreter:
* <ul>
* <li>Contribute a new format interpreter using the FormatInterpreter
* extension point. This allows to display any XML in RSSOwl as Feed.</li>
* <li>Contribute a new namespace handler using the NamespaceHandler extension
* point. This allows to properly handle any new namespace in RSSOwl.</li>
* <li>Contribute a new element handler using the ElementHandler extension
* point. This makes RSSOwl understand new elements or even attributes.</li>
* <li>Contribute a new xml parser using the XMLParser extension point if you
* are not happy with the default one.</li>
* </ul>
*
* @return Returns the service responsible for interpreting a resource.
* @see IFormatInterpreter
* @see IElementHandler
* @see INamespaceHandler
* @see IXMLParser
*/
public IInterpreterService getInterpreter() {
return fInterpreterService;
}
/**
* Provides access to the model factory of RSSOwl. This factory is used
* everywhere when new entities are created. The factory can be replaced using
* the ModelFactory extension point.
*
* @return Returns the model factory that is used to create model types.
*/
public IModelFactory getModelFactory() {
return fModelFactory;
}
/**
* Returns the profile {@link File} that contains all data and the
* {@link Long} timestamp when it was last successfully used.
*
* @return the profile {@link File} and the {@link Long} timestamp when it was
* last successfully used.
*/
public Pair<File /* Profile File */, Long /* Timestamp of last successful use */> getProfile() {
return ((PersistenceServiceImpl) fPersistenceService).getProfile();
}
/**
* Provides a list of available backups for the user to restore from in case
* of an unrecoverable error.
*
* @return a list of available backups for the user to restore from in case of
* an unrecoverable error.
*/
public List<File> getProfileBackups() {
return ((PersistenceServiceImpl) fPersistenceService).getProfileBackups();
}
/**
* Will rename the provided backup file to the operational RSSOwl profile
* database and trigger search reindexing after next start.
*
* @param backup the backup {@link File} to restore from.
* @throws PersistenceException in case a problem occurs while trying to
* execute this operation.
*/
public void restoreProfile(File backup) throws PersistenceException {
((PersistenceServiceImpl) fPersistenceService).restoreProfile(backup);
fPersistenceService.getModelSearch().reIndexOnNextStartup();
}
/**
* Recreate the Profile of the persistence layer. In case of a Database, this
* would drop relations and create them again.
*
* @param needsEmergencyStartup if <code>true</code> causes this method to
* also trigger an emergency startup so that other operations can be normally
* done afterwards like importing from a OPML backup.
* @throws PersistenceException In case of an error while starting up the
* persistence layer.
*/
public void recreateProfile(boolean needsEmergencyStartup) throws PersistenceException {
((PersistenceServiceImpl) fPersistenceService).recreateProfile(needsEmergencyStartup);
}
/* Load Model Types Factory contribution */
private IModelFactory loadTypesFactory() {
return (IModelFactory) ExtensionUtils.loadSingletonExecutableExtension(MODEL_TYPESFACTORY_EXTENSION_POINT);
}
}