001    /*
002     * Copyright 2002-2004 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.application;
017    
018    import java.awt.Image;
019    import java.util.Observable;
020    import java.util.Observer;
021    
022    import org.springframework.beans.factory.InitializingBean;
023    import org.springframework.context.ApplicationContext;
024    import org.springframework.context.ApplicationContextAware;
025    import org.springframework.context.ConfigurableApplicationContext;
026    import org.springframework.richclient.application.config.ApplicationLifecycleAdvisor;
027    import org.springframework.richclient.application.config.DefaultApplicationLifecycleAdvisor;
028    import org.springframework.richclient.application.support.DefaultApplicationServices;
029    import org.springframework.richclient.application.support.MultiViewPageDescriptor;
030    import org.springframework.richclient.image.ImageSource;
031    import org.springframework.richclient.image.NoSuchImageResourceException;
032    import org.springframework.util.Assert;
033    import org.springframework.util.StringUtils;
034    
035    /**
036     * A singleton workbench or shell of a rich client application.
037     * <p>
038     * The application provides a point of reference and context for an entire application. It
039     * provides an interface to open application windows.
040     * 
041     * @author Keith Donald
042     */
043    public class Application implements InitializingBean, ApplicationContextAware {
044    
045        private static final String DEFAULT_APPLICATION_IMAGE_KEY = "applicationInfo.image";
046    
047        private static Application SOLE_INSTANCE;
048    
049        private ApplicationContext applicationContext;
050    
051        private ApplicationDescriptor descriptor;
052    
053        private ApplicationLifecycleAdvisor lifecycleAdvisor;
054    
055        private WindowManager windowManager;
056    
057        private boolean forceShutdown = false;
058    
059        /**
060         * Load the single application instance.
061         * 
062         * @param instance The application
063         */
064        public static void load( Application instance ) {
065            SOLE_INSTANCE = instance;
066        }
067    
068        /**
069         * Return the single application instance.
070         * 
071         * @return The application
072         */
073        public static Application instance() {
074            Assert
075                    .state(isLoaded(),
076                            "The global rich client application instance has not yet been initialized; it must be created and loaded first.");
077            return SOLE_INSTANCE;
078        }
079    
080        public static boolean isLoaded() {
081            return SOLE_INSTANCE != null;
082        }
083    
084        /**
085         * Return a global service locator for application services.
086         * 
087         * @return The application services locator.
088         */
089        public static ApplicationServices services() {
090            if (!ApplicationServicesLocator.isLoaded()) {
091                    ApplicationServicesLocator.load(new ApplicationServicesLocator(new DefaultApplicationServices()));
092            }
093            return ApplicationServicesLocator.services();
094        }
095        
096        public Application() {
097            this(new DefaultApplicationLifecycleAdvisor());
098        }
099    
100        public Application( ApplicationLifecycleAdvisor advisor ) {
101            this(null, advisor);
102        }
103    
104        public Application( ApplicationDescriptor descriptor, ApplicationLifecycleAdvisor advisor ) {
105            setDescriptor(descriptor);
106            setLifecycleAdvisor(advisor);
107            this.windowManager = new WindowManager();
108            this.windowManager.addObserver(new CloseApplicationObserver());
109            Assert.state(!isLoaded(), "Only one instance of a Spring Rich Application allowed per VM.");
110            load(this);
111        }
112    
113        public void setDescriptor( ApplicationDescriptor descriptor ) {
114            this.descriptor = descriptor;
115        }
116    
117        public ApplicationDescriptor getDescriptor() {
118            return descriptor;
119        }
120    
121        private void setLifecycleAdvisor( ApplicationLifecycleAdvisor advisor ) {
122            this.lifecycleAdvisor = advisor;
123        }
124    
125        public void setApplicationContext( ApplicationContext context ) {
126            applicationContext = context;
127        }
128    
129        public ApplicationContext getApplicationContext() {
130            return applicationContext;
131        }
132    
133        public void afterPropertiesSet() throws Exception {
134            Assert.notNull(this.lifecycleAdvisor,
135                    "The application advisor is required, for processing of application lifecycle events");
136            getLifecycleAdvisor().setApplication(this);
137            getLifecycleAdvisor().onPreInitialize(this);
138        }
139    
140        public ApplicationLifecycleAdvisor getLifecycleAdvisor() {
141            return lifecycleAdvisor;
142        }
143    
144        public String getName() {
145            if( descriptor != null && StringUtils.hasText(descriptor.getDisplayName()) )
146                return descriptor.getDisplayName();
147    
148            return "Spring Rich Client Application";
149        }
150    
151        public Image getImage() {
152            if( descriptor != null && descriptor.getImage() != null )
153                return descriptor.getImage();
154    
155            try {
156                    ImageSource isrc = (ImageSource) services().getService(ImageSource.class);
157                    return isrc.getImage(DEFAULT_APPLICATION_IMAGE_KEY);
158            }
159            catch (NoSuchImageResourceException e) {
160                    return null;
161            }
162        }
163    
164        public void openWindow( String pageDescriptorId ) {
165            ApplicationWindow newWindow = initWindow(createNewWindow());
166            if ( pageDescriptorId == null ) {
167                    ApplicationPageFactory pageFactory
168                            = (ApplicationPageFactory)services().getService(ApplicationPageFactory.class);
169                    newWindow.showPage(pageFactory.createApplicationPage(newWindow, new MultiViewPageDescriptor()));
170            }
171            else {
172                    newWindow.showPage(pageDescriptorId);
173            }
174        }
175    
176        private ApplicationWindow initWindow( ApplicationWindow window ) {
177            windowManager.add(window);
178            return window;
179        }
180    
181        protected ApplicationWindow createNewWindow() {
182            ApplicationWindowFactory windowFactory = (ApplicationWindowFactory) services().getService(
183                    ApplicationWindowFactory.class);
184            return windowFactory.createApplicationWindow();
185        }
186    
187        public WindowManager getWindowManager() {
188            return windowManager;
189        }
190    
191        /**
192         * ActiveWindow is tracked by windowManager. When a window gains focus, the manager
193         * will receive this window as the active one.
194         * 
195         * @return the activeWindow.
196         */
197        public ApplicationWindow getActiveWindow() {
198            return windowManager.getActiveWindow();
199        }
200    
201        /**
202         * @return true if the application is in a force shutdown mode.
203         */
204        public boolean isForceShutdown()
205        {
206            return forceShutdown;
207        }
208    
209        public void close() {
210            close(false, 0);
211        }
212    
213        public void close(boolean force, int exitCode) {
214            forceShutdown = force;
215            try {
216                if (windowManager.close() ) {
217                    forceShutdown = true;
218                    if( getApplicationContext() instanceof ConfigurableApplicationContext ) {
219                        ((ConfigurableApplicationContext) getApplicationContext()).close();
220                    }
221                    getLifecycleAdvisor().onShutdown();
222                }
223            } finally {
224                if (isForceShutdown()) {
225                    System.exit(exitCode);
226                }
227            }
228        }
229    
230        /*
231         * Closes the application once all windows have been closed.
232         */
233        private class CloseApplicationObserver implements Observer {
234    
235            boolean firstWindowCreated = false;
236    
237            public void update( Observable o, Object arg ) {
238                int numOpenWidows = windowManager.getWindows().length;
239                // make sure we only close the application after at least 1 window
240                // has been added
241                if( !firstWindowCreated && numOpenWidows > 0 ) {
242                    firstWindowCreated = true;
243                } else if( firstWindowCreated && numOpenWidows == 0 ) {
244                    close();
245                }
246            }
247        }
248    
249        /**
250         * Starts this application.
251         */
252        public void start() {
253            getLifecycleAdvisor().onPreStartup();
254            openWindow(getLifecycleAdvisor().getStartingPageId());
255            getLifecycleAdvisor().onPostStartup();
256        }
257    }