View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2005 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  package ch.elca.el4j.services.gui.swing;
18  
19  import java.awt.EventQueue;
20  
21  import javax.swing.Action;
22  import javax.swing.JComponent;
23  import javax.swing.JDialog;
24  import javax.swing.JFrame;
25  import javax.swing.UIManager;
26  import javax.swing.UnsupportedLookAndFeelException;
27  
28  import org.bushe.swing.event.EventServiceExistsException;
29  import org.bushe.swing.event.EventServiceLocator;
30  import org.jdesktop.application.Application;
31  import org.jdesktop.application.SingleFrameApplication;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  import org.springframework.beans.factory.NoSuchBeanDefinitionException;
35  import org.springframework.context.ApplicationContext;
36  import org.springframework.context.ConfigurableApplicationContext;
37  
38  import ch.elca.el4j.core.context.ModuleApplicationContext;
39  import ch.elca.el4j.core.context.ModuleApplicationContextConfiguration;
40  import ch.elca.el4j.services.gui.swing.config.DefaultConfig;
41  import ch.elca.el4j.services.gui.swing.cookswing.TagLibraryFactory;
42  import ch.elca.el4j.services.gui.swing.cookswing.action.ActionsContextAware;
43  import ch.elca.el4j.services.gui.swing.eventbus.ExceptionThrowingEventService;
44  import ch.elca.el4j.services.gui.swing.exceptions.CookXmlExceptionHandler;
45  import ch.elca.el4j.services.gui.swing.exceptions.Exceptions;
46  import ch.elca.el4j.services.gui.swing.frames.ApplicationFrame;
47  import ch.elca.el4j.services.gui.swing.wrapper.JFrameWrapperFactory;
48  import ch.elca.el4j.util.config.GenericConfig;
49  import cookxml.cookswing.CookSwing;
50  
51  
52  /**
53   * Parent class for GUI applications. (For MDI applications refer to
54   *  {@link ch.elca.el4j.services.gui.swing.MDIApplication })
55   *
56   * Additional features:
57   *  <ul>
58   *   <li> give access to a Spring application context
59   *   <li> allows installing a handler for uncaught exceptions, refer also to
60   *         {@link ch.elca.el4j.services.gui.swing.exceptions.Exceptions} and
61   *         {@link ch.elca.el4j.services.gui.swing.exceptions.Handler}
62   *   <li> defines a convenience method for menus
63   *   <li> defines a getAction(String) method
64   *      (refer to recommended programming pattern with this method of
65   *       the app framework )
66   *   <li> defines an {@link ActionsContext} which allows to spread Actions over several classes
67   *  </ul>
68   *
69   * @svnLink $Revision: 4010 $;$Date: 2009-12-01 10:59:54 +0100 (Di, 01. Dez 2009) $;$Author: jonasha $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/swing/src/main/java/ch/elca/el4j/services/gui/swing/GUIApplication.java $
70   *
71   * @author Stefan Wismer (SWI)
72   */
73  public abstract class GUIApplication extends SingleFrameApplication implements ActionsContextAware {
74  
75  	/**
76  	 * The logger.
77  	 */
78  	private static final Logger s_logger = LoggerFactory.getLogger(
79  		GUIApplication.class);
80  
81  	/**
82  	 * The Spring context.
83  	 */
84  	protected ApplicationContext springContext;
85  	
86  	/**
87  	 * The Actions context for resolving action names.
88  	 */
89  	protected ActionsContext actionsContext = ActionsContext.create(this);
90  	
91  	/**
92  	 * The configuration. This is used to set constant parameters like colors to mark values as
93  	 * invalid or default renderers.
94  	 */
95  	protected GenericConfig config;
96  	
97  	
98  	/**
99  	 * @return      the Spring application context
100 	 */
101 	public ApplicationContext getSpringContext() {
102 		return springContext;
103 	}
104 	
105 	/**
106 	 * Sets the Spring context.
107 	 *
108 	 * @param springContext     the new Spring context
109 	 */
110 	public void setSpringContext(ApplicationContext springContext) {
111 		if (springContext instanceof ModuleApplicationContext) {
112 			ModuleApplicationContext context = (ModuleApplicationContext) springContext;
113 			context.getBeanFactory().registerSingleton("GUIApplication", this);
114 			//context.getBeanFactory().registerSingleton("GUIApplicationConfig", getConfig());
115 		}
116 		this.springContext = springContext;
117 	}
118 	
119 	/**
120 	 * @return    the current configuration
121 	 */
122 	public GenericConfig getConfig() {
123 		return config;
124 	}
125 	
126 	/**
127 	 * @param config    the configuration to set
128 	 */
129 	public void setConfig(GenericConfig config) {
130 		this.config = config;
131 	}
132 
133 	/**
134 	 * Launch the application and do some adaptations for Spring.
135 	 *
136 	 * @link GUIApplication } class documentation for more information) CAVEAT:
137 	 *       We used cut&paste to extend the launch method of the Application
138 	 *       class. At each change of the class Application of the app
139 	 *       framework, adapt the code of this method (the app framework does
140 	 *       not allow a "clean" extension).
141 	 * @param applicationClass
142 	 *            the application class to launch
143 	 * @param args
144 	 *            command line arguments
145 	 * @param contextConfig
146 	 *            spring application context configuration
147 	 */
148 	public static synchronized <T extends GUIApplication> void launch(
149 		final Class<T> applicationClass, final String[] args,
150 		final ModuleApplicationContextConfiguration contextConfig) {
151 
152 		// install exception handler
153 		Thread.setDefaultUncaughtExceptionHandler(Exceptions.getInstance());
154 		// little hack to make it work also in Swing/AWT
155 		System.setProperty("sun.awt.exception.handler", Exceptions.class.getName());
156 		// register custom eventService that not only logs errors but also throws exceptions
157 		try {
158 			EventServiceLocator.setEventService(EventServiceLocator.SERVICE_NAME_EVENT_BUS,
159 				new ExceptionThrowingEventService());
160 		} catch (EventServiceExistsException e) {
161 			s_logger.warn("Unable to register EventService.", e);
162 		}
163 		
164 		// configure CookSwing
165 		CookSwing.setDefaultExceptionHandler(CookXmlExceptionHandler.getInstance());
166 		CookSwing.setDefaultAccessible(true);
167 		CookSwing.setSwingTagLibrary(TagLibraryFactory.getTagLibrary());
168 
169 		Runnable doCreateAndShowGUI = new Runnable() {
170 			public void run() {
171 				try {
172 					GUIApplication application = Application.create(applicationClass);
173 					
174 					Application.setInstance(application);
175 
176 					// new: set the spring context early
177 					application.setSpringContext(new ModuleApplicationContext(contextConfig));
178 					
179 					// set default config
180 					application.setConfig(new DefaultConfig());
181 					
182 					setupLookAndFeel(application);
183 					
184 					application.initialize(args);
185 					application.startup();
186 					application.waitForReady();
187 				} catch (Exception e) {
188 					String msg = String.format("Application %s failed to launch", applicationClass);
189 					s_logger.error(msg, e);
190 					throw (new Error(msg, e));
191 				}
192 			}
193 		};
194 		EventQueue.invokeLater(doCreateAndShowGUI);
195 	}
196 	
197 	/**
198 	 * @return    the current instance
199 	 */
200 	public static GUIApplication getInstance() {
201 		return (GUIApplication) Application.getInstance();
202 	}
203 	
204 	/**
205 	 * Show a component which should be created by Spring.
206 	 * @param beanName    the Spring bean name
207 	 */
208 	@SuppressWarnings("unchecked")
209 	public void show(String beanName) throws NoSuchBeanDefinitionException {
210 		if (!springContext.containsBean(beanName)) {
211 			throw new NoSuchBeanDefinitionException(beanName);
212 		}
213 		Class beanClass = springContext.getType(beanName);
214 		if (JComponent.class.isAssignableFrom(beanClass)) {
215 			show((JComponent) springContext.getBean(beanName));
216 		} else if (JDialog.class.isAssignableFrom(beanClass)) {
217 			show((JDialog) springContext.getBean(beanName));
218 		}
219 	}
220 	
221 	/**
222 	 * Show a nested component.
223 	 * @param component    the component to show
224 	 */
225 	public void show(JComponent component) {
226 		show(JFrameWrapperFactory.wrap(component));
227 	}
228 	
229 	/**
230 	 * Show a frame.
231 	 * @param frame    the frame to show
232 	 */
233 	public void show(ApplicationFrame frame) {
234 		frame.show();
235 	}
236 	
237 	/**
238 	 * Show the main frame.
239 	 * @param frame    the main frame
240 	 */
241 	public void showMain(JFrame frame) {
242 		setMainFrame(frame);
243 		showMain();
244 	}
245 	
246 	/**
247 	 * Show the main frame.
248 	 * @param component    the component to put into the main frame
249 	 */
250 	public void showMain(JComponent component) {
251 		super.show(component);
252 	}
253 	
254 	/**
255 	 * Show the main frame.
256 	 */
257 	public void showMain() {
258 		super.show(getMainFrame());
259 	}
260 	
261 	/** {@inheritDoc} */
262 	public ActionsContext getActionsContext() {
263 		return actionsContext;
264 	}
265 	
266 	/**
267 	 * Returns the action object for a specific object and action name.
268 	 * @param object        the object containing actions
269 	 * @param actionName    the action name as String
270 	 * @return              the corresponding action object
271 	 */
272 	public Action getAction(Object object, String actionName) {
273 		org.jdesktop.application.ApplicationContext ac
274 			= Application.getInstance().getContext();
275 		
276 		return ac.getActionMap(Object.class, object).get(actionName);
277 	}
278 	
279 	/**
280 	 * Returns the string for a specific resource id.
281 	 * @param id    the resource id
282 	 * @return      the corresponding string
283 	 */
284 	public String getString(String id) {
285 		org.jdesktop.application.ApplicationContext ac
286 			= Application.getInstance().getContext();
287 
288 		return ac.getResourceMap().getString(id);
289 	}
290 	
291 	/** {@inheritDoc} */
292 	@Override
293 	protected void shutdown() {
294 		try {
295 			EventServiceLocator.setEventService(EventServiceLocator.SERVICE_NAME_EVENT_BUS, null);
296 		} catch (EventServiceExistsException e) {
297 			s_logger.warn("Unable to unregister EventService.", e);
298 		}
299 		
300 		if (springContext != null) {
301 			if (springContext instanceof ConfigurableApplicationContext) {
302 				((ConfigurableApplicationContext) springContext).close();
303 			}
304 		}
305 		super.shutdown();
306 	}
307 
308 	/**
309 	 * Setup look and feel of the application.
310 	 * @param application    the GUI application
311 	 */
312 	private static void setupLookAndFeel(GUIApplication application) {
313 		// apply first look and feel in list that is available
314 		final String lnfKey = "Application.preferredLookAndFeel";
315 		String lnfs = application.getContext().getResourceMap().getString(lnfKey);
316 		if (lnfs != null) {
317 			for (String lnf : lnfs.split(",")) {
318 				try {
319 					lnf = lnf.trim();
320 					if (lnf.equalsIgnoreCase("system")) {
321 						String name = UIManager.getSystemLookAndFeelClassName();
322 						UIManager.setLookAndFeel(name);
323 					} else {
324 						UIManager.setLookAndFeel(lnf);
325 					}
326 					
327 					break;
328 				} catch (ClassNotFoundException e) {
329 					s_logger.info("Look and feel '" + lnf + "' is not available.");
330 					continue;
331 				} catch (InstantiationException e) {
332 					s_logger.info("Look and feel '" + lnf + "' is not available.");
333 					continue;
334 				} catch (IllegalAccessException e) {
335 					s_logger.info("Look and feel '" + lnf + "' is not available.");
336 					continue;
337 				} catch (UnsupportedLookAndFeelException e) {
338 					s_logger.info("Look and feel '" + lnf + "' is not available.");
339 					continue;
340 				
341 				}
342 			}
343 		}
344 	}
345 	
346 }