
org.jppf.client.BaseJPPFClientConnection 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
The newest version!
/*
* JPPF.
* Copyright (C) 2005-2019 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 static org.jppf.client.JPPFClientConnectionStatus.NEW;
import java.io.NotSerializableException;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import org.jppf.JPPFException;
import org.jppf.client.balancer.ClientTaskBundle;
import org.jppf.comm.socket.*;
import org.jppf.io.IOHelper;
import org.jppf.node.protocol.*;
import org.jppf.node.protocol.graph.TaskGraphInfo;
import org.jppf.serialization.*;
import org.jppf.utils.*;
import org.slf4j.*;
/**
* Instances of this class represent connections to remote JPPF drivers.
* @author Laurent Cohen
*/
abstract class BaseJPPFClientConnection implements JPPFClientConnection {
/**
* Logger for this class.
*/
private static Logger log = LoggerFactory.getLogger(BaseJPPFClientConnection.class);
/**
* Determines whether the debug level is enabled in the logging configuration, without the cost of a method call.
*/
private static boolean debugEnabled = LoggingUtils.isDebugEnabled(log);
/**
* Determines whether the trace level is enabled in the logging configuration, without the cost of a method call.
*/
private static boolean traceEnabled = log.isTraceEnabled();
/**
* Used to prevent parallel deserialization.
*/
private static Lock lock = new ReentrantLock();
/**
* Determines whether tasks deserialization should be sequential rather than parallel.
*/
private final boolean SEQUENTIAL_DESERIALIZATION;
/**
* A sequence number used as suffix for the {@code connectionUuid}.
*/
static AtomicInteger connectionCount = new AtomicInteger(0);
/**
* Handler for the connection to the task server.
*/
TaskServerConnectionHandler taskServerConnection;
/**
* Enables loading local classes onto remote nodes.
*/
ClassServerDelegate delegate;
/**
* Configuration name for this local client.
*/
String name;
/**
* Unique ID for this connection and its two channels.
*/
String connectionUuid;
/**
* Status of the connection.
*/
AtomicReference status = new AtomicReference<>(NEW);
/**
* Synchronize on status changes.
*/
final Object statusChangeLock = new Object();
/**
* The connection pool this connection belongs to.
*/
final JPPFConnectionPool pool;
/**
*
*/
private final ObjectSerializer defaultSerializer;
/**
* Initialize this connection with a parent pool.
* @param pool the connection pool this connection belongs to.
*/
BaseJPPFClientConnection(final JPPFConnectionPool pool) {
this.pool = pool;
SEQUENTIAL_DESERIALIZATION = pool.getClient().getConfig().getBoolean("jppf.sequential.deserialization", false);
defaultSerializer = new ObjectSerializerImpl();
}
/**
* Initialize this client connection.
*/
abstract void init();
/**
* Send tasks to the server for execution.
* @param ser the serializer to use.
* @param cl classloader used for serialization.
* @param header the task bundle to send to the driver.
* @param clientBundle the job to execute remotely.
* @return a list of tasks that couldn't be serialized, possibly empty.
* @throws Exception if an error occurs while sending the request.
*/
public List> sendTasks(final ObjectSerializer ser, final ClassLoader cl, final TaskBundle header, final ClientTaskBundle clientBundle) throws Exception {
final JPPFJob job = clientBundle.getClientJob().getJob();
final TraversalList uuidPath = new TraversalList<>();
uuidPath.add(pool.getClient().getUuid());
header.setUuidPath(uuidPath);
header.setTaskCount(clientBundle.getTasksL().size());
header.setName(job.getName());
header.setUuid(job.getUuid());
header.setSLA(job.getSLA());
header.setMetadata(job.getMetadata());
List extends PositionalElement>> deps = null;
TaskGraphInfo graphInfo = null;
if ((clientBundle.getClientJob().getTaskGraph() != null) && job.getClientSLA().isGraphTraversalInClient()) graphInfo = clientBundle.getGraphInfo();
if (graphInfo != null) {
deps = graphInfo.getDependencies();
header.setParameter(BundleParameter.JOB_TASK_GRAPH_INFO, graphInfo);
final int[] positions = graphInfo.getDependenciesPositions();
if (debugEnabled) log.debug("sending {} dependencies with positions {}", deps.size(), Arrays.toString(positions));
}
if (debugEnabled) log.debug("found {} dependencies for bundle {}", (deps == null ? 0 : deps.size()), clientBundle);
final List> tasks = prepareTasksToSend(header, clientBundle);
final SocketWrapper socketClient = taskServerConnection.getSocketClient();
IOHelper.sendData(socketClient, header, ser);
try {
IOHelper.sendData(socketClient, job.getDataProvider(), ser);
} catch(final NotSerializableException e) {
log.error("error serializing data provider for {} : {}\nthe job will be cancelled", job, ExceptionUtils.getStackTrace(e));
IOHelper.sendData(socketClient, null, ser);
}
final List> notSerializableTasks = sendTasks(job, ser, socketClient, tasks);
if (deps != null) sendTasks(job, ser, socketClient, deps);
socketClient.flush();
return notSerializableTasks;
}
/**
* Send a set of tasks to a driver.
* @param job the job towhich the tasks belong.
* @param ser the serializer to use.
* @param socketClient an abstraction of the socket connection to send the tasks through.
* @param tasks the tasks to send.
* @return a list of tasks that couldn't be serialized, possibly empty.
* @throws Exception if an error occurs while sending the request.
*/
private static List> sendTasks(final JPPFJob job, final ObjectSerializer ser, final SocketWrapper socketClient, final List extends PositionalElement>> tasks) throws Exception {
final List> notSerializableTasks = new ArrayList<>(tasks.size());
for (final PositionalElement> task : tasks) {
try {
IOHelper.sendData(socketClient, task, ser);
} catch(final NotSerializableException e) {
log.error("error serializing task {} for {} : {}", task, job, ExceptionUtils.getStackTrace(e));
((Task>) task).setThrowable(e);
IOHelper.sendNullData(socketClient);
notSerializableTasks.add((Task>) task);
}
}
return notSerializableTasks;
}
/**
* Prepare the job header for the remaining tasks to send in the job.
* @param header the job header sent to the driver.
* @param clientBundle the job whose taskss are to be sent.
* @return an array of the tasks to send.
*/
private List> prepareTasksToSend(final TaskBundle header, final ClientTaskBundle clientBundle) {
final List> allTasks = clientBundle.getTasksL();
final int count = allTasks.size();
final int[] positions = new int[count];
final int[] maxResubmits = new int[count];
final List> tasks = new ArrayList<>(count);
int i = 0;
final JPPFJob job = clientBundle.getClientJob().getJob();
for (final Task> task : allTasks) {
final int pos = task.getPosition();
if (!job.getResults().hasResult(pos)) {
tasks.add(task);
positions[i] = pos;
maxResubmits[i] = task.getMaxResubmits();
i++;
}
}
header.setParameter(BundleParameter.TASK_POSITIONS, positions);
header.setParameter(BundleParameter.TASK_MAX_RESUBMITS, maxResubmits);
if (traceEnabled) log.trace(this.toDebugString() + " sending job " + header + ", positions=" + StringUtils.buildString(positions));
return tasks;
}
/**
* Send a handshake job to the server.
* @return a {@link TaskBundle} sent by the server in response to the handshake job.
* @throws Exception if an error occurs while sending the request.
*/
TaskBundle sendHandshakeJob() throws Exception {
final TaskBundle header = new JPPFTaskBundle();
final ObjectSerializer ser = new ObjectSerializerImpl();
final TraversalList uuidPath = new TraversalList<>();
uuidPath.add(pool.getClient().getUuid());
header.setUuidPath(uuidPath);
if (debugEnabled) log.debug("{} sending handshake job, uuidPath={}", toDebugString(), uuidPath);
header.setUuid(JPPFUuid.normalUUID());
header.setName("handshake job");
header.setHandshake(true);
header.setUuid(header.getName());
header.setParameter(BundleParameter.CONNECTION_UUID, connectionUuid);
header.setSLA(null);
header.setMetadata(null);
final SocketWrapper socketClient = taskServerConnection.getSocketClient();
IOHelper.sendData(socketClient, header, ser);
IOHelper.sendData(socketClient, null, ser); // null data provider
socketClient.flush();
if (debugEnabled) log.debug("{} sent handshake job, receiving handshake results", toDebugString());
return receiveBundleAndResults(ser, getClass().getClassLoader()).first();
}
/**
* Send a close command job to the server.
* @throws Exception if an error occurs while sending the request.
*/
void sendCloseConnectionCommand() throws Exception {
if (taskServerConnection == null) return;
final TaskBundle header = new JPPFTaskBundle();
final ObjectSerializer ser = new ObjectSerializerImpl();
final TraversalList uuidPath = new TraversalList<>();
uuidPath.add(pool.getClient().getUuid());
header.setUuidPath(uuidPath);
if (debugEnabled) log.debug("{} sending close command job, uuidPath={}", toDebugString(), uuidPath);
header.setName("close command job");
header.setUuid("close command job");
header.setParameter(BundleParameter.CONNECTION_UUID, connectionUuid);
header.setParameter(BundleParameter.CLOSE_COMMAND, true);
header.setSLA(null);
header.setMetadata(null);
final SocketWrapper socketClient = taskServerConnection.getSocketClient();
if (socketClient != null) {
IOHelper.sendData(socketClient, header, ser);
IOHelper.sendData(socketClient, null, ser); // null data provider
socketClient.flush();
}
if (debugEnabled) log.debug("{} sent close command job", toDebugString());
}
/**
* Receive results of tasks execution.
* @param ser the serializer to use.
* @param cl the class loader to use for deserializing the tasks.
* @return a pair of objects representing the executed tasks results, and the index of the first result within the initial task execution request.
* @throws Exception if an error is raised while reading the results from the server.
*/
public BundleWithTasks receiveBundleAndResults(final ObjectSerializer ser, final ClassLoader cl) throws Exception {
final TaskBundle bundle = receiveHeader(ser, cl);
final List> tasks = receiveTasks(bundle, ser, cl);
return new BundleWithTasks(bundle, tasks);
}
/**
* Receive the header part of the results of tasks execution.
* @param ser the serializer to use.
* @param cl the class loader to use for deserializing the tasks.
* @return a {@link TaskBundle} instance.
* @throws Exception if an error is raised while reading the results from the server.
*/
public TaskBundle receiveHeader(final ObjectSerializer ser, final ClassLoader cl) throws Exception {
TaskBundle bundle = null;
final ObjectSerializer actualSerializer = (ser == null) ? defaultSerializer : ser;
final ClassLoader ctxCl = Thread.currentThread().getContextClassLoader();
try {
final ClassLoader loader = (cl == null) ? getClass().getClassLoader() : cl;
Thread.currentThread().setContextClassLoader(loader);
final SocketWrapper socketClient = taskServerConnection.getSocketClient();
bundle = (TaskBundle) IOHelper.unwrappedData(socketClient, actualSerializer);
return bundle;
} finally {
Thread.currentThread().setContextClassLoader(ctxCl);
}
}
/**
* Receive results of tasks execution.
* @param bundle the job header.
* @param ser the serializer to use.
* @param cl the class loader to use for deserializing the tasks.
* @return a pair of objects representing the executed tasks results, and the index of the first result within the initial task execution request.
* @throws Exception if an error is raised while reading the results from the server.
*/
public List> receiveTasks(final TaskBundle bundle, final ObjectSerializer ser, final ClassLoader cl) throws Exception {
final ClassLoader ctxCl = Thread.currentThread().getContextClassLoader();
try {
final ClassLoader loader = (cl == null) ? getClass().getClassLoader() : cl;
Thread.currentThread().setContextClassLoader(loader);
final SocketWrapper socketClient = taskServerConnection.getSocketClient();
final int count = bundle.getTaskCount();
final int[] positions = bundle.getParameter(BundleParameter.TASK_POSITIONS);
final List> taskList = new ArrayList<>(count);
if (debugEnabled) log.debug("{} : received bundle {}, positions={}", toDebugString(), bundle, StringUtils.buildString(positions));
if (SEQUENTIAL_DESERIALIZATION) lock.lock();
try {
for (int i = 0; i < count; i++) {
final Task> task = (Task>) IOHelper.unwrappedData(socketClient, ser);
if (task != null) {
if ((positions != null) && (i < positions.length)) task.setPosition(positions[i]);
taskList.add(task);
}
}
} finally {
if (SEQUENTIAL_DESERIALIZATION) lock.unlock();
}
// if an exception prevented the node from executing the tasks
final Throwable t = bundle.getParameter(BundleParameter.NODE_EXCEPTION_PARAM);
if (t != null) {
if (debugEnabled) log.debug(toDebugString() + " : server returned exception parameter in the header for job '" + bundle.getName() + "' : " + t);
final Exception e = (t instanceof Exception) ? (Exception) t : new JPPFException(t);
taskList.forEach(task -> task.setThrowable(e));
}
return taskList;
} finally {
Thread.currentThread().setContextClassLoader(ctxCl);
}
}
/**
* Instantiate a SerializationHelper
using the current context class loader.
* @param classLoader the class loader to use to load the serialization helper class.
* @return a SerializationHelper
instance.
* @throws Exception if the serialization helper could not be instantiated.
*/
public SerializationHelper makeHelper(final ClassLoader classLoader) throws Exception {
return makeHelper(classLoader, pool.getClient().getSerializationHelperClassName());
}
/**
* Instantiate a SerializationHelper
using the current context class loader.
* @param classLoader the class loader to use to load the serialization helper class.
* @param helperClassName the fully qualified class name of the serialization helper to use.
* @return a SerializationHelper
instance.
* @throws Exception if the serialization helper could not be instantiated.
*/
public SerializationHelper makeHelper(final ClassLoader classLoader, final String helperClassName) throws Exception {
final ClassLoader[] clArray = { classLoader, Thread.currentThread().getContextClassLoader(), getClass().getClassLoader() };
Class> clazz = null;
for (final ClassLoader cl: clArray) {
try {
if (cl == null) continue;
clazz = Class.forName(helperClassName, true, cl);
break;
} catch (final Exception e) {
if (debugEnabled) log.debug(e.getMessage(), e);
}
if (clazz == null) throw new IllegalStateException("could not load class " + helperClassName + " from any of these class loaders: " + Arrays.asList(clArray));
}
return (SerializationHelper) clazz.newInstance();
}
@Override
public String getName() {
return name;
}
/**
* Create a socket initializer.
* @return an instance of a class implementing SocketInitializer
.
*/
abstract SocketInitializer createSocketInitializer();
/**
* Get the handler for the connection to the task server.
* @return a TaskServerConnectionHandler
instance.
*/
public TaskServerConnectionHandler getTaskServerConnection() {
return taskServerConnection;
}
/**
* Get the JPPF client that owns this connection.
* @return an AbstractGenericClient
instance.
*/
public JPPFClient getClient() {
return pool.getClient();
}
@Override
public String getDriverUuid() {
return pool.getDriverUuid();
}
@Override
public String getConnectionUuid() {
return connectionUuid;
}
@Override
public String getHost() {
return pool.getDriverHost();
}
@Override
public int getPort() {
return pool.getDriverPort();
}
/**
* Get a string representing this connection for debugging purposes.
* @return a string representing this connection.
*/
protected String toDebugString() {
final StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName()).append('[');
sb.append("connectionUuid=").append(connectionUuid);
sb.append(", status=").append(status);
sb.append(']');
return sb.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy