/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*/
package com.sun.sgs.impl.service.nodemap.affinity;
import com.sun.sgs.auth.Identity;
import com.sun.sgs.impl.kernel.StandardProperties;
import com.sun.sgs.impl.service.nodemap.affinity.graph.AffinityGraphBuilder;
import com.sun.sgs.impl.service.nodemap.affinity.graph.GraphListener;
import com.sun.sgs.impl.sharedutil.LoggerWrapper;
import com.sun.sgs.impl.sharedutil.PropertiesWrapper;
import com.sun.sgs.impl.util.AbstractKernelRunnable;
import com.sun.sgs.kernel.ComponentRegistry;
import com.sun.sgs.kernel.NodeType;
import com.sun.sgs.kernel.RecurringTaskHandle;
import com.sun.sgs.kernel.TaskScheduler;
import com.sun.sgs.profile.ProfileCollector;
import com.sun.sgs.service.TransactionProxy;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A driver for the Label Propagation Algorithm affinity group finders.
* The driver is responsible for instantiating the affinity group system,
* managing its life cycle, and invoking the group finder as necessary.
* <p>
* A driver must be instantiated on each node of the Darkstar cluster.
* <p>
* The following properties are supported:
* <p>
* <dl style="margin-left: 1em">
*
* <dt> <i>Property:</i> <code><b>
* com.sun.sgs.impl.service.nodemap.affinity.graphbuilder.class
* </b></code><br>
* <i>Default:</i>
* {@code
* com.sun.sgs.impl.service.nodemap.affinity.graph.dlpa.WeightedGraphBuilder}
* <br>
*
* <dd style="padding-top: .5em">The graph builder to use. Set to
* {@code None} if no affinity group finding is required, which is
* useful for testing. <p>
*
* <dt> <i>Property:</i> <code><b>
* com.sun.sgs.impl.service.nodemap.affinity.update.period
* </b></code><br>
* <i>Default:</i> {@code 60} (one minute)}
* <br>
*
* <dd style="padding-top: .5em">The frequency that we find affinity groups,
* in seconds. The value must be between {@code 5} and {@code 65535}.<p>
* </dl>
*/
public class LPADriver extends BasicState implements AffinityGroupFinder {
/** The base name for properties. */
private static final String PROP_NAME =
"com.sun.sgs.impl.service.nodemap.affinity";
/** The property for specifying the graph builder class. */
public static final String GRAPH_CLASS_PROPERTY =
PROP_NAME + ".graphbuilder.class";
/** The property name for the update frequency. */
public static final String UPDATE_FREQ_PROPERTY =
PROP_NAME + ".update.freq";
/** The default value of the update frequency, in seconds. */
static final int DEFAULT_UPDATE_FREQ = 60;
/**
* The value to be given to {@code GRAPH_CLASS_PROPERTY} if no
* affinity group finding should be instantiated (useful for testing).
*/
public static final String GRAPH_CLASS_NONE = "None";
private static final LoggerWrapper logger =
new LoggerWrapper(Logger.getLogger(PROP_NAME));
/** A graph listener, used for detecting affinity groups, or {@code null}
* if no graph listener is being used.
*/
private final GraphListener graphListener;
/** The affinity graph builder, null if there is none. */
private final AffinityGraphBuilder graphBuilder;
private final TaskScheduler taskScheduler;
private final Identity taskOwner;
private final long updatePeriod;
private RecurringTaskHandle updateTask = null;
/**
* Constructs an instance of this class with the specified properties.
* <p>
* @param properties the properties for configuring this subsystem
* @param systemRegistry the registry of available system components
* @param txnProxy the transaction proxy
*
* @throws Exception if an error occurs during creation
*/
public LPADriver(Properties properties,
ComponentRegistry systemRegistry,
TransactionProxy txnProxy)
throws Exception
{
PropertiesWrapper wrappedProps = new PropertiesWrapper(properties);
int updateSeconds = wrappedProps.getIntProperty(UPDATE_FREQ_PROPERTY,
DEFAULT_UPDATE_FREQ, 5, 65535);
updatePeriod = updateSeconds * 1000L;
taskScheduler = systemRegistry.getComponent(TaskScheduler.class);
taskOwner = txnProxy.getCurrentOwner();
NodeType type =
NodeType.valueOf(
properties.getProperty(StandardProperties.NODE_TYPE));
String builderName = wrappedProps.getProperty(GRAPH_CLASS_PROPERTY);
if (GRAPH_CLASS_NONE.equals(builderName)) {
// do not instantiate anything
graphBuilder = null;
graphListener = null;
return;
}
if (builderName != null) {
graphBuilder = wrappedProps.getClassInstanceProperty(
GRAPH_CLASS_PROPERTY, AffinityGraphBuilder.class,
new Class[] { Properties.class,
ComponentRegistry.class,
TransactionProxy.class },
properties, systemRegistry, txnProxy);
// TODO: The following code is commented out while our default is none,
// mostly to keep findbugs quiet. In the future, we expect the
// WeightedGraphBuilder to be the default if we're not in single node
// mode.
// } else if (type != NodeType.singleNode) {
// // Default is currently NONE, might become the distributed LPA/
// // weighted graph listener in the future.
// builder = null;
} else {
// Either we're in multi-node, but the current default is
// no action, or we're in single node and no builder was requested.
//
// If we're in single node, and no builder was requested,
// don't bother creating anything. Affinity groups will make
// no sense.
graphBuilder = null;
}
// Add the self as listener if there is a builder and we are
// not a core server node.
if (graphBuilder != null && type != NodeType.coreServerNode) {
ProfileCollector col =
systemRegistry.getComponent(ProfileCollector.class);
graphListener = new GraphListener(graphBuilder);
col.addListener(graphListener, false);
} else {
graphListener = null;
}
logger.log(Level.CONFIG,
"Created LPADriver with listener: " + graphListener +
", builder: " + graphBuilder + ", and properties:" +
"\n " + GRAPH_CLASS_PROPERTY + "=" + builderName +
"\n " + UPDATE_FREQ_PROPERTY + "=" + updateSeconds);
}
/** {@inheritDoc} */
@Override
public void disable() {
if (setDisabledState()) {
logger.log(Level.FINE, "LPA driver disabled");
if (graphBuilder != null) {
graphBuilder.disable();
}
if (updateTask != null) {
updateTask.cancel();
updateTask = null;
}
}
}
/** {@inheritDoc} */
@Override
public void enable() {
if (setEnabledState()) {
logger.log(Level.FINE, "LPA driver enabled");
if (graphBuilder != null) {
graphBuilder.enable();
if (graphBuilder.getAffinityGroupFinder() != null) {
updateTask = taskScheduler.scheduleRecurringTask(
new AbstractKernelRunnable("UpdateTask") {
public void run() {
findGroups();
} },
taskOwner,
System.currentTimeMillis() + updatePeriod,
updatePeriod);
updateTask.start();
}
}
}
}
/** {@inheritDoc} */
@Override
public void shutdown() {
if (setShutdownState()) {
logger.log(Level.FINE, "LPA driver shut down");
if (updateTask != null) {
updateTask.cancel();
}
if (graphListener != null) {
graphListener.shutdown();
}
if (graphBuilder != null) {
graphBuilder.shutdown();
}
}
}
/**
* Returns the graph builder used by this driver, or {@code null} if
* we are not building graphs.
* @return the graph builder or {@code null} if there is none
*/
public AffinityGraphBuilder getGraphBuilder() {
return graphBuilder;
}
/**
* Returns the graph listener used by this driver, or {@code null} if
* we are not building graphs.
* @return the graph listener or {@code null} if there is none
*/
public GraphListener getGraphListener() {
return graphListener;
}
/**
* Try to gather a new set of groups, pushing the results if successful.
*/
private void findGroups() {
try {
// TODO: Dead store until integrated with coordinator
// Set<AffinityGroup> groups =
graphBuilder.getAffinityGroupFinder().findAffinityGroups();
// tell the coordinator about the found groups
} catch (AffinityGroupFinderFailedException e) {
logger.logThrow(Level.INFO, e, "Affinity group finder failed");
}
}
}