alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (MultiBackgroundInitializer.java)

This example Java source code file (MultiBackgroundInitializer.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

backgroundinitializer, child, concurrentexception, executorservice, hashmap, illegalargumentexception, illegalstateexception, map, multibackgroundinitializerresults, name, nosuchelementexception, object, override, string, threading, threads, util

The MultiBackgroundInitializer.java Java example source code

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.commons.lang3.concurrent;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ExecutorService;

/**
 * <p>
 * A specialized {@link BackgroundInitializer} implementation that can deal with
 * multiple background initialization tasks.
 * </p>
 * <p>
 * This class has a similar purpose as {@link BackgroundInitializer}. However,
 * it is not limited to a single background initialization task. Rather it
 * manages an arbitrary number of {@code BackgroundInitializer} objects,
 * executes them, and waits until they are completely initialized. This is
 * useful for applications that have to perform multiple initialization tasks
 * that can run in parallel (i.e. that do not depend on each other). This class
 * takes care about the management of an {@code ExecutorService} and shares it
 * with the {@code BackgroundInitializer} objects it is responsible for; so the
 * using application need not bother with these details.
 * </p>
 * <p>
 * The typical usage scenario for {@code MultiBackgroundInitializer} is as
 * follows:
 * </p>
 * <ul>
 * <li>Create a new instance of the class. Optionally pass in a pre-configured
 * {@code ExecutorService}. Alternatively {@code MultiBackgroundInitializer} can
 * create a temporary {@code ExecutorService} and delete it after initialization
 * is complete.</li>
 * <li>Create specialized {@link BackgroundInitializer} objects for the
 * initialization tasks to be performed and add them to the {@code
 * MultiBackgroundInitializer} using the
 * {@link #addInitializer(String, BackgroundInitializer)} method.</li>
 * <li>After all initializers have been added, call the {@link #start()} method.
 * </li>
 * <li>When access to the result objects produced by the {@code
 * BackgroundInitializer} objects is needed call the {@link #get()} method. The
 * object returned here provides access to all result objects created during
 * initialization. It also stores information about exceptions that have
 * occurred.</li>
 * </ul>
 * <p>
 * {@code MultiBackgroundInitializer} starts a special controller task that
 * starts all {@code BackgroundInitializer} objects added to the instance.
 * Before the an initializer is started it is checked whether this initializer
 * already has an {@code ExecutorService} set. If this is the case, this {@code
 * ExecutorService} is used for running the background task. Otherwise the
 * current {@code ExecutorService} of this {@code MultiBackgroundInitializer} is
 * shared with the initializer.
 * </p>
 * <p>
 * The easiest way of using this class is to let it deal with the management of
 * an {@code ExecutorService} itself: If no external {@code ExecutorService} is
 * provided, the class creates a temporary {@code ExecutorService} (that is
 * capable of executing all background tasks in parallel) and destroys it at the
 * end of background processing.
 * </p>
 * <p>
 * Alternatively an external {@code ExecutorService} can be provided - either at
 * construction time or later by calling the
 * {@link #setExternalExecutor(ExecutorService)} method. In this case all
 * background tasks are scheduled at this external {@code ExecutorService}.
 * <strong>Important note: When using an external {@code
 * ExecutorService} be sure that the number of threads managed by the service is
 * large enough. Otherwise a deadlock can happen! This is the case in the
 * following scenario: {@code MultiBackgroundInitializer} starts a task that
 * starts all registered {@code BackgroundInitializer} objects and waits for
 * their completion. If for instance a single threaded {@code ExecutorService}
 * is used, none of the background tasks can be executed, and the task created
 * by {@code MultiBackgroundInitializer} waits forever.
 * </p>
 *
 * @since 3.0
 */
public class MultiBackgroundInitializer
        extends
        BackgroundInitializer<MultiBackgroundInitializer.MultiBackgroundInitializerResults> {
    /** A map with the child initializers. */
    private final Map<String, BackgroundInitializer childInitializers =
        new HashMap<String, BackgroundInitializer();

    /**
     * Creates a new instance of {@code MultiBackgroundInitializer}.
     */
    public MultiBackgroundInitializer() {
        super();
    }

    /**
     * Creates a new instance of {@code MultiBackgroundInitializer} and
     * initializes it with the given external {@code ExecutorService}.
     *
     * @param exec the {@code ExecutorService} for executing the background
     * tasks
     */
    public MultiBackgroundInitializer(final ExecutorService exec) {
        super(exec);
    }

    /**
     * Adds a new {@code BackgroundInitializer} to this object. When this
     * {@code MultiBackgroundInitializer} is started, the given initializer will
     * be processed. This method must not be called after {@link #start()} has
     * been invoked.
     *
     * @param name the name of the initializer (must not be <b>null)
     * @param init the {@code BackgroundInitializer} to add (must not be
     * <b>null)
     * @throws IllegalArgumentException if a required parameter is missing
     * @throws IllegalStateException if {@code start()} has already been called
     */
    public void addInitializer(final String name, final BackgroundInitializer<?> init) {
        if (name == null) {
            throw new IllegalArgumentException(
                    "Name of child initializer must not be null!");
        }
        if (init == null) {
            throw new IllegalArgumentException(
                    "Child initializer must not be null!");
        }

        synchronized (this) {
            if (isStarted()) {
                throw new IllegalStateException(
                        "addInitializer() must not be called after start()!");
            }
            childInitializers.put(name, init);
        }
    }

    /**
     * Returns the number of tasks needed for executing all child {@code
     * BackgroundInitializer} objects in parallel. This implementation sums up
     * the required tasks for all child initializers (which is necessary if one
     * of the child initializers is itself a {@code MultiBackgroundInitializer}
     * ). Then it adds 1 for the control task that waits for the completion of
     * the children.
     *
     * @return the number of tasks required for background processing
     */
    @Override
    protected int getTaskCount() {
        int result = 1;

        for (final BackgroundInitializer<?> bi : childInitializers.values()) {
            result += bi.getTaskCount();
        }

        return result;
    }

    /**
     * Creates the results object. This implementation starts all child {@code
     * BackgroundInitializer} objects. Then it collects their results and
     * creates a {@code MultiBackgroundInitializerResults} object with this
     * data. If a child initializer throws a checked exceptions, it is added to
     * the results object. Unchecked exceptions are propagated.
     *
     * @return the results object
     * @throws Exception if an error occurs
     */
    @Override
    protected MultiBackgroundInitializerResults initialize() throws Exception {
        Map<String, BackgroundInitializer inits;
        synchronized (this) {
            // create a snapshot to operate on
            inits = new HashMap<String, BackgroundInitializer(
                    childInitializers);
        }

        // start the child initializers
        final ExecutorService exec = getActiveExecutor();
        for (final BackgroundInitializer<?> bi : inits.values()) {
            if (bi.getExternalExecutor() == null) {
                // share the executor service if necessary
                bi.setExternalExecutor(exec);
            }
            bi.start();
        }

        // collect the results
        final Map<String, Object> results = new HashMap();
        final Map<String, ConcurrentException> excepts = new HashMap();
        for (final Map.Entry<String, BackgroundInitializer e : inits.entrySet()) {
            try {
                results.put(e.getKey(), e.getValue().get());
            } catch (final ConcurrentException cex) {
                excepts.put(e.getKey(), cex);
            }
        }

        return new MultiBackgroundInitializerResults(inits, results, excepts);
    }

    /**
     * A data class for storing the results of the background initialization
     * performed by {@code MultiBackgroundInitializer}. Objects of this inner
     * class are returned by {@link MultiBackgroundInitializer#initialize()}.
     * They allow access to all result objects produced by the
     * {@link BackgroundInitializer} objects managed by the owning instance. It
     * is also possible to retrieve status information about single
     * {@link BackgroundInitializer}s, i.e. whether they completed normally or
     * caused an exception.
     */
    public static class MultiBackgroundInitializerResults {
        /** A map with the child initializers. */
        private final Map<String, BackgroundInitializer initializers;

        /** A map with the result objects. */
        private final Map<String, Object> resultObjects;

        /** A map with the exceptions. */
        private final Map<String, ConcurrentException> exceptions;

        /**
         * Creates a new instance of {@code MultiBackgroundInitializerResults}
         * and initializes it with maps for the {@code BackgroundInitializer}
         * objects, their result objects and the exceptions thrown by them.
         *
         * @param inits the {@code BackgroundInitializer} objects
         * @param results the result objects
         * @param excepts the exceptions
         */
        private MultiBackgroundInitializerResults(
                final Map<String, BackgroundInitializer inits,
                final Map<String, Object> results,
                final Map<String, ConcurrentException> excepts) {
            initializers = inits;
            resultObjects = results;
            exceptions = excepts;
        }

        /**
         * Returns the {@code BackgroundInitializer} with the given name. If the
         * name cannot be resolved, an exception is thrown.
         *
         * @param name the name of the {@code BackgroundInitializer}
         * @return the {@code BackgroundInitializer} with this name
         * @throws NoSuchElementException if the name cannot be resolved
         */
        public BackgroundInitializer<?> getInitializer(final String name) {
            return checkName(name);
        }

        /**
         * Returns the result object produced by the {@code
         * BackgroundInitializer} with the given name. This is the object
         * returned by the initializer's {@code initialize()} method. If this
         * {@code BackgroundInitializer} caused an exception, <b>null is
         * returned. If the name cannot be resolved, an exception is thrown.
         *
         * @param name the name of the {@code BackgroundInitializer}
         * @return the result object produced by this {@code
         * BackgroundInitializer}
         * @throws NoSuchElementException if the name cannot be resolved
         */
        public Object getResultObject(final String name) {
            checkName(name);
            return resultObjects.get(name);
        }

        /**
         * Returns a flag whether the {@code BackgroundInitializer} with the
         * given name caused an exception.
         *
         * @param name the name of the {@code BackgroundInitializer}
         * @return a flag whether this initializer caused an exception
         * @throws NoSuchElementException if the name cannot be resolved
         */
        public boolean isException(final String name) {
            checkName(name);
            return exceptions.containsKey(name);
        }

        /**
         * Returns the {@code ConcurrentException} object that was thrown by the
         * {@code BackgroundInitializer} with the given name. If this
         * initializer did not throw an exception, the return value is
         * <b>null. If the name cannot be resolved, an exception is thrown.
         *
         * @param name the name of the {@code BackgroundInitializer}
         * @return the exception thrown by this initializer
         * @throws NoSuchElementException if the name cannot be resolved
         */
        public ConcurrentException getException(final String name) {
            checkName(name);
            return exceptions.get(name);
        }

        /**
         * Returns a set with the names of all {@code BackgroundInitializer}
         * objects managed by the {@code MultiBackgroundInitializer}.
         *
         * @return an (unmodifiable) set with the names of the managed {@code
         * BackgroundInitializer} objects
         */
        public Set<String> initializerNames() {
            return Collections.unmodifiableSet(initializers.keySet());
        }

        /**
         * Returns a flag whether the whole initialization was successful. This
         * is the case if no child initializer has thrown an exception.
         *
         * @return a flag whether the initialization was successful
         */
        public boolean isSuccessful() {
            return exceptions.isEmpty();
        }

        /**
         * Checks whether an initializer with the given name exists. If not,
         * throws an exception. If it exists, the associated child initializer
         * is returned.
         *
         * @param name the name to check
         * @return the initializer with this name
         * @throws NoSuchElementException if the name is unknown
         */
        private BackgroundInitializer<?> checkName(final String name) {
            final BackgroundInitializer<?> init = initializers.get(name);
            if (init == null) {
                throw new NoSuchElementException(
                        "No child initializer with name " + name);
            }

            return init;
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java MultiBackgroundInitializer.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.