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 }