PlaceStarter.java
package emissary.admin;
import emissary.config.ConfigUtil;
import emissary.config.Configurator;
import emissary.core.EmissaryException;
import emissary.core.EmissaryRuntimeException;
import emissary.core.Factory;
import emissary.core.Namespace;
import emissary.core.NamespaceException;
import emissary.directory.DirectoryEntry;
import emissary.directory.DirectoryPlace;
import emissary.directory.EmissaryNode;
import emissary.directory.IDirectoryPlace;
import emissary.directory.KeyManipulator;
import emissary.place.IServiceProviderPlace;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
/**
* Static methods to start places in the system.
*/
@SuppressWarnings("SystemExitOutsideMain")
public class PlaceStarter {
private static final Logger logger = LoggerFactory.getLogger(PlaceStarter.class);
private static final Configurator classConf;
protected static final String defaultClassName = "emissary.place.sample.DevNullPlace";
static {
Configurator tmpCConfigurator;
try {
tmpCConfigurator = ConfigUtil.getClassNameInventory();
} catch (IOException | EmissaryException iox) {
tmpCConfigurator = null;
logger.error("Missing ClassNameInventory.cfg: all places will become " + defaultClassName
+ " which is probably not what you want. Config is now " + System.getProperty(ConfigUtil.CONFIG_DIR_PROPERTY), iox);
System.exit(1);
}
classConf = tmpCConfigurator;
}
/**
* Create a place using File based config
*
* @param theLocation key for the new place
* @param theClassStr string name of the class to instantiate
* @param directory the string directory name to register in
* @return the place that was found or created, or null if it can't be done
*/
public static IServiceProviderPlace createPlace(final String theLocation, final String theClassStr, final String directory) {
// generate constructor args
final String theConfigFile = theClassStr + ConfigUtil.CONFIG_FILE_ENDING;
final Object[] constructorArgs = {theConfigFile, directory, theLocation};
return createPlace(theLocation, constructorArgs, theClassStr);
}
/**
* Create a place using Stream based config
*
* @param theLocation key for the new place
* @param theConfigStream stream configuration for the place
* @param theClassStr string name of the class to instantiate
* @param directory the string directory name to register in
* @return the place that was found or created, or null if it can't be done
*/
public static IServiceProviderPlace createPlace(final String theLocation, final InputStream theConfigStream, final String theClassStr,
final String directory) {
// generate constructor args
final Object[] constructorArgs = {theConfigStream, directory, theLocation};
return createPlace(theLocation, constructorArgs, theClassStr);
}
/**
* Create a place using Stream based config
*
* @param theLocation key for the new place
* @param theConfigStream stream configuration for the place
* @param theClassStr string name of the class to instantiate
* @param directory the string directory name to register in
* @param node the emissary node
* @return the place that was found or created, or null if it can't be done
*/
public static IServiceProviderPlace createPlace(final String theLocation, final InputStream theConfigStream, final String theClassStr,
final String directory, final EmissaryNode node) {
// generate constructor args
final Object[] constructorArgs = {theConfigStream, directory, theLocation, node};
return createPlace(theLocation, constructorArgs, theClassStr);
}
/**
* Create a place using generic Object[] constructor args for maximum flexibility for finding any existing constructor.
* Will check to see if the place already exists first and return the existing instance from the Namespace if it does.
*
* @param theLocation key for the new place
* @param constructorArgs array of args to pass to the place constructor
* @param theClassStr string name of the class to instantiate
* @return the place that was found or created, or null if it can't be done
* @deprecated use {@link #createPlace(String, List, String)}
*/
@Nullable
@Deprecated
@SuppressWarnings("AvoidObjectArrays")
public static IServiceProviderPlace createPlace(final String theLocation, final Object[] constructorArgs, @Nullable final String theClassStr) {
return createPlace(theLocation, Arrays.asList(constructorArgs), theClassStr);
}
/**
* Create a place using generic List constructor args for maximum flexibility for finding any existing constructor. Will
* check to see if the place already exists first and return the existing instance from the Namespace if it does.
*
* @param theLocation key for the new place
* @param constructorArgs list of args to pass to the place constructor
* @param theClassStr string name of the class to instantiate
* @return the place that was found or created, or null if it can't be done
*/
@Nullable
public static IServiceProviderPlace createPlace(final String theLocation, final List<Object> constructorArgs,
@Nullable final String theClassStr) {
logger.debug("Ready to createPlace {} as {}", theLocation, theClassStr);
final long t1 = System.currentTimeMillis();
// check the input arguments
// TODO we should add a check to validate theLocation
final IServiceProviderPlace place = alreadyExists(theLocation);
if (place != null) {
// a place already exists at this location, can't create another!
logger.warn("{} already exists!", theLocation);
return place;
}
// error, must have the class string known...
if (theClassStr == null) {
logger.warn("classStr check failed for {}", theLocation);
return null;
}
final String bindKey = KeyManipulator.removeExpense(theLocation);
final IServiceProviderPlace thePlace;
try {
thePlace = (IServiceProviderPlace) Factory.create(theClassStr, constructorArgs, bindKey);
} catch (Throwable te) {
// error creating place
logger.error("cannot create {}", theLocation, te);
shutdownFailedPlace(bindKey, null);
return null; // couldn't start the place.
}
final long t2 = System.currentTimeMillis();
if (logger.isDebugEnabled()) {
logger.debug("Started {} in {}s", theLocation, (t2 - t1) / 1000.0);
}
return thePlace;
}
public static void shutdownFailedPlace(final String loc, @Nullable final IServiceProviderPlace place) {
try {
logger.warn("shutting down the failed place: {}", loc);
if (place != null) {
place.shutDown();
} else {
// Force keys to be deregistered if we can
deregisterPlace(loc);
}
Namespace.unbind(loc);
} catch (Throwable tt) {
logger.error("whoa there pardner... {}", loc, tt);
}
}
public static void deregisterPlace(final String loc) {
try {
final IDirectoryPlace localDir = DirectoryPlace.lookup();
final List<DirectoryEntry> entries = localDir.getMatchingEntries("*." + loc);
if (entries != null && !entries.isEmpty()) {
final List<String> keys = new ArrayList<>();
for (final DirectoryEntry entry : entries) {
keys.add(entry.getKey());
}
logger.info("Forcing removal of {} keys due to failed {}", keys.size(), loc);
localDir.removePlaces(keys);
} else {
logger.debug("Failed {} did not have any directory keys registered", loc);
}
} catch (EmissaryException ee) {
logger.debug("NO local directory, cannot force key dereg for {}", loc);
}
}
// ////////////////////////////////////////////////////////////
/**
* method to check if the place already exists.
*/
// ////////////////////////////////////////////////////////////
@Nullable
public static IServiceProviderPlace alreadyExists(final String theLocation) {
final String thePlaceHost = Startup.placeHost(theLocation);
// TODO should we add a check for index of? Can cause an exception if // isn't present
final String luStr = theLocation.substring(theLocation.indexOf("//"));
try {
final IServiceProviderPlace thePlace = (IServiceProviderPlace) Namespace.lookup(luStr);
logger.debug("{} already running on {}", theLocation, thePlaceHost);
return thePlace;
} catch (NamespaceException nse) {
// expected when the place doesn't exist
} catch (Throwable t) {
// empty catch block
}
return null;
}
public static String getClassString(final String theLocation) {
return getClassString(theLocation, false);
}
public static String getClassString(final String theLocation, boolean isStrictMode) {
final String thePlaceName = Startup.placeName(theLocation);
if (StringUtils.isBlank(thePlaceName)) {
logger.error("Illegal location specified {}, has no place name", theLocation);
}
final List<String> classStringList = classConf.findEntries(thePlaceName);
if (classStringList.isEmpty()) {
logger.error("Need a CLASS config entry for {} check entry in emissary.admin.ClassNameInventory.cfg, using default "
+ "{} which is probably not what you want.", thePlaceName, defaultClassName);
return defaultClassName;
} else if (classStringList.size() > 1) {
if (isStrictMode) {
throw new EmissaryRuntimeException("Multiple entries for " + thePlaceName + ", found " + classStringList);
}
logger.warn("Multiple entries for {}, found {}", thePlaceName, classStringList);
}
return classStringList.get(0);
}
/** This class is not meant to be instantiated. */
private PlaceStarter() {}
}