Package de.timefinder.core.springrc

Source Code of de.timefinder.core.springrc.MyApplicationLauncher

/*
* Copyright 2002-2004 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 de.timefinder.core.springrc;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.richclient.application.splash.MonitoringSplashScreen;
import org.springframework.richclient.application.splash.SplashScreen;
import org.springframework.richclient.progress.ProgressMonitor;
import org.springframework.richclient.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.richclient.application.Application;
import org.springframework.richclient.application.ProgressMonitoringBeanFactoryPostProcessor;
import org.springframework.richclient.application.support.DefaultApplicationDescriptor;

import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;

/**
* The main driver for a Spring Rich Client application.
*
* <p>
* This class displays a configurable splash screen and launches a rich client
* {@link Application}. Both the splash screen and the application to be
* launched are expected to be defined as beans, under the names
* {@link #SPLASH_SCREEN_BEAN_ID} and {@link #APPLICATION_BEAN_ID}
* respectively, in one of the application contexts provided to the constructor.
* </p>
*
* <p>
* For quick loading and display of the splash screen while the rest of the
* application is being initialized, constructors are provided that take a
* separate startup context. The startup context will be searched for the
* {@link #SPLASH_SCREEN_BEAN_ID} bean, which will then be displayed before the
* main application context is loaded and the application launched. If no
* startup context is provided or it doesn't contain an appropriately named
* splash screen bean, an attempt will be made to load a splash screen from the
* main application context. This can only happen after the main application
* context has been loaded in its entirety so it is not the recommended approach
* for displaying a splash screen.
* </p>
*
* @author Keith Donald
* @see Application
*/
public class MyApplicationLauncher {

    /**
     * The name of the bean that defines the application's splash screen.
     * {@value}
     */
    public static final String SPLASH_SCREEN_BEAN_ID = "splashScreen";
    public static final String APPLICATION_DESCRIPTOR_ID = "applicationDescriptor";
    /**
     * The name of the bean that defines the {@code Application} that this class
     * will launch.
     * {@value}
     */
    public static final String APPLICATION_BEAN_ID = "application";
    private final Log logger = LogFactory.getLog(getClass());
    private ApplicationContext startupContext;
    private SplashScreen splashScreen;
    private ApplicationContext rootApplicationContext;

    /**
     * Launches the application defined by the Spring application context file
     * at the provided classpath-relative location.
     *
     * @param rootContextPath The classpath-relative location of the application context file.
     *
     * @throws IllegalArgumentException if {@code rootContextPath} is null or empty.
     */
    public MyApplicationLauncher(String rootContextPath) {
        this(new String[]{rootContextPath});
    }

    /**
     * Launches the application defined by the Spring application context files
     * at the provided classpath-relative locations.
     *
     * @param rootContextConfigLocations the classpath-relative locations of the
     * application context files.
     *
     * @throws IllegalArgumentException if {@code rootContextPath} is null or empty.
     */
    public MyApplicationLauncher(String[] rootContextConfigLocations) {
        this(null, rootContextConfigLocations);
    }

    /**
     * Launches the application defined by the Spring application context files
     * at the provided classpath-relative locations. The application context
     * file specified by {@code startupContext} is loaded first to allow for
     * quick loading of the application splash screen. It is recommended that
     * the startup context only contains the bean definition for the splash
     * screen and any other beans that it depends upon. Any beans defined in the
     * startup context will not be available to the main application once
     * launched.
     *
     * @param startupContextPath The classpath-relative location of the startup
     * application context file. May be null or empty.
     * @param rootContextPath The classpath-relative location of the main
     * application context file.
     *
     * @throws IllegalArgumentException if {@code rootContextPath} is null or empty.
     */
    public MyApplicationLauncher(String startupContextPath, String rootContextPath) {
        this(startupContextPath, new String[]{rootContextPath});
    }

    /**
     * Launches the application defined by the Spring application context files
     * at the provided classpath-relative locations. The application context
     * file specified by {@code startupContextPath} is loaded first to allow for
     * quick loading of the application splash screen. It is recommended that
     * the startup context only contains the bean definition for the splash
     * screen and any other beans that it depends upon. Any beans defined in the
     * startup context will not be available to the main application once
     * launched.
     *
     * @param startupContextPath The classpath-relative location of the startup
     * context file. May be null or empty.
     * @param rootContextConfigLocations The classpath-relative locations of the main
     * application context files.
     *
     * @throws IllegalArgumentException if {@code rootContextConfigLocations} is null or empty.
     */
    public MyApplicationLauncher(String startupContextPath, String[] rootContextConfigLocations) {

        Assert.noElementsNull(rootContextConfigLocations, "rootContextConfigLocations");
        Assert.notEmpty(rootContextConfigLocations,
                "One or more root rich client application context paths must be provided");

        this.startupContext = loadStartupContext(startupContextPath);
        if (startupContext != null) {
            displaySplashScreen(startupContext);
        }
        try {
            setRootApplicationContext(loadRootApplicationContext(rootContextConfigLocations, startupContext));
            launchMyRichClient();
        } finally {
            destroySplashScreen();
        }
    }

    /**
     * Launches the application from the pre-loaded application context.
     *
     * @param rootApplicationContext The main application context.
     *
     * @throws IllegalArgumentException if {@code rootApplicationContext} is
     * null.
     */
    public MyApplicationLauncher(ApplicationContext rootApplicationContext) {
        this(null, rootApplicationContext);
    }

    /**
     * Launch the application using a startup context from the given location
     * and a pre-loaded application context.
     *
     * @param startupContextPath the classpath-relative location of the starup
     * application context file. If null or empty, no splash screen will be
     * displayed.
     * @param rootApplicationContext the main application context.
     *
     * @throws IllegalArgumentException if {@code rootApplicationContext} is
     * null.
     *
     */
    public MyApplicationLauncher(String startupContextPath, ApplicationContext rootApplicationContext) {
        this.startupContext = loadStartupContext(startupContextPath);
        if (startupContext != null) {
            displaySplashScreen(startupContext);
        }
        try {
            setRootApplicationContext(rootApplicationContext);
            launchMyRichClient();
        } finally {
            destroySplashScreen();
        }
    }

    /**
     * Returns an application context loaded from the bean definition file at
     * the given classpath-relative location.
     *
     * @param startupContextPath The classpath-relative location of the
     * application context file to be loaded. May be null or empty.
     *
     * @return An application context loaded from the given location, or null if
     * {@code startupContextPath} is null or empty.
     */
    private ApplicationContext loadStartupContext(String startupContextPath) {
        if (StringUtils.hasText(startupContextPath)) {
            if (logger.isInfoEnabled()) {
                logger.info("Loading startup context from classpath resource [" + startupContextPath + "]");
            }

            return new ClassPathXmlApplicationContext(startupContextPath);
        } else {
            return null;
        }
    }

    /**
     * Returns an {@code ApplicationContext}, loaded from the bean definition
     * files at the classpath-relative locations specified by
     * {@code configLocations}.
     *
     * <p>
     * If a splash screen has been created, the application context will be
     * loaded with a bean post processor that will notify the splash screen's
     * progress monitor as each bean is initialized.
     * </p>
     *
     * @param configLocations The classpath-relative locations of the files from
     * which the application context will be loaded.
     *
     * @return The main application context, never null.
     */
    private ApplicationContext loadRootApplicationContext(String[] configLocations, MessageSource messageSource) {
        final ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocations, false);

        if (splashScreen instanceof MonitoringSplashScreen) {
            final ProgressMonitor tracker = ((MonitoringSplashScreen) splashScreen).getProgressMonitor();

            applicationContext.addBeanFactoryPostProcessor(
                    new ProgressMonitoringBeanFactoryPostProcessor(tracker, messageSource));

        }
       
        applicationContext.refresh();

        return applicationContext;
    }

    private void setRootApplicationContext(ApplicationContext context) {
        Assert.notNull(context, "The root rich client application context is required");
        this.rootApplicationContext = context;
    }

    /**
     * Launches the rich client application. If no startup context has so far
     * been provided, the main application context will be searched for a splash
     * screen to display. The main application context will then be searched for
     * the {@link Application} to be launched, using the bean name
     * {@link #APPLICATION_BEAN_ID}. If the application is found, it will be
     * started.
     *
     */
    private void launchMyRichClient() {

        if (startupContext == null) {
            displaySplashScreen(rootApplicationContext);
        }

        final Application application;

        try {
            application = (Application) rootApplicationContext.getBean(APPLICATION_BEAN_ID, Application.class);
        } catch (NoSuchBeanDefinitionException e) {
            throw new IllegalArgumentException(
                    "A single bean definition with id " + APPLICATION_BEAN_ID + ", of type " + Application.class.getName() + " must be defined in the main application context",
                    e);
        }

        try {
            // To avoid deadlocks when events fire during initialization of some swing components
            // Possible to do: in theory not a single Swing component should be created (=modified) in the launcher thread...
            SwingUtilities.invokeAndWait(new Runnable() {

                public void run() {
                    application.start();
                }
            });
        } catch (InterruptedException e) {
            logger.warn("Application start interrupted", e);
        } catch (InvocationTargetException e) {
            Throwable cause = e.getCause();
            throw new IllegalStateException("Application start thrown an exception: " + cause.getMessage(), cause);
        }

        logger.debug("Launcher thread exiting...");

    }

    public DefaultApplicationDescriptor getApplicationDescriptor() {
        rootApplicationContext.getBean(APPLICATION_DESCRIPTOR_ID, DefaultApplicationDescriptor.class);
        return (DefaultApplicationDescriptor) rootApplicationContext.getBean(APPLICATION_DESCRIPTOR_ID, DefaultApplicationDescriptor.class);
    }

    /**
     * Searches the given bean factory for a {@link SplashScreen} defined with
     * the bean name {@link #SPLASH_SCREEN_BEAN_ID} and displays it, if found.
     *
     * @param beanFactory The bean factory that is expected to contain the
     * splash screen bean definition. Must not be null.
     *
     * @throws NullPointerException if {@code beanFactory} is null.
     * @throws BeanNotOfRequiredTypeException if the bean found under the splash
     * screen bean name is not a {@link SplashScreen}.
     *
     */
    private void displaySplashScreen(BeanFactory beanFactory) {
        if (beanFactory.containsBean(SPLASH_SCREEN_BEAN_ID)) {
            this.splashScreen = (SplashScreen) beanFactory.getBean(SPLASH_SCREEN_BEAN_ID, SplashScreen.class);
            logger.debug("Displaying application splash screen...");
            try {
                SwingUtilities.invokeAndWait(new Runnable() {

                    public void run() {
                        MyApplicationLauncher.this.splashScreen.splash();
                    }
                });
            } catch (Exception e) {
                throw new RuntimeException("EDT threading issue while showing splash screen", e);
            }
        } else {
            logger.info("No splash screen bean found to display. Continuing...");
        }
    }

    private void destroySplashScreen() {
        if (splashScreen != null) {
            logger.debug("Closing splash screen...");

            SwingUtilities.invokeLater(new Runnable() {

                public void run() {
                    splashScreen.dispose();
                    splashScreen = null;
                }
            });
        }
    }
}
TOP

Related Classes of de.timefinder.core.springrc.MyApplicationLauncher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.