/*
*
* * Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
* *
* * 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.
* *
* * For more information: http://www.orientechnologies.com
*
*/
package com.orientechnologies.orient.core;
import com.orientechnologies.common.directmemory.OByteBufferPool;
import com.orientechnologies.common.io.OFileUtils;
import com.orientechnologies.common.io.OIOUtils;
import com.orientechnologies.common.listener.OListenerManger;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.parser.OSystemVariableResolver;
import com.orientechnologies.common.profiler.OProfiler;
import com.orientechnologies.common.profiler.OProfilerStub;
import com.orientechnologies.common.util.OClassLoaderHelper;
import com.orientechnologies.orient.core.cache.OLocalRecordCacheFactory;
import com.orientechnologies.orient.core.cache.OLocalRecordCacheFactoryImpl;
import com.orientechnologies.orient.core.command.script.OScriptManager;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.conflict.ORecordConflictStrategyFactory;
import com.orientechnologies.orient.core.db.ODatabaseLifecycleListener;
import com.orientechnologies.orient.core.db.ODatabaseThreadLocalFactory;
import com.orientechnologies.orient.core.engine.OEngine;
import com.orientechnologies.orient.core.exception.OConfigurationException;
import com.orientechnologies.orient.core.record.ORecordFactoryManager;
import com.orientechnologies.orient.core.security.OSecuritySystem;
import com.orientechnologies.orient.core.shutdown.OShutdownHandler;
import com.orientechnologies.orient.core.storage.OIdentifiableStorage;
import com.orientechnologies.orient.core.storage.OStorage;
import java.io.File;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Orient extends OListenerManger<OOrientListener> {
public static final String ORIENTDB_HOME = "ORIENTDB_HOME";
public static final String URL_SYNTAX = "<engine>:<db-type>:<db-name>[?<db-param>=<db-value>[&]]*";
private static final Orient instance = new Orient();
private static volatile boolean registerDatabaseByPath = false;
private final ConcurrentMap<String, OEngine> engines = new ConcurrentHashMap<String, OEngine>();
private final ConcurrentMap<String, OStorage> storages = new ConcurrentHashMap<String, OStorage>();
private final ConcurrentHashMap<Integer, Boolean> storageIds = new ConcurrentHashMap<Integer, Boolean>();
private final Map<ODatabaseLifecycleListener, ODatabaseLifecycleListener.PRIORITY> dbLifecycleListeners = new LinkedHashMap<ODatabaseLifecycleListener, ODatabaseLifecycleListener.PRIORITY>();
private final OScriptManager scriptManager = new OScriptManager();
private final ThreadGroup threadGroup;
private final ReadWriteLock engineLock = new ReentrantReadWriteLock();
private final ORecordConflictStrategyFactory recordConflictStrategy = new ORecordConflictStrategyFactory();
private final ReferenceQueue<OOrientStartupListener> removedStartupListenersQueue = new ReferenceQueue<OOrientStartupListener>();
private final ReferenceQueue<OOrientShutdownListener> removedShutdownListenersQueue = new ReferenceQueue<OOrientShutdownListener>();
private final Set<OOrientStartupListener> startupListeners = Collections
.newSetFromMap(new ConcurrentHashMap<OOrientStartupListener, Boolean>());
private final Set<WeakHashSetValueHolder<OOrientStartupListener>> weakStartupListeners = Collections
.newSetFromMap(new ConcurrentHashMap<WeakHashSetValueHolder<OOrientStartupListener>, Boolean>());
private final Set<WeakHashSetValueHolder<OOrientShutdownListener>> weakShutdownListeners = Collections
.newSetFromMap(new ConcurrentHashMap<WeakHashSetValueHolder<OOrientShutdownListener>, Boolean>());
private final PriorityQueue<OShutdownHandler> shutdownHandlers = new PriorityQueue<OShutdownHandler>(
11, new Comparator<OShutdownHandler>() {
@Override
public int compare(OShutdownHandler handlerOne, OShutdownHandler handlerTwo) {
if (handlerOne.getPriority() > handlerTwo.getPriority())
return 1;
if (handlerOne.getPriority() < handlerTwo.getPriority())
return -1;
return 0;
}
});
private final OLocalRecordCacheFactory localRecordCache = new OLocalRecordCacheFactoryImpl();
static {
instance.startup();
}
private final String os;
private volatile Timer timer;
private volatile ORecordFactoryManager recordFactoryManager = new ORecordFactoryManager();
private OrientShutdownHook shutdownHook;
private volatile OProfiler profiler;
private ODatabaseThreadLocalFactory databaseThreadFactory;
private volatile boolean active = false;
private ThreadPoolExecutor workers;
private OSignalHandler signalHandler;
private volatile OSecuritySystem security;
private boolean runningDistributed = false;
private static class WeakHashSetValueHolder<T> extends WeakReference<T> {
private final int hashCode;
private WeakHashSetValueHolder(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.hashCode = referent.hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
WeakHashSetValueHolder that = (WeakHashSetValueHolder) o;
if (hashCode != that.hashCode)
return false;
final T thisObject = get();
final Object thatObject = that.get();
if (thisObject == null && thatObject == null)
return super.equals(that);
else if (thisObject != null && thatObject != null)
return thisObject.equals(thatObject);
return false;
}
}
protected Orient() {
super(true);
this.os = System.getProperty("os.name").toLowerCase();
threadGroup = new ThreadGroup("OrientDB");
threadGroup.setDaemon(false);
}
public static Orient instance() {
return instance;
}
public static String getHomePath() {
String v = System.getProperty("orient.home");
if (v == null)
v = OSystemVariableResolver.resolveVariable(ORIENTDB_HOME);
return OFileUtils.getPath(v);
}
public static String getTempPath() {
return OFileUtils.getPath(System.getProperty("java.io.tmpdir") + "/orientdb/");
}
/**
* Tells if to register database by path. Default is false. Setting to true allows to have multiple databases in different path
* with the same name.
*
* @see #setRegisterDatabaseByPath(boolean)
*/
public static boolean isRegisterDatabaseByPath() {
return registerDatabaseByPath;
}
/**
* Register database by path. Default is false. Setting to true allows to have multiple databases in different path with the same
* name.
*/
public static void setRegisterDatabaseByPath(final boolean iValue) {
registerDatabaseByPath = iValue;
}
public ORecordConflictStrategyFactory getRecordConflictStrategy() {
return recordConflictStrategy;
}
public Orient startup() {
engineLock.writeLock().lock();
try {
if (active)
// ALREADY ACTIVE
return this;
if (timer == null)
timer = new Timer(true);
profiler = new OProfilerStub();
shutdownHook = new OrientShutdownHook();
if (signalHandler == null) {
signalHandler = new OSignalHandler();
signalHandler.installDefaultSignals();
}
final int cores = Runtime.getRuntime().availableProcessors();
workers = new ThreadPoolExecutor(cores, cores * 3, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(cores * 500) {
@Override
public boolean offer(Runnable e) {
// turn offer() and add() into a blocking calls (unless interrupted)
try {
put(e);
return true;
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
return false;
}
});
registerEngines();
if (OGlobalConfiguration.ENVIRONMENT_DUMP_CFG_AT_STARTUP.getValueAsBoolean())
OGlobalConfiguration.dumpConfiguration(System.out);
active = true;
for (OOrientStartupListener l : startupListeners)
try {
if (l != null)
l.onStartup();
} catch (Exception e) {
OLogManager.instance().error(this, "Error on startup", e);
}
purgeWeakStartupListeners();
for (final WeakHashSetValueHolder<OOrientStartupListener> wl : weakStartupListeners)
try {
if (wl != null) {
final OOrientStartupListener l = wl.get();
if (l != null)
l.onStartup();
}
} catch (Exception e) {
OLogManager.instance().error(this, "Error on startup", e);
}
initShutdownQueue();
} finally {
engineLock.writeLock().unlock();
}
return this;
}
/**
* Add handler which will be executed during {@link #shutdown()} call.
*
* @param shutdownHandler
* Shutdown handler instance.
*/
public void addShutdownHandler(OShutdownHandler shutdownHandler) {
engineLock.writeLock().lock();
try {
shutdownHandlers.add(shutdownHandler);
} finally {
engineLock.writeLock().unlock();
}
}
/**
* Adds shutdown handlers in order which will be used during execution of shutdown.
*/
private void initShutdownQueue() {
addShutdownHandler(new OShutdownWorkersHandler());
addShutdownHandler(new OShutdownEnginesHandler());
addShutdownHandler(new OShutdownPendingThreadsHandler());
addShutdownHandler(new OShutdownProfilerHandler());
addShutdownHandler(new OShutdownCallListenersHandler());
}
/**
* Shutdown whole OrientDB ecosystem. Usually is called during JVM shutdown by JVM shutdown handler. During shutdown all handlers
* which were registered by the call of {@link #addShutdownHandler(OShutdownHandler)} are called together with pre-registered
* system shoutdown handlers according to their priority.
*
* @see OShutdownWorkersHandler
* @see
*/
private void registerEngines() {
ClassLoader classLoader = Orient.class.getClassLoader();
Iterator<OEngine> engines = OClassLoaderHelper.lookupProviderWithOrientClassLoader(OEngine.class, classLoader);
OEngine engine = null;
while (engines.hasNext()) {
try {
engine = engines.next();
registerEngine(engine);
} catch (IllegalArgumentException e) {
if (engine != null)
OLogManager.instance().debug(this, "Failed to replace engine " + engine.getName());
}
}
}
public Orient shutdown() {
engineLock.writeLock().lock();
try {
if (!active)
return this;
active = false;
OLogManager.instance().info(this, "Orient Engine is shutting down...");
for (OShutdownHandler handler : shutdownHandlers) {
try {
OLogManager.instance().debug(this, "Shutdown handler %s is going to be called", handler);
handler.shutdown();
OLogManager.instance().debug(this, "Shutdown handler %s completed", handler);
} catch (Exception e) {
OLogManager.instance().error(this, "Exception during calling of shutdown handler %s", handler);
}
}
shutdownHandlers.clear();
OLogManager.instance().info(this, "OrientDB Engine shutdown complete");
OLogManager.instance().flush();
} finally {
try {
removeShutdownHook();
} finally {
try {
removeSignalHandler();
} finally {
engineLock.writeLock().unlock();
}
}
}
return this;
}
public void scheduleTask(final TimerTask task, final long delay, final long period) {
engineLock.readLock().lock();
try {
if (active) {
if (period > 0)
timer.schedule(task, delay, period);
else
timer.schedule(task, delay);
} else
OLogManager.instance().warn(this, "OrientDB engine is down. Task will not be scheduled.");
} finally {
engineLock.readLock().unlock();
}
}
public void scheduleTask(final TimerTask task, final Date firstTime, final long period) {
engineLock.readLock().lock();
try {
if (active)
if (period > 0)
timer.schedule(task, firstTime, period);
else
timer.schedule(task, firstTime);
else
OLogManager.instance().warn(this, "OrientDB engine is down. Task will not be scheduled.");
} finally {
engineLock.readLock().unlock();
}
}
public void closeAllStorages() {
shutdownAllStorages();
}
private void shutdownAllStorages() {
engineLock.writeLock().lock();
try {
// CLOSE ALL THE STORAGES
final List<OStorage> storagesCopy = new ArrayList<OStorage>(storages.values());
for (OStorage stg : storagesCopy) {
try {
OLogManager.instance().info(this, "- shutdown storage: " + stg.getName() + "...");
stg.shutdown();
} catch (Throwable e) {
OLogManager.instance().warn(this, "-- error on shutdown storage", e);
}
}
storages.clear();
} finally {
engineLock.writeLock().unlock();
}
}
public boolean isActive() {
return active;
}
/**
* @deprecated This method is not thread safe. Use {@link #submit(java.util.concurrent.Callable)} instead.
*/
@Deprecated
public ThreadPoolExecutor getWorkers() {
return workers;
}
public Future<?> submit(final Runnable runnable) {
engineLock.readLock().lock();
try {
if (active)
return workers.submit(runnable);
else {
OLogManager.instance().warn(this, "OrientDB engine is down. Task will not be submitted.");
throw new IllegalStateException("OrientDB engine is down. Task will not be submitted.");
}
} finally {
engineLock.readLock().unlock();
}
}
public <V> Future<V> submit(final Callable<V> callable) {
engineLock.readLock().lock();
try {
if (active)
return workers.submit(callable);
else {
OLogManager.instance().warn(this, "OrientDB engine is down. Task will not be submitted.");
throw new IllegalStateException("OrientDB engine is down. Task will not be submitted.");
}
} finally {
engineLock.readLock().unlock();
}
}
public OStorage loadStorage(String iURL) {
if (iURL == null || iURL.length() == 0)
throw new IllegalArgumentException("URL missed");
if (iURL.endsWith("/"))
iURL = iURL.substring(0, iURL.length() - 1);
// SEARCH FOR ENGINE
int pos = iURL.indexOf(':');
if (pos <= 0)
throw new OConfigurationException(
"Error in database URL: the engine was not specified. Syntax is: " + URL_SYNTAX + ". URL was: " + iURL);
final String engineName = iURL.substring(0, pos);
engineLock.readLock().lock();
try {
final OEngine engine = engines.get(engineName.toLowerCase());
if (engine == null)
throw new OConfigurationException("Error on opening database: the engine '" + engineName + "' was not found. URL was: "
+ iURL + ". Registered engines are: " + engines.keySet());
if (!engine.isRunning()) {
final List<String> knownEngines = new ArrayList<String>(engines.keySet());
if (!startEngine(engine))
throw new OConfigurationException(
"Error on opening database: the engine '" + engineName + "' was unable to start. URL was: " + iURL
+ ". Registered engines was: " + knownEngines);
}
// SEARCH FOR DB-NAME
iURL = iURL.substring(pos + 1);
if (isWindowsOS()) {
// WINDOWS ONLY: REMOVE DOUBLE SLASHES NOT AS PREFIX (WINDOWS PATH COULD NEED STARTING FOR "\\". EXAMPLE: "\\mydrive\db").
// AT
// THIS LEVEL BACKSLASHES ARRIVES AS SLASHES
iURL = iURL.charAt(0) + iURL.substring(1).replace("//", "/");
} else
// REMOVE ANY //
iURL = iURL.replace("//", "/");
pos = iURL.indexOf('?');
Map<String, String> parameters = null;
String dbPath;
if (pos > 0) {
dbPath = iURL.substring(0, pos);
iURL = iURL.substring(pos + 1);
// PARSE PARAMETERS
parameters = new HashMap<String, String>();
String[] pairs = iURL.split("&");
String[] kv;
for (String pair : pairs) {
kv = pair.split("=");
if (kv.length < 2)
throw new OConfigurationException(
"Error on opening database: parameter has no value. Syntax is: " + URL_SYNTAX + ". URL was: " + iURL);
parameters.put(kv[0], kv[1]);
}
} else
dbPath = iURL;
if (registerDatabaseByPath) {
try {
dbPath = new File(dbPath).getCanonicalPath();
} catch (IOException e) {
// IGNORE IT
}
}
final String dbName = registerDatabaseByPath ? dbPath : engine.getNameFromPath(dbPath);
OStorage storage;
// SEARCH IF ALREADY USED
storage = storages.get(dbName);
if (storage == null) {
// NOT FOUND: CREATE IT
do {
storage = engine.createStorage(dbPath, parameters);
} while ((storage instanceof OIdentifiableStorage)
&& storageIds.putIfAbsent(((OIdentifiableStorage) storage).getId(), Boolean.TRUE) != null);
final OStorage oldStorage = storages.putIfAbsent(dbName, storage);
if (oldStorage != null)
storage = oldStorage;
for (OOrientListener l : browseListeners())
l.onStorageRegistered(storage);
}
return storage;
} finally {
engineLock.readLock().unlock();
}
}
public boolean isWindowsOS() {
return os.contains("win");
}
public OStorage getStorage(final String dbName) {
engineLock.readLock().lock();
try {
return storages.get(dbName);
} finally {
engineLock.readLock().unlock();
}
}
protected void registerEngine(final OEngine iEngine) throws IllegalArgumentException {
OEngine oEngine = engines.get(iEngine.getName());
if (oEngine != null) {
if (!oEngine.getClass().isAssignableFrom(iEngine.getClass())) {
throw new IllegalArgumentException("Cannot replace storage " + iEngine.getName());
}
}
engines.put(iEngine.getName(), iEngine);
}
/**
* Returns the engine by its name.
*
* @param engineName
* Engine name to retrieve
* @return OEngine instance of found, otherwise null
*/
public OEngine getEngine(final String engineName) {
engineLock.readLock().lock();
try {
return engines.get(engineName);
} finally {
engineLock.readLock().unlock();
}
}
/**
* Obtains an {@link OEngine engine} instance with the given {@code engineName}, if it is {@link OEngine#isRunning() running}.
*
* @param engineName the name of the engine to obtain.
* @return the obtained engine instance or {@code null} if no such engine known or the engine is not running.
*/
public OEngine getEngineIfRunning(final String engineName) {
engineLock.readLock().lock();
try {
final OEngine engine = engines.get(engineName);
return engine == null || !engine.isRunning() ? null : engine;
} finally {
engineLock.readLock().unlock();
}
}
/**
* Obtains a {@link OEngine#isRunning() running} {@link OEngine engine} instance with the given {@code engineName}.
* If engine is not running, starts it.
*
* @param engineName the name of the engine to obtain.
* @return the obtained running engine instance, never {@code null}.
* @throws IllegalStateException if an engine with the given is not found or failed to start.
*/
public OEngine getRunningEngine(final String engineName) {
engineLock.readLock().lock();
try {
OEngine engine = engines.get(engineName);
if (engine == null)
throw new IllegalStateException("Engine '" + engineName + "' is not found.");
if (!engine.isRunning() && !startEngine(engine))
throw new IllegalStateException("Engine '" + engineName + "' is failed to start.");
return engine;
} finally {
engineLock.readLock().unlock();
}
}
public Set<String> getEngines() {
engineLock.readLock().lock();
try {
return Collections.unmodifiableSet(engines.keySet());
} finally {
engineLock.readLock().unlock();
}
}
public void unregisterStorageByName(final String name) {
final String dbName = registerDatabaseByPath ? name : OIOUtils.getRelativePathIfAny(name, null);
final OStorage stg = storages.get(dbName);
unregisterStorage(stg);
}
public void unregisterStorage(final OStorage storage) {
if (!active)
// SHUTDOWNING OR NOT ACTIVE: RETURN
return;
if (storage == null)
return;
engineLock.writeLock().lock();
try {
// UNREGISTER ALL THE LISTENER ONE BY ONE AVOIDING SELF-RECURSION BY REMOVING FROM THE LIST
final Iterable<OOrientListener> listenerCopy = getListenersCopy();
for (final OOrientListener l : listenerCopy) {
unregisterListener(l);
l.onStorageUnregistered(storage);
}
final List<String> storagesToRemove = new ArrayList<String>();
for (Entry<String, OStorage> s : storages.entrySet()) {
if (s.getValue().equals(storage))
storagesToRemove.add(s.getKey());
}
for (String dbName : storagesToRemove)
storages.remove(dbName);
// UNREGISTER STORAGE FROM ENGINES IN CASE IS CACHED
for (OEngine engine : engines.values()) {
engine.removeStorage(storage);
}
} finally {
engineLock.writeLock().unlock();
}
}
public Collection<OStorage> getStorages() {
engineLock.readLock().lock();
try {
return new ArrayList<OStorage>(storages.values());
} finally {
engineLock.readLock().unlock();
}
}
/**
* @deprecated This method is not thread safe please use {@link #scheduleTask(java.util.TimerTask, long, long)} instead.
*/
@Deprecated
public Timer getTimer() {
return timer;
}
public void removeShutdownHook() {
if (shutdownHook != null) {
shutdownHook.cancel();
shutdownHook = null;
}
}
public OSignalHandler getSignalHandler() {
return signalHandler;
}
public void removeSignalHandler() {
if (signalHandler != null) {
signalHandler.cancel();
signalHandler = null;
}
}
public boolean isSelfManagedShutdown() {
return shutdownHook != null;
}
public Iterator<ODatabaseLifecycleListener> getDbLifecycleListeners() {
return new HashSet<ODatabaseLifecycleListener>(dbLifecycleListeners.keySet()).iterator();
}
public void addDbLifecycleListener(final ODatabaseLifecycleListener iListener) {
final Map<ODatabaseLifecycleListener, ODatabaseLifecycleListener.PRIORITY> tmp = new LinkedHashMap<ODatabaseLifecycleListener, ODatabaseLifecycleListener.PRIORITY>(
dbLifecycleListeners);
if (iListener.getPriority() == null)
throw new IllegalArgumentException("Priority of DatabaseLifecycleListener '" + iListener + "' cannot be null");
tmp.put(iListener, iListener.getPriority());
dbLifecycleListeners.clear();
for (ODatabaseLifecycleListener.PRIORITY p : ODatabaseLifecycleListener.PRIORITY.values()) {
for (Map.Entry<ODatabaseLifecycleListener, ODatabaseLifecycleListener.PRIORITY> e : tmp.entrySet()) {
if (e.getValue() == p)
dbLifecycleListeners.put(e.getKey(), e.getValue());
}
}
}
public void removeDbLifecycleListener(final ODatabaseLifecycleListener iListener) {
dbLifecycleListeners.remove(iListener);
}
public ThreadGroup getThreadGroup() {
return threadGroup;
}
public ODatabaseThreadLocalFactory getDatabaseThreadFactory() {
return databaseThreadFactory;
}
public ORecordFactoryManager getRecordFactoryManager() {
return recordFactoryManager;
}
public void setRecordFactoryManager(final ORecordFactoryManager iRecordFactoryManager) {
recordFactoryManager = iRecordFactoryManager;
}
public OProfiler getProfiler() {
return profiler;
}
public void setProfiler(final OProfiler iProfiler) {
profiler = iProfiler;
}
public OSecuritySystem getSecurity() {
return this.security;
}
public void setSecurity(final OSecuritySystem security) {
this.security = security;
}
public void registerThreadDatabaseFactory(final ODatabaseThreadLocalFactory iDatabaseFactory) {
databaseThreadFactory = iDatabaseFactory;
}
public OScriptManager getScriptManager() {
return scriptManager;
}
@Override
public void registerListener(OOrientListener listener) {
if (listener instanceof OOrientStartupListener)
registerOrientStartupListener((OOrientStartupListener) listener);
super.registerListener(listener);
}
@Override
public void unregisterListener(OOrientListener listener) {
if (listener instanceof OOrientStartupListener)
unregisterOrientStartupListener((OOrientStartupListener) listener);
super.unregisterListener(listener);
}
public void registerOrientStartupListener(OOrientStartupListener listener) {
startupListeners.add(listener);
}
public void registerWeakOrientStartupListener(OOrientStartupListener listener) {
purgeWeakStartupListeners();
weakStartupListeners.add(new WeakHashSetValueHolder<OOrientStartupListener>(listener, removedStartupListenersQueue));
}
public void unregisterOrientStartupListener(OOrientStartupListener listener) {
startupListeners.remove(listener);
}
public void unregisterWeakOrientStartupListener(OOrientStartupListener listener) {
purgeWeakStartupListeners();
weakStartupListeners.remove(new WeakHashSetValueHolder<OOrientStartupListener>(listener, null));
}
public void registerWeakOrientShutdownListener(OOrientShutdownListener listener) {
purgeWeakShutdownListeners();
weakShutdownListeners.add(new WeakHashSetValueHolder<OOrientShutdownListener>(listener, removedShutdownListenersQueue));
}
public void unregisterWeakOrientShutdownListener(OOrientShutdownListener listener) {
purgeWeakShutdownListeners();
weakShutdownListeners.remove(new WeakHashSetValueHolder<OOrientShutdownListener>(listener, null));
}
@Override
public void resetListeners() {
super.resetListeners();
weakShutdownListeners.clear();
startupListeners.clear();
weakStartupListeners.clear();
}
public OLocalRecordCacheFactory getLocalRecordCache() {
return localRecordCache;
}
private void purgeWeakStartupListeners() {
synchronized (removedStartupListenersQueue) {
WeakHashSetValueHolder<OOrientStartupListener> ref = (WeakHashSetValueHolder<OOrientStartupListener>) removedStartupListenersQueue
.poll();
while (ref != null) {
weakStartupListeners.remove(ref);
ref = (WeakHashSetValueHolder<OOrientStartupListener>) removedStartupListenersQueue.poll();
}
}
}
private void purgeWeakShutdownListeners() {
synchronized (removedShutdownListenersQueue) {
WeakHashSetValueHolder<OOrientShutdownListener> ref = (WeakHashSetValueHolder<OOrientShutdownListener>) removedShutdownListenersQueue
.poll();
while (ref != null) {
weakShutdownListeners.remove(ref);
ref = (WeakHashSetValueHolder<OOrientShutdownListener>) removedShutdownListenersQueue.poll();
}
}
}
private boolean startEngine(OEngine engine) {
final String name = engine.getName();
try {
engine.startup();
return true;
} catch (Exception e) {
OLogManager.instance().error(this, "Error during initialization of engine '%s', engine will be removed", e, name);
try {
engine.shutdown();
} catch (Exception se) {
OLogManager.instance().error(this, "Error during engine shutdown", se);
}
engines.remove(name);
}
return false;
}
/**
* Shutdown thread group which is used in methods {@link #submit(Callable)} and {@link #submit(Runnable)}.
*/
public class OShutdownWorkersHandler implements OShutdownHandler {
@Override
public int getPriority() {
return SHUTDOWN_WORKERS_PRIORITY;
}
@Override
public void shutdown() throws Exception {
workers.shutdown();
try {
workers.awaitTermination(2, TimeUnit.MINUTES);
} catch (InterruptedException e) {
}
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}
/**
* Closes all storages and shutdown all engines.
*/
public class OShutdownEnginesHandler implements OShutdownHandler {
@Override
public int getPriority() {
return SHUTDOWN_ENGINES_PRIORITY;
}
@Override
public void shutdown() throws Exception {
shutdownAllStorages();
// SHUTDOWN ENGINES
for (OEngine engine : engines.values())
if (engine.isRunning())
engine.shutdown();
engines.clear();
OByteBufferPool.instance().verifyState();
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}
/**
* Interrupts all threads in OrientDB thread group and stops timer is used in methods {@link #scheduleTask(TimerTask, Date, long)}
* and {@link #scheduleTask(TimerTask, long, long)}.
*/
private class OShutdownPendingThreadsHandler implements OShutdownHandler {
@Override
public int getPriority() {
return SHUTDOWN_PENDING_THREADS_PRIORITY;
}
@Override
public void shutdown() throws Exception {
if (threadGroup != null)
// STOP ALL THE PENDING THREADS
threadGroup.interrupt();
if (timer != null) {
timer.cancel();
timer = null;
}
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}
/**
* Shutdown OrientDB profiler.
*/
private class OShutdownProfilerHandler implements OShutdownHandler {
@Override
public int getPriority() {
return SHUTDOWN_PROFILER_PRIORITY;
}
@Override
public void shutdown() throws Exception {
// NOTE: DON'T REMOVE PROFILER TO AVOID NPE AROUND THE CODE IF ANY THREADS IS STILL WORKING
profiler.shutdown();
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}
/**
* Calls all shutdown listeners.
*/
private class OShutdownCallListenersHandler implements OShutdownHandler {
@Override
public int getPriority() {
return SHUTDOWN_CALL_LISTENERS;
}
@Override
public void shutdown() throws Exception {
purgeWeakShutdownListeners();
for (final WeakHashSetValueHolder<OOrientShutdownListener> wl : weakShutdownListeners)
try {
if (wl != null) {
final OOrientShutdownListener l = wl.get();
if (l != null) {
l.onShutdown();
}
}
} catch (Exception e) {
OLogManager.instance().error(this, "Error during orient shutdown", e);
}
// CALL THE SHUTDOWN ON ALL THE LISTENERS
for (OOrientListener l : browseListeners()) {
if (l != null)
try {
l.onShutdown();
} catch (Exception e) {
OLogManager.instance().error(this, "Error during orient shutdown", e);
}
}
System.gc();
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}
public boolean isRunningDistributed() {
return runningDistributed;
}
public void setRunningDistributed(final boolean runningDistributed) {
this.runningDistributed = runningDistributed;
}
}