All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jppf.client.AbstractGenericClient Maven / Gradle / Ivy

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 java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

import javax.sql.DataSource;

import org.jppf.client.balancer.*;
import org.jppf.client.balancer.queue.JPPFPriorityQueue;
import org.jppf.client.event.*;
import org.jppf.discovery.*;
import org.jppf.load.balancer.persistence.*;
import org.jppf.load.balancer.spi.JPPFBundlerFactory;
import org.jppf.node.policy.*;
import org.jppf.persistence.JPPFDatasourceFactory;
import org.jppf.queue.*;
import org.jppf.startup.JPPFClientStartupSPI;
import org.jppf.utils.*;
import org.jppf.utils.concurrent.*;
import org.jppf.utils.configuration.*;
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; /** * Performs server discovery. */ private JPPFMulticastReceiverThread receiverThread; /** * 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<>(); /** * Supports built-in and custom discovery mechanisms. */ @SuppressWarnings({ "unchecked", "rawtypes" }) final DriverDiscoveryHandler discoveryHandler = new DriverDiscoveryHandler(ClientDriverDiscovery.class); /** * Listens to new connection pool notifications from {@link DriverDiscovery} instances. */ private ClientDriverDiscoveryListener discoveryListener; /** * The factory that creates load-balancer instances. */ JPPFBundlerFactory bundlerFactory; /** * Manages the persisted states of the load-balancers. */ LoadBalancerPersistenceManager loadBalancerPersistenceManager; /** * The default server-side job sla execution policy. */ final AtomicReference defaultPolicy = new AtomicReference<>(null); /** * The default client-side job sla execution policy. */ final AtomicReference defaultClientPolicy = new AtomicReference<>(null); /** * Whether the client should print connection events to stdout. */ private boolean sysoutEnabled; /** * 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) { if (listener != null) addConnectionPoolListener(listener); } } discoveryListener = new ClientDriverDiscoveryListener(this); init(configuration); } /** * Initialize this client with the specified configuration. * @param configuration the configuration to use with this client. */ protected void init(final TypedProperties configuration) { if (debugEnabled) log.debug("initializing client"); closed.set(false); resetting.set(false); this.config = initConfig(configuration); try { final Map result = JPPFDatasourceFactory.getInstance().createDataSources(config); log.info("created client-side datasources: {}", result.keySet()); } catch (final Exception e) { log.error(e.getMessage(), e); } this.bundlerFactory = new JPPFBundlerFactory(JPPFBundlerFactory.Defaults.CLIENT, config); this.loadBalancerPersistenceManager = new LoadBalancerPersistenceManager(this.bundlerFactory); try { HookFactory.registerSPIMultipleHook(JPPFClientStartupSPI.class, null, null).invoke("run"); } catch (final Exception e) { log.error(e.getMessage(), e); } final int coreThreads = Runtime.getRuntime().availableProcessors(); final BlockingQueue queue = new SynchronousQueue<>(); executor = new ThreadPoolExecutor(coreThreads, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, queue, new JPPFThreadFactory("JPPF Client")); executor.allowCoreThreadTimeOut(true); if (jobManager == null) jobManager = createJobManager(); defaultPolicy.set(retrieveDefaultPolicy(JPPFProperties.JOB_SLA_DEFAULT_POLICY)); defaultClientPolicy.set(retrieveDefaultPolicy(JPPFProperties.JOB_CLIENT_SLA_DEFAULT_POLICY)); ThreadUtils.startThread(() -> initPools(config), "InitPools"); } /** * Retrieve and parse an execution àpolicy from the specified configuration property. * @param prop the configuration propoerty from which to retrieve the execution policy. * @return the retrieved policy, or {@code null} if no policy was specified or if the parsing failed. */ private ExecutionPolicy retrieveDefaultPolicy(final JPPFProperty prop) { final String policyXML = PolicyUtils.resolvePolicy(config, prop.getName()); if (policyXML != null) { try { return PolicyParser.parsePolicy(policyXML.trim()); } catch (final Exception e) { log.warn("failed to parse execution policy for {}, with content = {}\n{}", prop.getName(), policyXML, ExceptionUtils.getStackTrace(e)); } } return null; } /** * Initialize this client's configuration. * @param configuration an object holding the JPPF configuration. * @return {@link TypedProperties} instance holding JPPF configuration. Never {@code null}. */ TypedProperties initConfig(final Object configuration) { if (configuration instanceof TypedProperties) return (TypedProperties) configuration; return JPPFConfiguration.getProperties(); } @Override protected void initPools(final TypedProperties config) { if (debugEnabled) log.debug("initializing connections"); sysoutEnabled = config.get(JPPFProperties.CLIENT_SYSOUT_ENABLED); if (config.get(JPPFProperties.LOCAL_EXECUTION_ENABLED)) { if (sysoutEnabled) System.out.println("local execution enabled"); setLocalExecutionEnabled(true); } discoveryHandler.register(discoveryListener.open()).start(); if (config.get(JPPFProperties.REMOTE_EXECUTION_ENABLED)) addDriverDiscovery(new ClientConfigDriverDiscovery(config)); } /** * Called when a new connection pool is read from the configuration. * @param info the information required for the connection to connect to the driver. */ void newConnectionPool(final ClientConnectionPoolInfo info) { if (debugEnabled) log.debug("new connection pool: {}", info.getName()); final int size = info.getPoolSize() > 0 ? info.getPoolSize() : 1; final Runnable r = new Runnable() { @Override public void run() { final JPPFConnectionPool pool = new JPPFConnectionPool((JPPFClient) AbstractGenericClient.this, poolSequence.incrementAndGet(), info); pool.setDriverPort(info.getPort()); synchronized(pools) { pools.putValue(info.getPriority(), pool); } HostIP hostIP = new HostIP(info.getHost(), info.getHost()); if (getConfig().get(JPPFProperties.RESOLVE_ADDRESSES)) hostIP = NetworkUtils.getHostIP(info.getHost()); if (debugEnabled) log.debug("'{}' was resolved into '{}'", info.getHost(), hostIP.hostName()); pool.setDriverHostIP(hostIP); fireConnectionPoolAdded(pool); for (int i=1; i<=size; i++) { if (isClosed()) return; submitNewConnection(pool); } pool.initHeartbeat(); } }; executor.execute(r); } /** * Called to submit the initialization of a new connection. * @param pool thez connection pool to which the connection belongs. */ void submitNewConnection(final JPPFConnectionPool pool) { final JPPFClientConnectionImpl c = createConnection(pool.getName() + "-" + pool.nextSequence(), pool); newConnection(c); } /** * Create a new driver connection based on the specified parameters. * @param name the name of the connection. * @param pool id of the connection pool the connection belongs to. * @return an instance of a subclass of {@link JPPFClientConnectionImpl}. */ abstract JPPFClientConnectionImpl createConnection(String name, final JPPFConnectionPool pool); @Override void newConnection(final JPPFClientConnectionImpl c) { if (isClosed()) return; log.info("connection [" + c.getName() + "] created"); c.addClientConnectionStatusListener(this); c.submitInitialization(); fireConnectionAdded(c); if (debugEnabled) log.debug("end of of newConnection({})", c.getName()); } /** * Invoked when the status of a connection has changed to {@link JPPFClientConnectionStatus#FAILED}. * @param connection the connection that failed. */ @Override void connectionFailed(final JPPFClientConnection connection) { if (debugEnabled) log.debug("Connection [{}] {}", connection.getName(), connection.getStatus()); final JPPFConnectionPool pool = connection.getConnectionPool(); connection.close(); final boolean poolRemoved = removeClientConnection(connection); fireConnectionRemoved(connection); if (poolRemoved) { fireConnectionPoolRemoved(pool); if (receiverThread != null) receiverThread.removeConnectionInformation(connection.getDriverUuid()); final ClientConnectionPoolInfo info = pool.getDiscoveryInfo(); if (info != null) { final boolean b = discoveryListener.onPoolRemoved(info); if (debugEnabled) log.debug("removal of {} = {}", info, b); } } } @Override public void close() { close(false); } /** * Close this client. * @param reset if {@code true}, then this client is left in a state where it can be reopened. */ void close(final boolean reset) { try { log.info("closing JPPF client with uuid={}, PID={}", getUuid(), SystemUtils.getPID()); if (!closed.compareAndSet(false, true)) return; if (debugEnabled) log.debug("closing discovery handler"); discoveryListener.close(); discoveryHandler.stop(); 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(); } catch(final Throwable t) { log.error(t.getMessage(), t); } } /** * Determine whether local execution is enabled on this client. * @return {@code true} if local execution is enabled, {@code false} otherwise. */ public boolean isLocalExecutionEnabled() { final JobManager jobManager = getJobManager(); return (jobManager != null) && jobManager.isLocalExecutionEnabled(); } /** * Specify whether local execution is enabled on this client. * @param localExecutionEnabled {@code true} to enable local execution, {@code false} otherwise */ public void setLocalExecutionEnabled(final boolean localExecutionEnabled) { final 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() { final JobManager jobManager = getJobManager(); return (jobManager != null) && jobManager.hasAvailableConnection(); } /** * @exclude */ @Override public void statusChanged(final ClientConnectionStatusEvent event) { super.statusChanged(event); final JobManager jobManager = getJobManager(); if(jobManager != null) { final ClientConnectionStatusListener listener = jobManager.getClientConnectionStatusListener(); if (listener != null) listener.statusChanged(event); } } /** * 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 {@link 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 {@code true} when cancel was successful, {@code 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 */ protected void fireQueueEvent(final QueueEvent qEvent, final boolean jobAdded) { final ClientQueueEvent event = new ClientQueueEvent((JPPFClient) this, qEvent.getJob().getJob(), (JPPFPriorityQueue) qEvent.getQueue()); if (jobAdded) { for (final ClientQueueListener listener: queueListeners) listener.jobAdded(event); } else { for (final ClientQueueListener listener: queueListeners) listener.jobRemoved(event); } } /** * @exclude */ @Override public void bundleAdded(final QueueEvent event) { fireQueueEvent(event, true); } /** * @exclude */ @Override public void bundleRemoved(final QueueEvent event) { fireQueueEvent(event, false); } /** * Get the object that manages the persisted states of the load-balancers. * @return an instance of {@link LoadBalancerPersistenceManagement}. * @since 6.0 */ public LoadBalancerPersistenceManagement getLoadBalancerPersistenceManagement() { return loadBalancerPersistenceManager; } /** * Add a custom driver discovery mechanism to those already registered, if any. * @param discovery the driver discovery to add. */ public void addDriverDiscovery(final ClientDriverDiscovery discovery) { discoveryHandler.addDiscovery(discovery); } /** * Determine whether the client should print connection events to stdout. * @return {@code true} if the client should print ot the console, {@code false} otherwise. */ public boolean isSysoutEnabled() { return sysoutEnabled; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy