* JPPF.
 * Copyright (C) 2005-2019 JPPF Team.
 * 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
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * See the License for the specific language governing permissions and
 * limitations under the License.

package org.jppf.example.extendedclassloading.client;

import java.util.List;

import org.jppf.client.*;
import org.jppf.example.extendedclassloading.*;
import org.jppf.node.protocol.Task;
import org.jppf.utils.ExceptionUtils;
import org.slf4j.*;

 * <p>This client application maintains a repository of Java libraries that are automatically
 * downloaded by the nodes. Each node also maintains its own local repository.
 * The updates are computed by scanning the folder where the libs are stored, and comparing
 * the scan results with the repository's index file to determine which libraries were added,
 * updated or removed. This information is then communicated to the node via the metadata in
 * a JPPF job.
 * <p>This enables the management of the nodes remote repositories by simply removing files from,
 * or dropping files into the folder where the libraries are located.
 * @author Laurent Cohen
public class MyRunner {
   * Logger for this class.
  private static Logger log = LoggerFactory.getLogger(MyRunner.class);
   * Location where the downloaded libraries are stored on the client's file system.
  public static final String CLIENT_LIB_DIR = "dynamicLibs";
   * A number assigned to each job as part of its name.
  private static int jobCount = 1;

   * Entry point for the demo.
   * @param args there can be one optional argument specifying a file pattern as a wildcard-based expression,
   * which will be used as a filter to delete the libraries in the nodes' repositories. Additional arguments are ignored.
  public static void main(final String[] args) {
    try (final JPPFClient client = new JPPFClient()) {
      // create the classpath specified with the '-cp' command-line argument
      final ClassPath classpath = ClassPathHelper.createClassPathFromArguments(CLIENT_LIB_DIR, args);
      if ((classpath != null) && (classpath.size() > 0)) output("found dynamic libraries: " + classpath);
      else output("found no dynamic library");

      // create the jobs
      // setting a non-null classpath on the first job will cause the node to update the current task class loader
      final JPPFJob job1 = createJob(classpath, new MyTask1());
      // setting a non-null classpath on the second job will cause the node to create a new task class loader
      final JPPFJob job2 = createJob(classpath, new MyTask2());

      // if a file pattern is provided, add a corresponding filter to the metadata of the first job
      final RepositoryFilter filter = ClassPathHelper.getFilterFromArguments(args);
      if (filter != null) {
        job1.getMetadata().setParameter(ClassPathHelper.REPOSITORY_DELETE_FILTER, filter);
        output("requesting deletion of files matching " + filter);

      // execute the jobs and process their results
      executeJob(client, job1);
      executeJob(client, job2);
    } catch (final Exception e) {

   * Create a job with the specified classpath and tasks.
   * @param classpath the classpath to set onto the job's metadata
   * @param tasks the tasks to add to the job.
   * @return the newly created job.
   * @throws Exception if any error occurs while creating the job.
  private static JPPFJob createJob(final ClassPath classpath, final Task<?>...tasks) throws Exception {
    final JPPFJob job = new JPPFJob();
    job.setName("Extended Class Loading " + jobCount++);

    // update the job metadata to specifiy which libraries are needed for the job
    if ((classpath != null) && (classpath.size() > 0)) {
      job.getMetadata().setParameter(ClassPathHelper.JOB_CLASSPATH, classpath);

    // add the tasks to the job
    int taskNumber = 1;
    final String prefix = job.getName() + ":task ";
    for (final Task<?> task: tasks) job.add(task).setId(prefix + taskNumber++);
    return job;

   * Execute the specified job on the grid and process its results.
   * @param client the JPPF client to which the job is submitted.
   * @param job the job to execute.
   * @throws Exception if any error occurs while creating the job.
  private static void executeJob(final JPPFClient client, final JPPFJob job) throws Exception {
    // submit the job to the grid
    final List<Task<?>> results = client.submit(job);

    // process the results
    output("*** results for job '" + job.getName() + "'");
    for (final Task<?> task: results) {
      final String prefix = "task " + task.getId() + " ";
      if (task.getThrowable() != null) {
        // if an error occurred, show the exception stack trace
        output(prefix + "got exception: " + ExceptionUtils.getStackTrace(task.getThrowable()));
      } else {
        // otherwise show the task result
        output(prefix + "result: " + task.getResult());

   * Print a message to the console and/or log file.
   * @param message the message to print.
  public static void output(final String message) {
    // comment out this line to remove messages from the console
    // comment out this line to remove messages from the log file;