org.perfcake.examples.weaver.Weaver.java Source code

Java tutorial

Introduction

Here is the source code for org.perfcake.examples.weaver.Weaver.java

Source

/*
 * -----------------------------------------------------------------------\
 * PerfCake
 * 
 * Copyright (C) 2010 - 2016 the original author or authors.
 * 
 * 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.perfcake.examples.weaver;

import org.perfcake.examples.weaver.worker.MapConfigurable;
import org.perfcake.examples.weaver.worker.Worker;
import org.perfcake.util.ObjectFactory;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Main class to start the Weaver server according to the provided configuration.
 */
public final class Weaver {

    /**
     * My logger.
     */
    private static final Logger log = LogManager.getLogger(Weaver.class);

    @Parameter(names = { "-t",
            "--threads" }, description = "Number of threads, 0 = automatic based on number of workers")
    private int threads = 0;

    @Parameter(names = { "-s", "--shuffle" }, description = "Shuffle the workers", arity = 1)
    private boolean shuffle = false;

    @Parameter(names = { "-c", "--config" }, description = "Configuration file", required = true)
    private String config = null;

    @Parameter(names = { "--help" }, description = "Prints out command usage", help = true)
    private boolean help = false;

    @Parameter(names = { "-p", "--port" }, description = "Network port to listen on")
    private int port = 8080;

    @Parameter(names = { "-h", "--host" }, description = "Network host to listen on")
    private String host = "localhost";

    /**
     * Workers processing requests.
     */
    private final Queue<Worker> workers = new ConcurrentLinkedQueue<>();

    /**
     * Thread pool executing the workers.
     */
    private ThreadPoolExecutor executor;

    /**
     * Starts the server.
     *
     * @param args
     *       Command line arguments.
     * @throws IOException
     *       When it was not possible to parse the configuration.
     */
    public static void main(String[] args) throws IOException {
        final Weaver weaver = new Weaver();
        final JCommander jCommander = new JCommander(weaver, args);
        jCommander.setProgramName("weaver");

        if (weaver.config == null || weaver.help) {
            jCommander.usage();
            System.exit(1);
        }

        if (!(new File(weaver.config).exists())) {
            System.out.println("The specified configuration file '" + weaver.config + "' does not exists.");
            System.exit(2);
        }

        weaver.init();
        weaver.run();
    }

    /**
     * Initializes the configuration.
     *
     * @throws IOException
     *       When it was not possible to parse the configuration.
     */
    public void init() throws IOException {
        int maxThreads = Files.lines(Paths.get(config)).collect(Collectors.summingInt(this::parseWorker));
        if (threads > maxThreads || threads == 0) {
            if (threads > maxThreads) {
                log.warn("Maximum possible threads is " + maxThreads + ", while you requested " + threads
                        + ". Using " + maxThreads + ".");
            }
            threads = maxThreads;
        }

        if (shuffle) {
            log.info("Shuffling workers...");

            final List<Worker> workersList = workers.stream().collect(Collectors.toList());
            Collections.shuffle(workersList);
            workers.clear();
            workersList.forEach(workers::offer);
        }

        log.info("Creating executor with " + threads + " threads.");

        executor = new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
                new ThreadFactoryBuilder().setDaemon(true).setNameFormat("worker-thread-%d").build());
    }

    /**
     * Parses worker configuration line.
     *
     * @param configLine
     *       The configuration line.
     * @return The number of worker instances created.
     */
    private int parseWorker(final String configLine) {
        if (configLine != null && !configLine.isEmpty() && !configLine.startsWith("#")) {
            final String[] spaceSplit = configLine.split(" ", 2);
            final String[] equalsSplit = spaceSplit[1].split("=", 2);
            final int count = Integer.parseInt(StringUtils.strip(spaceSplit[0], " x"));
            String clazz = StringUtils.strip(equalsSplit[0]);
            clazz = clazz.contains(".") ? clazz : "org.perfcake.examples.weaver.worker." + clazz;
            final String[] propertiesConfig = StringUtils.stripAll(StringUtils.strip(equalsSplit[1]).split(","));
            final Properties properties = new Properties();
            final Properties mapProperties = new Properties();
            for (final String property : propertiesConfig) {
                final String[] keyValue = StringUtils.stripAll(property.split(":", 2));
                if (keyValue[0].matches("worker[0-9][0-9]*_.*")) {
                    mapProperties.setProperty(keyValue[0], keyValue[1]);
                } else {
                    properties.setProperty(keyValue[0], keyValue[1]);
                }
            }

            try {
                log.info("Summoning " + count + " instances of " + clazz + " with properties " + properties
                        + " and map properties " + mapProperties);
                for (int i = 0; i < count; i++) {
                    final Worker worker = (Worker) ObjectFactory.summonInstance(clazz, properties);

                    boolean add = true;
                    if (worker instanceof MapConfigurable) {
                        add = ((MapConfigurable) worker).configure(mapProperties);
                    }

                    if (add) {
                        workers.add(worker);
                    } else {
                        log.warn("Bad configuration. Skipping worker " + clazz);
                    }
                }

                return count;
            } catch (ReflectiveOperationException e) {
                log.error("Unable to parse line '" + configLine + "': ", e);
            }
        }

        return 0;
    }

    /**
     * Starts the server.
     */
    public void run() {
        final WeaverServer server = new WeaverServer(workers, executor, port, host);
        log.info("Started server listening on " + host + ":" + port);
        log.info("Press Ctrl+C to terminate...");
        try {
            System.in.read();
        } catch (IOException ioe) {
            log.error("Unable to read standard input: ", ioe);
        } finally {
            server.close();
        }
    }

    public int getThreads() {
        return threads;
    }

    public void setThreads(final int threads) {
        this.threads = threads;
    }

    public boolean isShuffle() {
        return shuffle;
    }

    public void setShuffle(final boolean shuffle) {
        this.shuffle = shuffle;
    }

    public String getConfig() {
        return config;
    }

    public void setConfig(final String config) {
        this.config = config;
    }

    public int getPort() {
        return port;
    }

    public void setPort(final int port) {
        this.port = port;
    }

    public String getHost() {
        return host;
    }

    public void setHost(final String host) {
        this.host = host;
    }

    public Queue<Worker> getWorkers() {
        return workers;
    }
}