
org.jppf.client.AbstractGenericClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jppf-client Show documentation
Show all versions of jppf-client Show documentation
JPPF, the open source grid computing solution
/*
* JPPF.
* Copyright (C) 2005-2015 JPPF Team.
* http://www.jppf.org
*
* 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.
*/
package org.jppf.client;
import java.util.*;
import java.util.concurrent.*;
import org.jppf.client.balancer.*;
import org.jppf.client.balancer.queue.JPPFPriorityQueue;
import org.jppf.client.event.*;
import org.jppf.comm.discovery.*;
import org.jppf.queue.*;
import org.jppf.startup.JPPFClientStartupSPI;
import org.jppf.utils.*;
import org.jppf.utils.configuration.ConfigurationHelper;
import org.jppf.utils.hooks.HookFactory;
import org.slf4j.*;
/**
* This class provides an API to submit execution requests and administration commands,
* and request server information data.
* It has its own unique identifier, used by the nodes, to determine whether classes from
* the submitting application should be dynamically reloaded or not, depending on whether
* the uuid has changed or not.
* @author Laurent Cohen
*/
public abstract class AbstractGenericClient extends AbstractJPPFClient implements QueueListener {
/**
* Logger for this class.
*/
private static Logger log = LoggerFactory.getLogger(AbstractGenericClient.class);
/**
* Determines whether debug-level logging is enabled.
*/
private static boolean debugEnabled = LoggingUtils.isDebugEnabled(log);
/**
* Constant for JPPF automatic connection discovery
*/
static final String VALUE_JPPF_DISCOVERY = "jppf_discovery";
/**
* The pool of threads used for submitting execution requests.
*/
private ThreadPoolExecutor executor = null;
/**
* The JPPF configuration properties.
*/
private TypedProperties config;
/**
* Performs server discovery.
*/
private JPPFMulticastReceiverThread receiverThread = null;
/**
* The job manager.
*/
private JobManager jobManager;
/**
* Handles the class loaders used for inbound class loading requests from the servers.
*/
private final ClassLoaderRegistrationHandler classLoaderRegistrationHandler;
/**
* The list of listeners on the queue associated with this client.
*/
private final List queueListeners = new CopyOnWriteArrayList<>();
/**
* Initialize this client with a specified application UUID.
* @param uuid the unique identifier for this local client.
* @param configuration the object holding the JPPF configuration.
* @param listeners the listeners to add to this JPPF client to receive notifications of new connections.
*/
public AbstractGenericClient(final String uuid, final TypedProperties configuration, final ConnectionPoolListener... listeners) {
super(uuid);
this.classLoaderRegistrationHandler = new ClassLoaderRegistrationHandler();
if ((listeners != null) && (listeners.length > 0)) for (ConnectionPoolListener listener : listeners) addConnectionPoolListener(listener);
init(configuration);
}
/**
* Initialize this client with the specified configuration.
* @param configuration the configuration to use with this client.
*/
protected void init(final TypedProperties configuration) {
closed.set(false);
resetting.set(false);
this.config = initConfig(configuration);
try {
HookFactory.registerSPIMultipleHook(JPPFClientStartupSPI.class, null, null).invoke("run");
} catch (Exception e) {
log.error(e.getMessage(), e);
}
if (jobManager == null) jobManager = createJobManager();
//System.out.println("jobManager = " + jobManager);
Runnable r = new Runnable() {
@Override
public void run() {
initPools(config);
}
};
new Thread(r, "InitPools").start();
}
/**
* Get JPPF configuration properties. These properties are unmodifiable.
* @return TypedProperties
instance. With JPPF configuration.
*/
public TypedProperties getConfig() {
return config;
}
/**
* Initialize this client's configuration.
* @param configuration an object holding the JPPF configuration.
* @return TypedProperties
instance holding JPPF configuration. Never be null
.
* @exclude
*/
protected TypedProperties initConfig(final Object configuration) {
if (configuration instanceof TypedProperties) return (TypedProperties) configuration;
return JPPFConfiguration.getProperties();
}
/**
* {@inheritDoc}
* @exclude
*/
@Override
@SuppressWarnings("unchecked")
protected void initPools(final TypedProperties config) {
if (debugEnabled) log.debug("initializing connections");
int coreThreads = Runtime.getRuntime().availableProcessors();
//LinkedBlockingQueue queue = new LinkedBlockingQueue<>(coreThreads);
//BlockingQueue queue = new LinkedBlockingQueue<>();
BlockingQueue queue = new SynchronousQueue<>();
executor = new ThreadPoolExecutor(coreThreads, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, queue, new JPPFThreadFactory("JPPF Client"));
executor.allowCoreThreadTimeOut(true);
if (config.getBoolean("jppf.local.execution.enabled", false)) setLocalExecutionEnabled(true);
if (config.getBoolean("jppf.remote.execution.enabled", true)) initRemotePools(config);
}
/**
* Initialize remote connection pools according to configuration.
* @param config The JPPF configuration properties.
* @exclude
*/
protected void initRemotePools(final TypedProperties config) {
try {
boolean initPeers;
if (config.getBoolean("jppf.discovery.enabled", true)) {
final int priority = config.getInt("jppf.discovery.priority", 0);
boolean acceptMultipleInterfaces = config.getBoolean("jppf.discovery.acceptMultipleInterfaces", false);
if (debugEnabled) log.debug("initializing connections from discovery with priority = {} and acceptMultipleInterfaces = {}", priority, acceptMultipleInterfaces);
receiverThread = new JPPFMulticastReceiverThread(new JPPFMulticastReceiverThread.ConnectionHandler() {
@Override
public void onNewConnection(final String name, final JPPFConnectionInformation info) {
ConfigurationHelper ch = new ConfigurationHelper(config);
boolean ssl = config.getBoolean("jppf.ssl.enabled", false);
if (info.hasValidPort(ssl)) {
int poolSize = ch.getInt("jppf.pool.size", 1, 1, Integer.MAX_VALUE);
int jmxPoolSize = ch.getInt("jppf.jmx.pool.size", 1, 1, Integer.MAX_VALUE);
newConnectionPool(name, info, priority, poolSize, ssl, jmxPoolSize);
} else {
String type = ssl ? "secure" : "plain";
String msg = String.format("this client cannot fulfill a %s connection request to %s:%d because the host does not expose that port as a %s port",
type, info.host, info.getValidPort(ssl), type);
log.warn(msg);
}
}
}, new IPFilter(config), acceptMultipleInterfaces);
new Thread(receiverThread).start();
initPeers = false;
} else {
receiverThread = null;
initPeers = true;
}
if (debugEnabled) log.debug("found peers in the configuration");
String discoveryNames = config.getString("jppf.drivers");
if ((discoveryNames == null) || "".equals(discoveryNames.trim())) discoveryNames = "default-driver";
if (debugEnabled) log.debug("list of drivers: " + discoveryNames);
String[] names = discoveryNames.split("\\s");
for (String name : names) initPeers |= VALUE_JPPF_DISCOVERY.equals(name);
if (initPeers) {
for (String name : names) {
if (!VALUE_JPPF_DISCOVERY.equals(name)) {
JPPFConnectionInformation info = new JPPFConnectionInformation();
boolean ssl = config.getBoolean(name + ".jppf.ssl.enabled", false);
info.host = config.getString(name + ".jppf.server.host", "localhost");
int port = config.getInt(name + ".jppf.server.port", ssl ? 11443 : 11111);
if (!ssl) info.serverPorts = new int[] { port };
else info.sslServerPorts = new int[] { port };
if (!ssl) info.managementPort = config.getInt(name + ".jppf.management.port", -1);
else info.sslManagementPort = config.getInt(name + ".jppf.management.port", -1);
int priority = config.getInt(name + ".jppf.priority", 0);
if (receiverThread != null) receiverThread.addConnectionInformation(info);
ConfigurationHelper ch = new ConfigurationHelper(config);
int poolSize = ch.getInt(name + ".jppf.pool.size", 1, 1, Integer.MAX_VALUE);
int jmxPoolSize = ch.getInt(name + ".jppf.jmx.pool.size", 1, 1, Integer.MAX_VALUE);
newConnectionPool(name, info, priority, poolSize, ssl, jmxPoolSize);
}
}
}
} catch(Exception e) {
log.error(e.getMessage(), e);
}
}
/**
* Called when a new connection pool is read from the configuration.
* @param name the name assigned to the connection pool.
* @param info the information required for the connection to connect to the driver.
* @param priority the priority assigned to the connection.
* @param poolSize the size of the connection pool.
* @param ssl determines whether the pool is for SSL connections.
* @param jmxPoolSize the core size of the JMX connections pool.
* @exclude
*/
protected void newConnectionPool(final String name, final JPPFConnectionInformation info, final int priority, final int poolSize, final boolean ssl, final int jmxPoolSize) {
if (debugEnabled) log.debug("new connection pool: {}", name);
final int size = poolSize > 0 ? poolSize : 1;
Runnable r = new Runnable() {
@Override public void run() {
final JPPFConnectionPool pool = new JPPFConnectionPool((JPPFClient) AbstractGenericClient.this, poolSequence.incrementAndGet(), name, priority, size, ssl, jmxPoolSize);
pool.setDriverPort(ssl ? info.sslServerPorts[0] : info.serverPorts[0]);
synchronized(pools) {
pools.putValue(priority, pool);
pendingPools.add(pool);
}
HostIP hostIP = new HostIP(info.host, info.host);
if (JPPFConfiguration.getProperties().getBoolean("org.jppf.resolve.addresses", true)) hostIP = NetworkUtils.getHostIP(info.host);
if (debugEnabled) log.debug("'{}' was resolved into '{}'", info.host, hostIP.hostName());
pool.setDriverHostIP(hostIP);
fireConnectionPoolAdded(pool);
for (int i=1; i<=size; i++) {
if (isClosed()) return;
submitNewConnection(info, pool);
}
}
};
executor.submit(r);
}
/**
* Called to submit the initialization of a new connection.
* @param info the information required for the connection to connect to the driver.
* @param pool thez connection pool to which the connection belongs.
* @exclude
*/
protected void submitNewConnection(final JPPFConnectionInformation info, final JPPFConnectionPool pool) {
AbstractJPPFClientConnection c = createConnection(info.uuid, pool.getName() + "-" + pool.nextSequence(), info, pool);
newConnection(c);
}
/**
* Create a new driver connection based on the specified parameters.
* @param uuid the uuid of the remote JPPF driver.
* @param name the name of the connection.
* @param info the driver connection information.
* @param pool id of the connection pool the connection belongs to.
* @return an instance of a subclass of {@link AbstractJPPFClientConnection}.
*/
abstract AbstractJPPFClientConnection createConnection(String uuid, String name, JPPFConnectionInformation info, final JPPFConnectionPool pool);
@Override
void newConnection(final AbstractJPPFClientConnection c) {
if (isClosed()) return;
log.info("connection [" + c.getName() + "] created");
c.addClientConnectionStatusListener(this);
//c.setStatus(JPPFClientConnectionStatus.NEW);
executor.submit(new ConnectionInitializer(c));
fireConnectionAdded(c);
if (debugEnabled) log.debug("end of of newConnection({})", c.getName());
}
/**
* Invoked when the status of a connection has changed to JPPFClientConnectionStatus.FAILED
.
* @param connection the connection that failed.
* @exclude
*/
@Override
protected void connectionFailed(final JPPFClientConnection connection) {
if (debugEnabled) log.debug("Connection [{}] {}", connection.getName(), connection.getStatus());
JPPFConnectionPool pool = connection.getConnectionPool();
connection.close();
boolean poolRemoved = removeClientConnection(connection);
fireConnectionRemoved(connection);
if (poolRemoved) {
fireConnectionPoolRemoved(pool);
if (receiverThread != null) receiverThread.removeConnectionInformation(connection.getDriverUuid());
}
}
@Override
public void close() {
close(false);
}
/**
* Close this client.
* @param reset if true
, then this client is left in a state where it can be reopened.
* @exclude
*/
protected void close(final boolean reset) {
if (closed.get()) return;
if (debugEnabled) log.debug("closing JPPF client");
closed.set(true);
if (debugEnabled) log.debug("closing broadcast receiver");
if (receiverThread != null) {
receiverThread.close();
receiverThread = null;
}
if (debugEnabled) log.debug("unregistering startup classes");
HookFactory.unregister(JPPFClientStartupSPI.class);
if (jobManager != null) {
if (reset) {
if (debugEnabled) log.debug("resetting job manager");
jobManager.reset();
} else {
if (debugEnabled) log.debug("closing job manager");
jobManager.close();
jobManager = null;
}
}
if (debugEnabled) log.debug("closing executor");
if (executor != null) {
executor.shutdownNow();
executor = null;
}
if (debugEnabled) log.debug("clearing registered class loaders");
classLoaderRegistrationHandler.close();
super.close();
}
/**
* Determine whether local execution is enabled on this client.
* @return true
if local execution is enabled, false
otherwise.
*/
public boolean isLocalExecutionEnabled() {
JobManager jobManager = getJobManager();
return (jobManager != null) && jobManager.isLocalExecutionEnabled();
}
/**
* Specify whether local execution is enabled on this client.
* @param localExecutionEnabled true
to enable local execution, false
otherwise
*/
public void setLocalExecutionEnabled(final boolean localExecutionEnabled) {
JobManager jobManager = getJobManager();
if (jobManager != null) jobManager.setLocalExecutionEnabled(localExecutionEnabled);
}
/**
* Determine whether there is a client connection available for execution.
* @return true if at least one connection is available, false otherwise.
*/
public boolean hasAvailableConnection() {
JobManager jobManager = getJobManager();
return (jobManager != null) && jobManager.hasAvailableConnection();
}
/**
* {@inheritDoc}
* @exclude
*/
@Override
public void statusChanged(final ClientConnectionStatusEvent event) {
super.statusChanged(event);
JobManager jobManager = getJobManager();
if(jobManager != null) {
ClientConnectionStatusListener listener = jobManager.getClientConnectionStatusListener();
if (listener != null) listener.statusChanged(event);
if (jobManager instanceof ThreadSynchronization) ((ThreadSynchronization) jobManager).wakeUp();
}
}
/**
* Get the pool of threads used for submitting execution requests.
* @return a {@link ThreadPoolExecutor} instance.
* @exclude
*/
public ThreadPoolExecutor getExecutor() {
return executor;
}
/**
* Get the job manager for this JPPF client.
* @return a JobManager
instance.
* @exclude
*/
public JobManager getJobManager() {
return jobManager;
}
/**
* Create the job manager for this JPPF client.
* @return a JobManager
instance.
*/
abstract JobManager createJobManager();
/**
* Cancel the job with the specified id.
* @param jobId the id of the job to cancel.
* @throws Exception if any error occurs.
* @see org.jppf.server.job.management.DriverJobManagementMBean#cancelJob(java.lang.String)
* @return a true
when cancel was successful false
otherwise.
*/
public boolean cancelJob(final String jobId) throws Exception {
if (jobId == null || jobId.isEmpty()) throw new IllegalArgumentException("jobUUID is blank");
if (debugEnabled) log.debug("request to cancel job with uuid=" + jobId);
return getJobManager().cancelJob(jobId);
}
/**
* Get a class loader associated with a job.
* @param uuid unique id assigned to classLoader. Added as temporary fix for problems hanging jobs.
* @return a {@code Collection} of {@code RegisteredClassLoader} instances.
* @exclude
*/
public Collection getRegisteredClassLoaders(final String uuid) {
return classLoaderRegistrationHandler.getRegisteredClassLoaders(uuid);
}
/**
* Register a class loader for the specified job uuid.
* @param cl the ClassLoader
instance to register.
* @param uuid the uuid of the job for which the class loader is registered.
* @return a RegisteredClassLoader
instance.
*/
public ClassLoader registerClassLoader(final ClassLoader cl, final String uuid) {
return classLoaderRegistrationHandler.registerClassLoader(cl, uuid);
}
/**
* Unregisters the class loader associated with the specified job uuid.
* @param uuid the uuid of the job the class loaders are associated with.
* @exclude
*/
public void unregisterClassLoaders(final String uuid) {
classLoaderRegistrationHandler.unregister(uuid);
}
/**
* Register the specified listener to receive client queue event notifications.
* @param listener the listener to register.
* @since 4.1
*/
public void addClientQueueListener(final ClientQueueListener listener) {
queueListeners.add(listener);
}
/**
* Unregister the specified listener.
* @param listener the listener to unregister.
* @since 4.1
*/
public void removeClientQueueListener(final ClientQueueListener listener) {
queueListeners.remove(listener);
}
/**
* Notify all client queue listeners that a queue event has occurred.
* @param qEvent the actual event which occurred in the queue.
* @param jobAdded {@code true} for a job added event, {@code false} for a job removed event.
* @exclude
* @since 4.1
*/
protected void fireQueueEvent(final QueueEvent qEvent, final boolean jobAdded) {
ClientQueueEvent event = new ClientQueueEvent((JPPFClient) this, qEvent.getBundleWrapper().getJob(), (JPPFPriorityQueue) qEvent.getQueue());
if (jobAdded) {
for (ClientQueueListener listener: queueListeners) listener.jobAdded(event);
} else {
for (ClientQueueListener listener: queueListeners) listener.jobRemoved(event);
}
}
/**
* {@inheritDoc}
* @exclude
* @since 4.1
*/
@Override
public void bundleAdded(final QueueEvent event) {
fireQueueEvent(event, true);
}
/**
* {@inheritDoc}
* @exclude
* @since 4.1
*/
@Override
public void bundleRemoved(final QueueEvent event) {
fireQueueEvent(event, false);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy