1 22 package org.jboss.ejb.plugins; 23 24 import org.jboss.ejb.Container; 25 import org.jboss.invocation.Invocation; 26 import org.jboss.ha.framework.interfaces.GenericClusteringException; 27 import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock; 28 import org.jboss.ejb.EjbModule; 29 import javax.management.NotificationListener ; 30 import javax.management.Notification ; 31 import javax.management.AttributeChangeNotification ; 32 import javax.management.AttributeChangeNotificationFilter ; 33 import java.util.Collection ; 34 import java.util.Iterator ; 35 import org.jboss.system.ServiceMBean; 36 37 62 63 public class CleanShutdownInterceptor extends AbstractInterceptor 64 { 65 66 68 70 protected Container container = null; 71 72 protected EjbModule ejbModule = null; 73 protected String ejbModuleName = null; 74 75 private static ThreadLocal currentModule = new ThreadLocal (); 76 77 protected boolean allowInvocations = false; 78 protected boolean allowRemoteInvocations = false; 79 80 protected boolean isDebugEnabled = false; 81 82 public long runningInvocations = 0; 83 public long runningHomeInvocations = 0; 84 public long shutdownTimeout = 60000; 85 public long readAcquireTimeMs = 10000; 86 87 protected ReentrantWriterPreferenceReadWriteLock rwLock = new ReentrantWriterPreferenceReadWriteLock(); 88 89 91 private static final String METHOD_INVOCATION_TAG = "WrappingEjbModuleName"; 92 93 95 public CleanShutdownInterceptor () 96 { 97 } 98 99 101 public void onlyAllowLocalInvocations () 102 { 103 if (isDebugEnabled) log.debug ("Only allow local invocation from now on: " + this.container.getServiceName ().toString ()); 104 this.allowRemoteInvocations = false; 105 } 106 107 public void waitForNoMoreInvocations () 108 { 109 this.log.debug ("Waiting that the container " + container.getJmxName () + " finishes its running invocations. " + 110 this.runningHomeInvocations + " current home invocations and " + 111 this.runningInvocations + " current remote invocations."); 112 113 purgeRunningInvocations (); 114 if (isDebugEnabled) log.debug ("... Done: no more remote invocations currently running in this container."); 115 } 116 117 119 121 public void create() throws Exception { 122 super.create (); 123 this.allowInvocations = false; 124 this.allowRemoteInvocations = false; 125 126 this.isDebugEnabled = log.isDebugEnabled (); 127 128 ejbModuleName = ejbModule.getServiceName().toString(); 129 130 AttributeChangeNotificationFilter filter = new AttributeChangeNotificationFilter (); 133 filter.enableAttribute ("State"); 134 135 this.container.getServer (). 136 addNotificationListener (this.container.getEjbModule ().getServiceName (), 137 new CleanShutdownInterceptor.StateChangeListener (), 138 filter, 139 null); 140 141 ejbModule.putModuleData ("CleanShutDownInterceptor-" + this.container.getServiceName ().toString (), this); 144 } 145 146 public void start() throws Exception { 147 super.start(); 148 this.allowInvocations = true; 149 this.allowRemoteInvocations = true; 150 } 151 152 public void stop() { 153 super.stop (); 154 155 this.log.debug ("Stopping container " + container.getJmxName () + ". " + 156 this.runningHomeInvocations + " current home invocations and " + 157 this.runningInvocations + " current remote invocations."); 158 159 forbidInvocations (); 160 } 161 162 public void destroy() { 163 super.destroy (); 164 165 this.log.debug ("Destroying container " + container.getJmxName ().toString () + ". " + 166 this.runningHomeInvocations + " current home invocations and " + 167 this.runningInvocations + " current remote invocations."); 168 169 forbidInvocations() ; 170 } 171 172 public Object invokeHome (Invocation mi) 173 throws Exception 174 { 175 if (this.allowInvocations) 176 { 177 String origin = getOrigin (mi); 178 boolean isAppLocalCall = ejbModuleName.equals (origin); 179 180 if (!this.allowRemoteInvocations && !isAppLocalCall) 181 { 184 if (isDebugEnabled) log.debug ("Refusing a remote home invocation. here= " + ejbModuleName + "; Origin= " + origin); 185 throw new GenericClusteringException (GenericClusteringException.COMPLETED_NO, 186 "This application does not accept remote calls any more"); 187 } 188 189 try 193 { 194 if (!isAppLocalCall) { 196 if (!rwLock.readLock ().attempt (readAcquireTimeMs)) 197 throw new GenericClusteringException (GenericClusteringException.COMPLETED_NO, 198 "Container is shuting down on this node (timeout)"); 199 } 200 } 201 catch (java.lang.InterruptedException ie) 202 { 203 throw new GenericClusteringException (GenericClusteringException.COMPLETED_NO, 204 "Container is shuting down on this node"); 205 } 206 207 runningHomeInvocations++; 208 try 209 { 210 if (!isAppLocalCall) 211 setOrigin (mi); 212 return this.getNext ().invokeHome (mi); 213 } 214 catch (GenericClusteringException gce) 215 { 216 gce.setCompletionStatus (gce.COMPLETED_MAYBE); 221 throw gce; 222 } 223 finally 224 { 225 if (!isAppLocalCall) 226 revertOrigin (mi, origin); 227 228 runningHomeInvocations--; 229 if (!isAppLocalCall) rwLock.readLock ().release (); 231 } 232 } 233 else 234 throw new GenericClusteringException (GenericClusteringException.COMPLETED_NO, 235 "Container is shuting down on this node"); 236 } 237 238 public Object invoke (Invocation mi) 239 throws Exception 240 { 241 if (this.allowInvocations) 242 { 243 String origin = getOrigin (mi); 244 boolean isAppLocalCall = ejbModuleName.equals (origin); 245 246 if (!this.allowRemoteInvocations && !isAppLocalCall) 247 { 250 if (isDebugEnabled) log.debug ("Refusing a remote invocation"); 251 throw new GenericClusteringException (GenericClusteringException.COMPLETED_NO, 252 "This application does not accept remote calls any more"); 253 } 254 255 try 259 { 260 if (!isAppLocalCall) { 262 if (!rwLock.readLock ().attempt (readAcquireTimeMs)) 263 throw new GenericClusteringException (GenericClusteringException.COMPLETED_NO, 264 "Container is shuting down on this node (timeout)"); 265 } 266 } 267 catch (java.lang.InterruptedException ie) 268 { 269 throw new GenericClusteringException (GenericClusteringException.COMPLETED_NO, 270 "Container is shuting down on this node"); 271 } 272 273 runningInvocations++; 274 try 275 { 276 if (!isAppLocalCall) 277 setOrigin (mi); 278 return this.getNext ().invoke (mi); 279 } 280 catch (GenericClusteringException gce) 281 { 282 gce.setCompletionStatus (gce.COMPLETED_MAYBE); 287 throw gce; 288 } 289 finally 290 { 291 if (!isAppLocalCall) 292 revertOrigin (mi, origin); 293 294 runningInvocations--; 295 if (!isAppLocalCall) rwLock.readLock ().release (); 297 } 298 } 299 else 300 throw new GenericClusteringException (GenericClusteringException.COMPLETED_NO, 301 "Container is shuting down on this node"); 302 } 303 304 public Container getContainer () 305 { 306 return this.container; 307 } 308 309 313 public void setContainer (Container con) 314 { 315 this.container = con; 316 if (con != null) 317 this.ejbModule = con.getEjbModule (); 318 else 319 this.ejbModule = null; 320 } 321 322 324 326 protected void forbidInvocations () 327 { 328 this.allowInvocations = false; 329 330 purgeRunningInvocations(); 331 } 332 333 protected void purgeRunningInvocations () 334 { 335 try 336 { 337 if (this.rwLock.writeLock ().attempt (shutdownTimeout)) 338 this.rwLock.writeLock ().release (); 339 else 340 log.info ("Possible running invocations not terminated " + 341 "while leaving the container. Home: " + runningHomeInvocations + 342 ". Remote: " + runningInvocations + "."); 343 } 344 catch (Exception e) 345 { 346 log.info ("Exception while waiting for running invocations " + 347 "to leave container. Home: " + runningHomeInvocations + 348 ". Remote: " + runningInvocations + ".", e); 349 } 350 finally 351 { 352 353 } 354 } 355 356 protected String getOrigin (Invocation mi) 357 { 358 String value = (String )currentModule.get (); 359 if (log.isTraceEnabled()) 360 log.trace ("GET_ORIGIN: " + value + " in " + this.container.getServiceName ().toString ()); 361 return value; 362 } 363 364 protected void setOrigin (Invocation mi) 365 { 366 currentModule.set (this.ejbModuleName); 367 } 368 369 protected void revertOrigin (Invocation mi, String origin) 370 { 371 372 if (log.isTraceEnabled()) log.trace ("Crossing ejbModule border from " + this.ejbModuleName + " to " + origin); 373 currentModule.set (origin); 374 } 375 376 377 379 protected void containerIsAboutToStop () 380 { 381 log.debug ("Container about to stop: disabling HA-RMI access to bean from interceptor"); 382 383 boolean iAmTheManager = !Boolean.TRUE.equals (ejbModule.getModuleData ("ShutdownInterceptorElected")); 388 389 if (iAmTheManager) 390 { 391 ejbModule.putModuleData ("ShutdownInterceptorElected", Boolean.TRUE); 392 393 if (isDebugEnabled) log.debug ("Container is about to stop and I am the manager of the first step: blocking remote calls"); 394 Collection containers = ejbModule.getContainers (); 397 Iterator containersIter = containers.iterator (); 398 while (containersIter.hasNext ()) 399 { 400 Container otherContainer = (Container)containersIter.next (); 401 CleanShutdownInterceptor inter = (CleanShutdownInterceptor) 402 ejbModule.getModuleData ("CleanShutDownInterceptor-" + otherContainer.getServiceName ().toString ()); 403 if (inter == null) 404 { 405 log.debug ("Found an EJB that doesnt have a clean-shutdown interceptor: " + otherContainer.getJmxName ()); 406 } 407 else 408 { 409 inter.onlyAllowLocalInvocations (); 410 } 411 } 412 } 413 else 414 { 415 if (isDebugEnabled) log.debug ("Container is about to stop but I am not the manager: I don't manage the first step of the process."); 416 } 417 418 waitForNoMoreInvocations (); 423 } 424 425 427 class StateChangeListener implements NotificationListener 428 { 429 430 public void handleNotification (Notification notification, java.lang.Object handback) 431 { 432 if (notification instanceof AttributeChangeNotification ) 433 { 434 AttributeChangeNotification notif = (AttributeChangeNotification ) notification; 435 int value = ((Integer )notif.getNewValue()).intValue (); 436 437 if (value == ServiceMBean.STOPPING) 439 { 440 containerIsAboutToStop (); 441 } 442 } 443 } 444 445 } 446 447 } 448 | Popular Tags |