KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tcspring > ApplicationContextEventProtocol


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tcspring;
5
6 import org.apache.commons.logging.Log;
7 import org.apache.commons.logging.LogFactory;
8 import org.springframework.beans.factory.BeanFactory;
9 import org.springframework.context.ApplicationEvent;
10 import org.springframework.context.support.AbstractApplicationContext;
11
12 import com.tc.aspectwerkz.joinpoint.StaticJoinPoint;
13 import com.tc.object.bytecode.Manager;
14 import com.tc.object.bytecode.ManagerUtil;
15 import com.tc.object.config.DSOSpringConfigHelper;
16
17 import java.util.ArrayList JavaDoc;
18 import java.util.Collection JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.Map JavaDoc;
23
24 /**
25  * Manages the event multicast of ApplicationContext events. Preserves the context's ID, e.g. a multicast is a multicast
26  * only to the same "logical" contexts in the cluster (and its parents).
27  *
28  * @author Jonas Bonér
29  * @author Eugene Kuleshov
30  */

31 public class ApplicationContextEventProtocol {
32
33   private static transient Log logger = LogFactory.getLog(ApplicationContextEventProtocol.class);
34
35   /**
36    * Local list of <code>ConfigurableApplicationContext</code> TODO use weak set or register for context disposal,
37    * e.g. close()
38    */

39   private static transient final Map JavaDoc contexts = new HashMap JavaDoc();
40
41   private transient ThreadLocal JavaDoc isInMulticastEventCflow;
42
43   private String JavaDoc appName;
44   private Collection JavaDoc configs;
45   private Collection JavaDoc beanNames;
46   private Collection JavaDoc eventTypes;
47
48   /**
49    * Puts all clustered ApplicationContext instances into a node local HashMap .
50    * <p>
51    * Puts itself (the aspect) into a shared map, this is needed for DMI to work.
52    * <p>
53    * Puts some additional information into the aspect to be displayed as a regular info in the terracotta console.
54    * <p>
55    * Advises:
56    * <tt>
57    * before(
58    * withincode(* org.springframework.context.support.AbstractApplicationContext+.refresh())
59    * AND call(* org.springframework.context.support.AbstractApplicationContext+.publishEvent(..))
60    * AND target(ctx))
61    * </tt>
62    */

63   public void registerContext(StaticJoinPoint jp, AbstractApplicationContext ctx) {
64     DistributableBeanFactory distributableBeanFactory = (DistributableBeanFactory) ctx.getBeanFactory();
65     if (!distributableBeanFactory.isClustered()) { return; }
66
67     String JavaDoc id = distributableBeanFactory.getId();
68     List JavaDoc configHelpers = distributableBeanFactory.getSpringConfigHelpers();
69
70     String JavaDoc lockName = "@spring_info_" + id;
71
72     ManagerUtil.beginLock(lockName, Manager.LOCK_TYPE_WRITE);
73     try {
74       populateAspectWithApplicationContextInfo(distributableBeanFactory, configHelpers);
75       List JavaDoc aspects = (List JavaDoc) ManagerUtil.lookupOrCreateRoot("tc:spring_info:" + id, new ArrayList JavaDoc());
76       aspects.add(this); // needed for DMI
77
} finally {
78       ManagerUtil.commitLock(lockName);
79     }
80
81     synchronized (contexts) {
82       if (!contexts.containsKey(id)) {
83         contexts.put(id, ctx);
84       }
85     }
86   }
87
88   private void populateAspectWithApplicationContextInfo(DistributableBeanFactory distributableBeanFactory, List JavaDoc configHelpers) {
89     this.appName = distributableBeanFactory.getAppName();
90     this.configs = distributableBeanFactory.getLocations();
91     this.beanNames = new ArrayList JavaDoc();
92     this.eventTypes = new ArrayList JavaDoc();
93
94     for (Iterator JavaDoc it = configHelpers.iterator(); it.hasNext();) {
95       DSOSpringConfigHelper configHelper = (DSOSpringConfigHelper) it.next();
96       this.beanNames.addAll(configHelper.getDistributedBeans().keySet());
97       this.eventTypes.addAll(configHelper.getDistributedEvents());
98     }
99   }
100
101   /**
102    * Intercepts the sending of a spring ApplicationContext event, this advice captures it and
103    * tries to publish it as a distributed event.
104    * Invoked around void org.springframework.context.support.AbstractApplicationContext.publishEvent(..)
105    */

106   public Object JavaDoc interceptEvent(StaticJoinPoint jp, ApplicationEvent event, AbstractApplicationContext ctx)
107       throws Throwable JavaDoc {
108
109     BeanFactory beanFactory = ctx.getBeanFactory();
110     if (tryToPublishDistributedEvent(beanFactory, event)) {
111       return null;
112     }
113     
114     return jp.proceed();
115   }
116   
117   /**
118    *Tries to publish the event as a distributed event, by invoking a distributed method invocation.
119    */

120   public boolean tryToPublishDistributedEvent(BeanFactory beanFactory, ApplicationEvent event) {
121     if (isInMulticastEventCflow != null && isInMulticastEventCflow.get() != null) {
122       // logger.info("Could not publish as distributed (nested) " + event);
123
return false;
124     }
125
126     if (ignoredEventType(event)) {
127       // logger.info("Could not publish as distributed (ignored) " + event);
128
return false;
129     }
130     if (beanFactory instanceof DistributableBeanFactory) {
131       DistributableBeanFactory distributableBeanFactory = (DistributableBeanFactory) beanFactory;
132       if (distributableBeanFactory.isDistributedEvent(event.getClass().getName())) {
133         String JavaDoc ctxId = distributableBeanFactory.getId();
134         // logger.info("Publishing distributed " + event);
135
boolean requireCommit = false;
136         try {
137           requireCommit = ManagerUtil.distributedMethodCall(
138                                      this,
139                                      "multicastEvent(Ljava/lang/String;Lorg/springframework/context/ApplicationEvent;)V",
140                                      new Object JavaDoc[] { ctxId, event });
141           multicastEvent(ctxId, event); // local call
142

143         } catch (Throwable JavaDoc e) {
144           logger.error("Unable to send event " + event + "; " + e.getMessage(), e);
145         } finally {
146           if(requireCommit) {
147             ManagerUtil.distributedMethodCallCommit();
148           }
149         }
150         return true;
151 // } else {
152
// logger.info("Could not publish as distributed (not distributed context) " + event);
153
}
154     }
155     return false;
156
157   }
158
159   // TODO make ignored classes configurable
160
private boolean ignoredEventType(ApplicationEvent event) {
161     String JavaDoc name = event.getClass().getName();
162     return "org.springframework.context.event.ContextRefreshedEvent".equals(name)
163            || "org.springframework.context.event.ContextClosedEvent".equals(name)
164            || "org.springframework.web.context.support.RequestHandledEvent".equals(name)
165            || "org.springframework.web.context.support.ServletRequestHandledEvent".equals(name); // since 2.0
166
}
167
168   /**
169    * [Distributed Method Invocation]
170    * <p>
171    * 1. Gets the context by ID - needed since the DMI is invoked on another node and
172    * need to look up [it's context].
173    * <p>
174    * 2. Publish event in the context matching the ID
175    */

176   public void multicastEvent(final String JavaDoc ctxId, final ApplicationEvent event) {
177     AbstractApplicationContext context;
178     synchronized (contexts) {
179       context = (AbstractApplicationContext) contexts.get(ctxId);
180     }
181
182     logger.info(ctxId + " Publishing event " + event + " to " + context + " " + Thread.currentThread());
183
184     if (context != null) {
185       publish(context, event);
186     }
187   }
188
189   private void publish(AbstractApplicationContext context, final ApplicationEvent event) {
190     if (isInMulticastEventCflow == null) {
191       isInMulticastEventCflow = new ThreadLocal JavaDoc();
192     }
193     Integer JavaDoc n = (Integer JavaDoc) isInMulticastEventCflow.get();
194     if (n == null) {
195       isInMulticastEventCflow.set(new Integer JavaDoc(0));
196     } else {
197       isInMulticastEventCflow.set(new Integer JavaDoc(n.intValue() + 1));
198     }
199     try {
200       context.publishEvent(event);
201     } finally {
202       n = (Integer JavaDoc) isInMulticastEventCflow.get();
203       if (n == null || n.intValue() == 0) {
204         isInMulticastEventCflow.set(null);
205       } else {
206         isInMulticastEventCflow.set(new Integer JavaDoc(n.intValue() - 1));
207       }
208     }
209   }
210
211   public String JavaDoc toString() {
212     return appName + ":" + configs;
213   }
214 }
215
Popular Tags