View Javadoc

1   /*
2    * Copyright 2004-2008 the Seasar Foundation and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.seasar.uruma.rcp.core;
17  
18  import java.io.FileNotFoundException;
19  import java.net.URL;
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.Enumeration;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.MissingResourceException;
26  import java.util.ResourceBundle;
27  
28  import org.eclipse.core.runtime.ContributorFactoryOSGi;
29  import org.eclipse.core.runtime.IContributor;
30  import org.eclipse.ui.IWorkbench;
31  import org.eclipse.ui.PlatformUI;
32  import org.osgi.framework.Bundle;
33  import org.seasar.framework.container.S2Container;
34  import org.seasar.framework.container.factory.S2ContainerFactory;
35  import org.seasar.framework.container.factory.SingletonS2ContainerFactory;
36  import org.seasar.framework.env.Env;
37  import org.seasar.framework.exception.ResourceNotFoundRuntimeException;
38  import org.seasar.framework.util.ResourceUtil;
39  import org.seasar.framework.util.StringUtil;
40  import org.seasar.framework.util.URLUtil;
41  import org.seasar.uruma.component.Template;
42  import org.seasar.uruma.component.UIComponentContainer;
43  import org.seasar.uruma.component.jface.TemplateImpl;
44  import org.seasar.uruma.component.rcp.ViewPartComponent;
45  import org.seasar.uruma.component.rcp.WorkbenchComponent;
46  import org.seasar.uruma.context.ApplicationContext;
47  import org.seasar.uruma.context.ContextFactory;
48  import org.seasar.uruma.context.WindowContext;
49  import org.seasar.uruma.core.ComponentUtil;
50  import org.seasar.uruma.core.TemplateManager;
51  import org.seasar.uruma.core.UrumaConstants;
52  import org.seasar.uruma.core.UrumaMessageCodes;
53  import org.seasar.uruma.core.ViewTemplateLoader;
54  import org.seasar.uruma.debug.action.UrumaDebugViewAction;
55  import org.seasar.uruma.exception.NotFoundException;
56  import org.seasar.uruma.exception.UrumaAppInitException;
57  import org.seasar.uruma.log.UrumaLogger;
58  import org.seasar.uruma.rcp.UrumaService;
59  import org.seasar.uruma.rcp.binding.CommandRegistry;
60  import org.seasar.uruma.rcp.configuration.ContributionBuilder;
61  import org.seasar.uruma.rcp.configuration.Extension;
62  import org.seasar.uruma.rcp.core.UrumaBundleState.BundleState;
63  import org.seasar.uruma.rcp.util.RcpResourceUtil;
64  import org.seasar.uruma.util.AssertionUtil;
65  
66  /**
67   * {@link UrumaService} の実装クラスです。<br />
68   * 本クラスは、 {@link UrumaServiceFactory} によって、Uruma アプリケーション毎に固有のインスタンスが生成されます。<br />
69   * 
70   * @author y-komori
71   */
72  public class UrumaServiceImpl implements UrumaService, UrumaConstants,
73          UrumaMessageCodes {
74      private static final UrumaLogger logger = UrumaLogger
75              .getLogger(UrumaService.class);
76  
77      protected static final String URUMA_CLASSLOADER_NAME = "UrumaClassLoader";
78  
79      protected static final String APP_CLASSLOADER_PREFIX = "AppClassLoader-";
80  
81      protected String appClassLoaderName;
82  
83      protected Bundle targetBundle;
84  
85      protected ClassLoader urumaClassLoader;
86  
87      protected ClassLoader appClassLoader;
88  
89      protected ClassLoader oldClassLoader;
90  
91      protected IContributor contributor;
92  
93      protected String pluginId;
94  
95      protected S2Container container;
96  
97      protected TemplateManager templateManager;
98  
99      protected ViewTemplateLoader templateLoader;
100 
101     protected ApplicationContext applicationContext;
102 
103     protected WindowContext windowContext;
104 
105     protected WorkbenchComponent workbenchComponent;
106 
107     protected List<Extension> extensions = new ArrayList<Extension>();
108 
109     protected String defaultContextId;
110 
111     protected ResourceBundle imageBundle;
112 
113     /**
114      * {@link UrumaServiceImpl} を構築します。<br />
115      * 
116      * @param targetBundle
117      *            ターゲットバンドル
118      */
119     public UrumaServiceImpl(final Bundle targetBundle) {
120         AssertionUtil.assertNotNull("targetBundle", targetBundle);
121         this.targetBundle = targetBundle;
122         this.urumaClassLoader = getClass().getClassLoader();
123         this.appClassLoader = getClass().getClassLoader();
124         this.pluginId = targetBundle.getSymbolicName();
125         this.defaultContextId = pluginId + ".context";
126         this.appClassLoaderName = APP_CLASSLOADER_PREFIX + this.pluginId;
127         initialize();
128     }
129 
130     /**
131      * 初期化処理を行います。<br />
132      */
133     protected void initialize() {
134         logger.log(URUMA_SERVICE_INIT_START, targetBundle.getSymbolicName());
135 
136         try {
137             ClassLoader loader = activateUrumaApplication(targetBundle);
138 
139             if (loader != null) {
140                 this.appClassLoader = loader;
141             }
142             switchToAppClassLoader();
143 
144             initS2Container();
145             prepareS2Components();
146 
147             UrumaBundleState.getInstance().setAppBundleState(
148                     BundleState.AVAILABLE);
149             logger.log(URUMA_SERVICE_INIT_END, targetBundle.getSymbolicName());
150         } catch (Throwable ex) {
151             logger.log(EXCEPTION_OCCURED_WITH_REASON, ex, ex.getMessage());
152             UrumaBundleState.getInstance().setUrumaAppInitializingException(ex);
153             throw new UrumaAppInitException(targetBundle, ex, ex.getMessage());
154         } finally {
155             restoreClassLoader();
156         }
157     }
158 
159     /**
160      * 拡張ポイントの設定を行います。<br />
161      */
162     protected void registerExtensions() {
163 
164         // Uruma Debug View XMLの読み込み
165         if (Env.UT.equals(Env.getValue())) {
166             try {
167                 String workbenchPath = DEFAULT_WORKBENCH_XML;
168                 URL resourceUrl = RcpResourceUtil
169                         .getLocalResourceUrl(workbenchPath);
170                 if (resourceUrl != null) {
171                     templateLoader.loadViewTemplates(resourceUrl);
172 
173                     // テンプレートファイルを遅延ロードすると
174                     // UrumaClassLoaderではないため
175                     // テンプレートファイルをロードできないので
176                     // ここで事前にテンプレートファイルを読み込む
177                     templateManager.getTemplates(ViewPartComponent.class);
178                 } else {
179                     throw new ResourceNotFoundRuntimeException(workbenchPath);
180                 }
181             } catch (Exception ex) {
182                 logger.log(EXCEPTION_OCCURED_WITH_REASON, ex, ex.getMessage());
183                 throw new UrumaAppInitException(targetBundle, ex, ex
184                         .getMessage());
185             }
186         }
187 
188         switchToAppClassLoader();
189         try {
190             setupContributor();
191 
192             setupContexts();
193             if (!DUMMY_WORKBENCH_PATH.equals(workbenchComponent.getPath())) {
194                 // アプリケーションのビューテンプレートの読み込み
195                 URL resourceUrl = RcpResourceUtil
196                         .getLocalResourceUrl(DEFAULT_WORKBENCH_XML);
197                 templateLoader.loadViewTemplates(resourceUrl);
198             }
199             ContributionBuilder.build(contributor, extensions);
200         } catch (Exception ex) {
201             logger.log(EXCEPTION_OCCURED_WITH_REASON, ex, ex.getMessage());
202             throw new UrumaAppInitException(targetBundle, ex, ex.getMessage());
203         } finally {
204             restoreClassLoader();
205         }
206     }
207 
208     /**
209      * 指定したバンドルをアクティベートします。
210      * 
211      * @param bundle
212      *            Urumaアプリケーションを含むバンドル
213      * 
214      * @return バンドルのクラスローダ
215      */
216     protected ClassLoader activateUrumaApplication(final Bundle bundle) {
217         String symbolicName = bundle.getSymbolicName();
218         logger.log(URUMA_APP_STARTING, symbolicName);
219 
220         ClassLoader bundleLoader = null;
221 
222         String className = findFirstClassName(bundle);
223         if (className != null) {
224             try {
225                 Class<?> clazz = bundle.loadClass(className);
226                 bundleLoader = clazz.getClassLoader();
227             } catch (ClassNotFoundException ex) {
228                 throw new UrumaAppInitException(bundle, ex, ex.getMessage());
229             }
230         } else {
231             throw new NotFoundException(URUMA_APP_CLASS_LOADER_NOT_FOUND);
232         }
233 
234         logger.log(URUMA_APP_STARTED, symbolicName);
235         return bundleLoader;
236     }
237 
238     /**
239      * {@link Bundle} に含まれるクラスファイルのうち、最初に見つかった一つのクラス名を返します。
240      * 
241      * @param bundle
242      *            {@link Bundle} オブジェクト
243      * @return 見つかったクラス名。見つからなかった場合は <code>null</code>。
244      */
245     @SuppressWarnings("unchecked")
246     protected String findFirstClassName(final Bundle bundle) {
247         Enumeration entries = bundle.findEntries("", "*.class", true);
248         if (entries != null) {
249             while (entries.hasMoreElements()) {
250                 // 最初に見つかったエントリのURLを取得
251                 String path = ((URL) entries.nextElement()).getPath();
252                 // パスから拡張子を取り除く
253                 path = path.substring(0, path.length() - ".class".length());
254                 // スラッシュをピリオドに変換
255                 path = StringUtil.replace(path, SLASH, PERIOD);
256 
257                 // クラスパスルートからの相対位置を調べるため、
258                 // 先頭から要素を取り去りながら、実際にロードできるパスを調べる
259                 while (true) {
260                     int pos = path.indexOf(PERIOD);
261                     if (pos >= 0) {
262                         path = path.substring(pos + 1);
263                         try {
264                             bundle.loadClass(path);
265                             return path;
266                         } catch (ClassNotFoundException ex) {
267                             continue;
268                         } catch (NoClassDefFoundError ex) {
269                             continue;
270                         }
271                     } else {
272                         break;
273                     }
274                 }
275                 return path;
276             }
277         }
278         return null;
279     }
280 
281     /**
282      * {@link S2Container} の初期化を行います。<br />
283      */
284     protected void initS2Container() throws ClassNotFoundException {
285         switchToUrumaClassLoader();
286 
287         // S2ContainerFactoryのConfiguretionを初期化。
288         S2ContainerFactory.destroy();
289 
290         // UrumaPluginのS2Containerを作成。
291         S2Container urumaContainer = S2ContainerFactory
292                 .create(UrumaConstants.URUMA_RCP_DICON_PATH);
293 
294         switchToAppClassLoader();
295 
296         String configPath = SingletonS2ContainerFactory.getConfigPath();
297 
298         // UrumaAppPluginのenv.txt読み込み
299         URL url = RcpResourceUtil
300                 .getLocalResourceUrlNoException(Env.DEFAULT_FILE_PATH);
301         if (url != null) {
302             try {
303                 Env.setFile(URLUtil.toFile(url));
304             } catch (FileNotFoundException ignore) {
305             }
306         }
307 
308         // UrumaAppPluginのS2Containerを作成。
309         try {
310             S2ContainerFactory.configure();
311             ResourceUtil.getResource(configPath);
312             container = S2ContainerFactory.create(configPath,
313                     getAppClassLoader());
314         } catch (ResourceNotFoundRuntimeException ex) {
315             // app.dicon が存在しない場合は、空の S2Container を生成する
316             container = S2ContainerFactory.create();
317         }
318 
319         container.include(urumaContainer);
320         container.init();
321 
322         SingletonS2ContainerFactory.setContainer(container);
323 
324         ComponentUtil.setS2Container(container);
325     }
326 
327     protected void prepareS2Components() {
328         this.templateManager = (TemplateManager) container
329                 .getComponent(TemplateManager.class);
330         this.templateLoader = (ViewTemplateLoader) container
331                 .getComponent(ViewTemplateLoader.class);
332         this.applicationContext = (ApplicationContext) container
333                 .getComponent(ApplicationContext.class);
334 
335         container.register(this, UrumaConstants.URUMA_SERVICE_S2NAME);
336 
337         // Debug用 アクションをロード
338         if (Env.UT.equals(Env.getValue())) {
339             UrumaDebugViewAction urumaDebugViewAction = new UrumaDebugViewAction();
340             String name = StringUtil.decapitalize(UrumaDebugViewAction.class
341                     .getSimpleName());
342             container.register(urumaDebugViewAction, name);
343         }
344     }
345 
346     protected void switchClassLoader(final ClassLoader loader,
347             final String loaderName) {
348         Thread currentThread = Thread.currentThread();
349         this.oldClassLoader = currentThread.getContextClassLoader();
350         if (logger.isDebugEnabled()) {
351             logger.log(SWITCH_CONTEXT_CLASS_LOADER, loaderName + "("
352                     + loader.toString() + ")");
353         }
354         currentThread.setContextClassLoader(loader);
355     }
356 
357     protected void setupContributor() {
358         this.contributor = ContributorFactoryOSGi
359                 .createContributor(targetBundle);
360     }
361 
362     protected void setupContexts() {
363         Template workbenchTemplate = getTemplate(DEFAULT_WORKBENCH_XML);
364         if (workbenchTemplate == null) {
365             logger.log(WORKBENCH_DEF_FILE_NOT_FOUND, DEFAULT_WORKBENCH_XML);
366             // workbench.xml が見つからない場合はダミーを生成する
367             workbenchTemplate = createDummyWorkbenchTemplate();
368         }
369 
370         UIComponentContainer root = workbenchTemplate.getRootComponent();
371         if (root instanceof WorkbenchComponent) {
372             this.workbenchComponent = (WorkbenchComponent) root;
373         } else {
374             throw new NotFoundException(WORKBENCH_ELEMENT_NOT_FOUND,
375                     workbenchTemplate.getPath());
376         }
377 
378         this.applicationContext.setValue(WORKBENCH_TEMPLATE_NAME,
379                 workbenchTemplate);
380         this.windowContext = ContextFactory.createWindowContext(
381                 applicationContext, WORKBENCH_WINDOW_CONTEXT_ID);
382     }
383 
384     protected Template createDummyWorkbenchTemplate() {
385         Template template = new TemplateImpl();
386         WorkbenchComponent workbench = new WorkbenchComponent();
387         workbench.setPath(DUMMY_WORKBENCH_PATH);
388         workbench.title = "Uruma";
389         workbench.initWidth = "50%";
390         workbench.initHeight = "50%";
391         template.setRootComponent(workbench);
392         return template;
393     }
394 
395     /*
396      * @see org.seasar.uruma.rcp.UrumaService#getBundle()
397      */
398     public Bundle getBundle() {
399         return this.targetBundle;
400     }
401 
402     /*
403      * @see org.seasar.uruma.rcp.UrumaService#getPluginId()
404      */
405     public String getPluginId() {
406         return this.pluginId;
407     }
408 
409     /*
410      * @see org.seasar.uruma.rcp.UrumaService#createRcpId(java.lang.String)
411      */
412     public String createRcpId(final String id) {
413         return pluginId + PERIOD + id;
414     }
415 
416     /*
417      * @see org.seasar.uruma.rcp.UrumaService#getLocalId(java.lang.String)
418      */
419     public String getLocalId(final String rcpId) {
420         if (rcpId != null) {
421             if (rcpId.startsWith(pluginId)) {
422                 return rcpId.substring(pluginId.length() + 1, rcpId.length());
423             } else {
424                 return rcpId;
425             }
426         } else {
427             return null;
428         }
429     }
430 
431     /*
432      * @see org.seasar.uruma.rcp.UrumaService#getTemplate(java.lang.String)
433      */
434     public Template getTemplate(final String path) {
435         try {
436             Template template = templateManager.getTemplate(path);
437             return template;
438         } catch (ResourceNotFoundRuntimeException ex) {
439             return null;
440         }
441     }
442 
443     /*
444      * @see org.seasar.uruma.rcp.UrumaService#getWorkbench()
445      */
446     public IWorkbench getWorkbench() {
447         return PlatformUI.getWorkbench();
448     }
449 
450     /*
451      * @see org.seasar.uruma.rcp.UrumaService#getWorkbenchComponent()
452      */
453     public WorkbenchComponent getWorkbenchComponent() {
454         Template template = (Template) applicationContext
455                 .getValue(UrumaConstants.WORKBENCH_TEMPLATE_NAME);
456         return (WorkbenchComponent) template.getRootComponent();
457     }
458 
459     /*
460      * @see org.seasar.uruma.rcp.UrumaService#getExtensions()
461      */
462     public List<Extension> getExtensions() {
463         return Collections.unmodifiableList(extensions);
464     }
465 
466     /*
467      * @see org.seasar.uruma.rcp.UrumaService#getExtension(java.lang.String)
468      */
469     public Extension getExtension(final String point) {
470         for (Extension extension : extensions) {
471             if (extension.point.equals(point)) {
472                 return extension;
473             }
474         }
475         return null;
476     }
477 
478     /*
479      * @see org.seasar.uruma.rcp.UrumaService#getWorkbenchWindowContext()
480      */
481     public WindowContext getWorkbenchWindowContext() {
482         return this.windowContext;
483     }
484 
485     /*
486      * @see org.seasar.uruma.rcp.UrumaService#getContainer()
487      */
488     public S2Container getContainer() {
489         return this.container;
490     }
491 
492     /*
493      * @see org.seasar.uruma.rcp.UrumaService#getAppClassLoader()
494      */
495     public ClassLoader getAppClassLoader() {
496         return this.appClassLoader;
497     }
498 
499     /*
500      * @see org.seasar.uruma.rcp.UrumaService#getUrumaClassLoader()
501      */
502     public ClassLoader getUrumaClassLoader() {
503         return this.urumaClassLoader;
504     }
505 
506     /*
507      * @see org.seasar.uruma.rcp.UrumaService#switchToAppClassLoader()
508      */
509     public void switchToAppClassLoader() {
510         switchClassLoader(appClassLoader, appClassLoaderName);
511     }
512 
513     /*
514      * @see org.seasar.uruma.rcp.UrumaService#switchToUrumaClassLoader()
515      */
516     public void switchToUrumaClassLoader() {
517         switchClassLoader(urumaClassLoader, URUMA_CLASSLOADER_NAME);
518     }
519 
520     /*
521      * @see org.seasar.uruma.rcp.UrumaService#restoreClassLoader()
522      */
523     public void restoreClassLoader() {
524         if (logger.isDebugEnabled()) {
525             String name = "";
526             if (oldClassLoader == urumaClassLoader) {
527                 name = URUMA_CLASSLOADER_NAME;
528             } else if (oldClassLoader == appClassLoader) {
529                 name = appClassLoaderName;
530             }
531             logger.log(SWITCH_CONTEXT_CLASS_LOADER, name + "("
532                     + oldClassLoader.toString() + ")");
533         }
534         Thread.currentThread().setContextClassLoader(oldClassLoader);
535     }
536 
537     /*
538      * @see org.seasar.uruma.rcp.UrumaService#getCommandRegistry()
539      */
540     public CommandRegistry getCommandRegistry() {
541         return (CommandRegistry) container.getComponent(CommandRegistry.class);
542     }
543 
544     /*
545      * @see org.seasar.uruma.rcp.UrumaService#getDefaultContextId()
546      */
547     public String getDefaultContextId() {
548         return this.defaultContextId;
549     }
550 
551     /*
552      * @see org.seasar.uruma.rcp.UrumaService#getImageBundle()
553      */
554     public ResourceBundle getImageBundle() {
555         if (imageBundle == null) {
556             try {
557                 imageBundle = ResourceBundle.getBundle(
558                         DEFAULT_IMAGE_BUNDLE_PATH, Locale.getDefault(),
559                         appClassLoader);
560             } catch (MissingResourceException ex) {
561                 logger.log(IMAGE_DEF_BUNDLE_NOT_FOUND,
562                         DEFAULT_IMAGE_BUNDLE_PATH);
563             }
564         }
565         return imageBundle;
566     }
567 
568     /**
569      * 本サービスを破棄します。<br />
570      */
571     void destroy() {
572         logger.log(URUMA_SERVICE_DESTROY, targetBundle.getSymbolicName());
573         container.destroy();
574     }
575 
576     /*
577      * @see org.seasar.uruma.rcp.UrumaService#getViewPartComponent()
578      */
579     public List<ViewPartComponent> getViewPartComponent() {
580         List<ViewPartComponent> resultList = new ArrayList<ViewPartComponent>();
581         List<Template> templates = templateManager
582                 .getTemplates(ViewPartComponent.class);
583         for (Template template : templates) {
584             ViewPartComponent viewPart = (ViewPartComponent) template
585                     .getRootComponent();
586             resultList.add(viewPart);
587         }
588         return resultList;
589     }
590 }