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 }