You are viewing a plain text version of this content. The canonical link for it is here.
Posted to axis-cvs@ws.apache.org by ba...@apache.org on 2009/03/27 18:08:52 UTC
svn commit: r759272 - in /webservices/axis2/trunk/java/modules:
jaxws/src/org/apache/axis2/jaxws/
jaxws/src/org/apache/axis2/jaxws/core/controller/impl/
jaxws/src/org/apache/axis2/jaxws/handler/
jaxws/src/org/apache/axis2/jaxws/spi/ jaxws/test/org/apac...
Author: barrettj
Date: Fri Mar 27 17:08:51 2009
New Revision: 759272
URL: http://svn.apache.org/viewvc?rev=759272&view=rev
Log:
Cleanup dynamic ports added to a service when the associated service goes out of scope and is finalized.
Fix logic so that if a service delegate is closed and other delegates are using the same service, any dynamic ports that aren't in use by other delegates are released
Add associated tests.
Added:
webservices/axis2/trunk/java/modules/metadata/src/org/apache/axis2/jaxws/util/WeakKey.java
Modified:
webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/BindingProvider.java
webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/core/controller/impl/InvocationControllerImpl.java
webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/handler/HandlerResolverImpl.java
webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/spi/ServiceDelegate.java
webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/client/ReleaseServiceTests.java
webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/description/DescriptionTestUtils2.java
webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/spi/ClientMetadataTest.java
webservices/axis2/trunk/java/modules/metadata/src/org/apache/axis2/jaxws/description/impl/ServiceDescriptionImpl.java
Modified: webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/BindingProvider.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/BindingProvider.java?rev=759272&r1=759271&r2=759272&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/BindingProvider.java (original)
+++ webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/BindingProvider.java Fri Mar 27 17:08:51 2009
@@ -54,6 +54,9 @@
protected EndpointDescription endpointDesc;
+ // NOTE this reference to the ServiceDelegate MUST be a strong reference to keep the delegate
+ // from being GC'd when the Service instance in the client goes out of scope but ports under
+ // that service are still in use.
protected ServiceDelegate serviceDelegate;
private org.apache.axis2.jaxws.spi.Binding binding;
Modified: webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/core/controller/impl/InvocationControllerImpl.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/core/controller/impl/InvocationControllerImpl.java?rev=759272&r1=759271&r2=759272&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/core/controller/impl/InvocationControllerImpl.java (original)
+++ webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/core/controller/impl/InvocationControllerImpl.java Fri Mar 27 17:08:51 2009
@@ -336,7 +336,7 @@
// Put the understood header qnames on the request context where it can
// be found during response processing.
- if (understoodHeaders != null & understoodHeaders.size() > 0) {
+ if (understoodHeaders != null && understoodHeaders.size() > 0) {
if (log.isDebugEnabled()) {
log.debug("Adding understood header QName collection to message context " + understoodHeaders);
}
Modified: webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/handler/HandlerResolverImpl.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/handler/HandlerResolverImpl.java?rev=759272&r1=759271&r2=759272&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/handler/HandlerResolverImpl.java (original)
+++ webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/handler/HandlerResolverImpl.java Fri Mar 27 17:08:51 2009
@@ -43,6 +43,8 @@
import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.PortInfo;
import javax.xml.ws.handler.soap.SOAPHandler;
+
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -68,7 +70,7 @@
*/
private ServiceDescription serviceDesc;
- private Object serviceDelegateKey;
+ private WeakReference<Object> serviceDelegateKeyReference;
public HandlerResolverImpl(ServiceDescription sd) {
this(sd, null);
@@ -76,7 +78,9 @@
public HandlerResolverImpl(ServiceDescription sd, Object serviceDelegateKey) {
this.serviceDesc = sd;
- this.serviceDelegateKey = serviceDelegateKey;
+ if (serviceDelegateKey != null) {
+ this.serviceDelegateKeyReference = new WeakReference<Object>(serviceDelegateKey);
+ }
}
private ResolvedHandlersDescription getResolvedHandlersDescription(PortInfo portInfo) {
@@ -84,17 +88,26 @@
// On the client the handler information can be changed via service-delegate-specific
// deployment information. So, only look into the service-side cache only if the
// service delegate key is null.
- if (serviceDelegateKey == null) {
+ if (getServiceDelegateKey() == null) {
resolvedHandlersDesc = serviceDesc.getResolvedHandlersDescription(portInfo);
}
return resolvedHandlersDesc;
}
+
+ private Object getServiceDelegateKey() {
+ Object returnKey = null;
+ if (serviceDelegateKeyReference != null) {
+ returnKey = serviceDelegateKeyReference.get();
+ }
+ return returnKey;
+ }
+
private ResolvedHandlersDescription getOrCreateResolvedHandlersDescription(PortInfo portInfo) {
ResolvedHandlersDescription resolvedHandlersDesc = null;
// On the client the handler information can be changed via service-delegate-specific
// deployment information. So, only look into the service-side cache only if the
// service delegate key is null.
- if (serviceDelegateKey == null) {
+ if (getServiceDelegateKey() == null) {
resolvedHandlersDesc = serviceDesc.getResolvedHandlersDescription(portInfo);
if (resolvedHandlersDesc == null) {
resolvedHandlersDesc = DescriptionFactory.createResolvedHandlersDescription();
@@ -281,7 +294,7 @@
// Get the HandlerChains specified on the Endpoint (service-provider) or on the Service
// (service-requester).
- handlerChainsType = serviceDesc.getHandlerChain(serviceDelegateKey);
+ handlerChainsType = serviceDesc.getHandlerChain(getServiceDelegateKey());
// HandlerChains apply to specific Port Compoments (service-provider) or Ports (
// (service-requesters) so find the appropriate one.
@@ -303,7 +316,7 @@
// overrides the annotations as described in the long-winded comment above.
// -- THEN --
// Use this handler chains information
- HandlerChainsType hct_includingComposite = ed.getHandlerChain(serviceDelegateKey);
+ HandlerChainsType hct_includingComposite = ed.getHandlerChain(getServiceDelegateKey());
HandlerChainsType hct_noComposite = ed.getHandlerChain();
if (handlerChainsType == null || (hct_includingComposite != hct_noComposite)) {
handlerChainsType = hct_includingComposite;
Modified: webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/spi/ServiceDelegate.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/spi/ServiceDelegate.java?rev=759272&r1=759271&r2=759272&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/spi/ServiceDelegate.java (original)
+++ webservices/axis2/trunk/java/modules/jaxws/src/org/apache/axis2/jaxws/spi/ServiceDelegate.java Fri Mar 27 17:08:51 2009
@@ -901,12 +901,12 @@
ServiceDelegate returnServiceDelegate = null;
try {
try {
- Field serviceDelgateField = service.getClass().getDeclaredFields()[0];
+ Field serviceDelgateField = service.getClass().getDeclaredField("delegate");
serviceDelgateField.setAccessible(true);
returnServiceDelegate = (ServiceDelegate) serviceDelgateField.get(service);
- } catch (ArrayIndexOutOfBoundsException e) {
+ } catch (NoSuchFieldException e) {
// This may be a generated service subclass, so get the delegate from the superclass
- Field serviceDelegateField = service.getClass().getSuperclass().getDeclaredFields()[0];
+ Field serviceDelegateField = service.getClass().getSuperclass().getDeclaredField("delegate");
serviceDelegateField.setAccessible(true);
returnServiceDelegate = (ServiceDelegate) serviceDelegateField.get(service);
}
@@ -920,6 +920,11 @@
log.debug("Attempt to get service delegate for service caught exception.", e);
}
throw ExceptionFactory.makeWebServiceException(e);
+ } catch (NoSuchFieldException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Attempt to get service delegate for service caught exception.", e);
+ }
+ throw ExceptionFactory.makeWebServiceException(e);
}
return returnServiceDelegate;
@@ -957,6 +962,9 @@
}
protected void finalize() throws Throwable {
+ if (log.isDebugEnabled()) {
+ log.debug("ServiceDelegate.finalize entered for " + this);
+ }
try {
releaseServiceResources();
} catch (Exception e) {
@@ -966,5 +974,8 @@
} finally {
super.finalize();
}
+ if (log.isDebugEnabled()) {
+ log.debug("ServiceDelegate.finalize exited");
+ }
}
}
Modified: webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/client/ReleaseServiceTests.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/client/ReleaseServiceTests.java?rev=759272&r1=759271&r2=759272&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/client/ReleaseServiceTests.java (original)
+++ webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/client/ReleaseServiceTests.java Fri Mar 27 17:08:51 2009
@@ -28,11 +28,13 @@
import javax.jws.WebService;
import javax.xml.namespace.QName;
+import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import java.io.File;
+import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
@@ -53,7 +55,7 @@
static final String dynamicPort1 = "dynamicPort1";
static final String bindingID1 = null;
static final String epr1 = null;
-
+
/**
* When a ServiceDelegate will not be used anymore, a close call on it should release
* the AxisServices and such it holds. Verify this for dynamic ports.
@@ -141,16 +143,256 @@
ClientMetadataTest.restoreOriginalFactory();
}
}
+
+ /**
+ * Verify that a service with no BindingProviders created under it will result in the
+ * associated service delegate being finalized. BindingProviders are created when a port
+ * is added.
+ *
+ * This test only uses a single Service, so the delegate is not shared,
+ * so the entire Service Description will be released.
+ *
+ * NOTE: This test is disabled because forcing garbage collection is an inexact science
+ * at best. You can only ask the JVM to consider doing GC, and that behaves differently
+ * on different JVMS. So, there's no reliable way to make sure this test runs on various
+ * JVMs. So, it is disabled. Note that the test ran successfully on IBM
+ * java version "1.6.0" and failed on Sun java version "1.5.0_17"
+ */
+ public void _DISABLED_testGarbageCollection() {
+ QName svcQN = new QName(namespaceURI, svcLocalPart);
+ try {
+ ClientMetadataTest.installCachingFactory();
+
+ TestFinalizerService.finalizerCalled = 0;
+ TestFinalizerService svc1 = new TestFinalizerService(svcQN);
+
+ QName portQN1 = new QName(namespaceURI, dynamicPort1);
+ QName portQN2 = new QName(namespaceURI, dynamicPort1 + "_2");
+
+ svc1.addPort(portQN1,bindingID1, epr1);
+ svc1.addPort(portQN2,bindingID1, epr1);
+
+ // Verify that all is as expected in the runtime
+ ServiceDelegate sd1 = DescriptionTestUtils2.getServiceDelegate(svc1);
+
+ ServiceDescription svcDesc1 = sd1.getServiceDescription();
+ AxisConfiguration axisConfig = svcDesc1.getAxisConfigContext().getAxisConfiguration();
+
+ EndpointDescription epDesc1_port1 = svcDesc1.getEndpointDescription(portQN1, sd1);
+ AxisService axisSvc1_port1 = epDesc1_port1.getAxisService();
+ EndpointDescription epDesc1_port2 = svcDesc1.getEndpointDescription(portQN2, sd1);
+ AxisService axisSvc1_port2 = epDesc1_port2.getAxisService();
+
+ // Make sure all the AxisServices we expect exist. When the resources associated
+ // with a port are release below when the service is GC'd, the AxisServices are released.
+ assertTrue(axisConfig.getServiceGroup(axisSvc1_port1.getAxisServiceGroup().getServiceGroupName()) != null);
+ assertTrue(axisConfig.getServiceGroup(axisSvc1_port2.getAxisServiceGroup().getServiceGroupName()) != null);
+
+ // De-reference the Service instance so it can be GC'd
+ svc1 = null;
+
+ // Loop asking the sytem to GC until the service finalizer is called
+ int loop = 0;
+ while (TestFinalizerService.finalizerCalled == 0 && loop++ < 1000) {
+ System.gc();
+ }
+ assertTrue("GC did not occur", loop < 1000);
+
+ // Make sure the ports were released, which should be driven by the finalizer logic.
+ // The AxisServices are removed as part of the ports being released.
+ assertTrue("GC did not occur on delegate on port 1", axisConfig.getServiceGroup(axisSvc1_port1.getAxisServiceGroup().getServiceGroupName()) == null);
+ assertTrue("GC did not occur on delegate on port 2", axisConfig.getServiceGroup(axisSvc1_port2.getAxisServiceGroup().getServiceGroupName()) == null);
+
+ } finally {
+ ClientMetadataTest.restoreOriginalFactory();
+ }
+ }
/**
+ * Verify that service with no active Binding Providers under it results in the
+ * associated service delegate is finalized. This test uses multiple identical Services, so
+ * the delegate is shared. That means the entire ServiceDescription can't be released when the
+ * first instance goes out of scope; the specific delegate associated with the de-scoped service
+ * must be released individually.
+ *
+ * NOTE: This test is disabled because forcing garbage collection is an inexact science
+ * at best. You can only ask the JVM to consider doing GC, and that behaves differently
+ * on different JVMS. So, there's no reliable way to make sure this test runs on various
+ * JVMs. So, it is disabled. Note that the test ran successfully on IBM
+ * java version "1.6.0" and failed on Sun java version "1.5.0_17"
+ */
+ public void _DISABLED_testGarbageCollectionMultipleService() {
+ QName svcQN = new QName(namespaceURI, svcLocalPart);
+ try {
+ ClientMetadataTest.installCachingFactory();
+
+ TestFinalizerService.finalizerCalled = 0;
+ // We need 2 services we can de-reference and 2 we hold on, to which prevents the
+ // entire ServiceDescription from being released.
+ TestFinalizerService svc1 = new TestFinalizerService(svcQN);
+ TestFinalizerService svc2 = new TestFinalizerService(svcQN);
+ TestFinalizerService svc3 = new TestFinalizerService(svcQN);
+ TestFinalizerService svc4 = new TestFinalizerService(svcQN);
+
+ QName portQN1 = new QName(namespaceURI, dynamicPort1);
+ QName portQN2 = new QName(namespaceURI, dynamicPort1 + "_2");
+ QName portQN3 = new QName(namespaceURI, dynamicPort1 + "_3");
+ QName portQN4 = new QName(namespaceURI, dynamicPort1 + "_4");
+ QName portQN5 = new QName(namespaceURI, dynamicPort1 + "_5");
+ QName portQN6 = new QName(namespaceURI, dynamicPort1 + "_6");
+
+ // The services don't share any ports, so the ports can be released when the service
+ // is de-referenced and finalized.
+ svc1.addPort(portQN1, bindingID1, epr1);
+ svc1.addPort(portQN2, bindingID1, epr1);
+ svc2.addPort(portQN3, bindingID1, epr1);
+ svc2.addPort(portQN4, bindingID1, epr1);
+ svc3.addPort(portQN5, bindingID1, epr1);
+ svc3.addPort(portQN6, bindingID1, epr1);
+
+ ServiceDelegate sd1 = DescriptionTestUtils2.getServiceDelegate(svc1);
+ ServiceDelegate sd2 = DescriptionTestUtils2.getServiceDelegate(svc2);
+ ServiceDelegate sd3 = DescriptionTestUtils2.getServiceDelegate(svc3);
+
+ // Note that all the delegates will share the same service description
+ ServiceDescription svcDesc = sd1.getServiceDescription();
+ AxisConfiguration axisConfig = svcDesc.getAxisConfigContext().getAxisConfiguration();
+
+ EndpointDescription epDesc1_port1 = svcDesc.getEndpointDescription(portQN1, sd1);
+ AxisService axisSvc1_port1 = epDesc1_port1.getAxisService();
+ EndpointDescription epDesc1_port2 = svcDesc.getEndpointDescription(portQN2, sd1);
+ AxisService axisSvc1_port2 = epDesc1_port2.getAxisService();
+
+ EndpointDescription epDesc2_port3 = svcDesc.getEndpointDescription(portQN3, sd2);
+ AxisService axisSvc2_port3 = epDesc2_port3.getAxisService();
+ EndpointDescription epDesc2_port4 = svcDesc.getEndpointDescription(portQN4, sd2);
+ AxisService axisSvc2_port4 = epDesc2_port4.getAxisService();
+
+ EndpointDescription epDesc3_port5 = svcDesc.getEndpointDescription(portQN5, sd3);
+ AxisService axisSvc3_port5 = epDesc3_port5.getAxisService();
+ EndpointDescription epDesc3_port6 = svcDesc.getEndpointDescription(portQN6, sd3);
+ AxisService axisSvc3_port6 = epDesc3_port6.getAxisService();
+
+
+ // Make sure all the AxisServices we expect exist. When the resources associated
+ // with a port are released below after gc, the AxisServices are released.
+ assertTrue(axisConfig.getServiceGroup(axisSvc1_port1.getAxisServiceGroup().getServiceGroupName()) != null);
+ assertTrue(axisConfig.getServiceGroup(axisSvc1_port2.getAxisServiceGroup().getServiceGroupName()) != null);
+
+ // De-scope 2 of the 4 services, which should allow them to be GC'd
+ // Note that this assert keeps Java from optimizing the finalization of these early
+ assertTrue(svc1 != null && svc2 != null && svc3 != null && svc4 != null);
+ svc1 = null;
+ svc2 = null;
+
+ int loop = 0;
+ while (TestFinalizerService.finalizerCalled < 2 && loop++ < 1000) {
+ System.gc();
+ }
+ assertTrue("GC did not occur", loop < 1000);
+
+ // Make sure the ports were released for services 1 & 2, which should be driven by the finalizer logic.
+ // The AxisServices are removed as part of the ports being released.
+ assertTrue("GC did not occur on delegate 1 on port 1", axisConfig.getServiceGroup(axisSvc1_port1.getAxisServiceGroup().getServiceGroupName()) == null);
+ assertTrue("GC did not occur on delegate 1 on port 2", axisConfig.getServiceGroup(axisSvc1_port2.getAxisServiceGroup().getServiceGroupName()) == null);
+ assertTrue("GC did not occur on delegate 2 on port 3", axisConfig.getServiceGroup(axisSvc2_port3.getAxisServiceGroup().getServiceGroupName()) == null);
+ assertTrue("GC did not occur on delegate 2 on port 4", axisConfig.getServiceGroup(axisSvc2_port4.getAxisServiceGroup().getServiceGroupName()) == null);
+ // Make sure the ports for service 3 was not released
+ assertTrue("GC should not occur on delegate 3 on port 5", axisConfig.getServiceGroup(axisSvc3_port5.getAxisServiceGroup().getServiceGroupName()) != null);
+ assertTrue("GC should not occur on delegate 3 on port 6", axisConfig.getServiceGroup(axisSvc3_port6.getAxisServiceGroup().getServiceGroupName()) != null);
+ // Need to reference the value of svc3 & svc4 to keep Java from optimizing it and releasing it early
+ assertNotNull(svc3);
+ assertNotNull(svc4);
+ } finally {
+ ClientMetadataTest.restoreOriginalFactory();
+ }
+ }
+
+ /**
+ * Verify that a service with BindingProviders created under it does not result in the
+ * associated service delegate being finalized until those providers are no longer referenced
+ * and can be GC'd themselves.
+ *
+ * This validates the scenario where a client creates a service, then uses that service
+ * to create a port (either a dispatch or a proxy), and then de-references the service while
+ * still retaining a reference to the ports. One example of this is where a method is called
+ * to create a port, and the service is created within that method, used to create the port
+ * and then goes out of scope when the method returns the port.
+ *
+ * NOTE: This test is disabled because forcing garbage collection is an inexact science
+ * at best. You can only ask the JVM to consider doing GC, and that behaves differently
+ * on different JVMS. So, there's no reliable way to make sure this test runs on various
+ * JVMs. So, it is disabled. Note that the test ran successfully on IBM
+ * java version "1.6.0" and failed on Sun java version "1.5.0_17"
+ */
+ public void _DISABLED_testGarbageCollectionWithProvider() {
+ QName svcQN = new QName(namespaceURI, svcLocalPart);
+ Dispatch<String> port1Dispatch = null;
+ Dispatch<String> port2Dispatch = null;
+ for (int i = 0; i < 3; i++) {
+ System.out.println("Start testGarbageCollectionWithProvider " + i);
+ try {
+ ClientMetadataTest.installCachingFactory();
+
+ TestFinalizerService.finalizerCalled = 0;
+ TestFinalizerService svc1 = new TestFinalizerService(svcQN);
+
+ QName portQN1 = new QName(namespaceURI, dynamicPort1);
+ QName portQN2 = new QName(namespaceURI, dynamicPort1 + "_2");
+
+ svc1.addPort(portQN1,bindingID1, epr1);
+ port1Dispatch = svc1.createDispatch(portQN1, String.class, Service.Mode.PAYLOAD);
+ svc1.addPort(portQN2,bindingID1, epr1);
+ port2Dispatch = svc1.createDispatch(portQN2, String.class, Service.Mode.PAYLOAD);
+
+ ServiceDelegate sd1 = DescriptionTestUtils2.getServiceDelegate(svc1);
+
+ ServiceDescription svcDesc1 = sd1.getServiceDescription();
+ AxisConfiguration axisConfig = svcDesc1.getAxisConfigContext().getAxisConfiguration();
+
+ EndpointDescription epDesc1_port1 = svcDesc1.getEndpointDescription(portQN1, sd1);
+ AxisService axisSvc1_port1 = epDesc1_port1.getAxisService();
+
+ EndpointDescription epDesc1_port2 = svcDesc1.getEndpointDescription(portQN2, sd1);
+ AxisService axisSvc1_port2 = epDesc1_port2.getAxisService();
+
+ // Make sure all the AxisServices we expect exist. When the resources associated
+ // with a port are release below after the closes, the AxisServices are released.
+ assertTrue(axisConfig.getServiceGroup(axisSvc1_port1.getAxisServiceGroup().getServiceGroupName()) != null);
+ assertTrue(axisConfig.getServiceGroup(axisSvc1_port2.getAxisServiceGroup().getServiceGroupName()) != null);
+
+ svc1 = null;
+
+ int loop = 0;
+ while (TestFinalizerService.finalizerCalled == 0 && loop++ < 1000) {
+ System.gc();
+ }
+ assertTrue("GC did not occur", loop < 1000);
+
+ // After the service was released, make sure the ports were NOT released since
+ // there is still an active reference to the ports.
+ assertTrue("GC should not occur on delegate on port 1", axisConfig.getServiceGroup(axisSvc1_port1.getAxisServiceGroup().getServiceGroupName()) != null);
+ assertTrue("GC should not occur on delegate on port 2", axisConfig.getServiceGroup(axisSvc1_port2.getAxisServiceGroup().getServiceGroupName()) != null);
+
+ // The asserts keep the Java compiler from optimizing the null assignment from
+ // occurring too early
+ assertNotNull(port1Dispatch);
+ assertNotNull(port2Dispatch);
+ } finally {
+ ClientMetadataTest.restoreOriginalFactory();
+ }
+ }
+
+ }
+ /**
* Test that creating a large number of services and ports, and then having them released by
* the finalizers during garbage collections does not produce on Out of Memory Error.
*
* NOTE: This test is disabled because forcing garbage collection is an inexact science
- * at best. You can only ask the JVM to consider doing GC, and that behaves differntly
+ * at best. You can only ask the JVM to consider doing GC, and that behaves differently
* on different JVMS. So, there's no reliable way to make sure this test runs on various
- * JVMs. So, it is disabled. See the test that runns in a similar loop creating lots of
- * services and ports, and then explicitly calls the relese method. That test should
+ * JVMs. So, it is disabled. See the test that runs in a similar loop creating lots of
+ * services and ports, and then explicitly calls the release method. That test should
* reliably and predictably not produce an OOM because of the explicit release call.
*/
public void _DISABLED_testServiceReleaseServiceDescriptionFinalizer() {
@@ -324,6 +566,11 @@
// First close should NOT cleanup the endpoints since the other service is
// still using them.
org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc1);
+ // But it should remove the dynamic enpdoits for the closed service delegate
+ EndpointDescription epDesc1_port1_after_close = svcDesc1.getEndpointDescription(portQN1, sd1);
+ assertNull(epDesc1_port1_after_close);
+ EndpointDescription epDesc1_port2_after_close = svcDesc1.getEndpointDescription(portQN2, sd1);
+ assertNull(epDesc1_port2_after_close);
ServiceDescription svcDesc2_afterClose = sd2.getServiceDescription();
assertSame(svcDesc2, svcDesc2_afterClose);
@@ -371,6 +618,7 @@
}
}
+
public void testSeviceUseAfterClose() {
QName svcQN = new QName(namespaceURI, svcLocalPart);
try {
@@ -470,7 +718,7 @@
EndpointDescription epDesc3_port2 = svcDesc3.getEndpointDescription(portQN2, sd3);
assertSame(epDesc3_port2, epDesc2_port2_afterClose);
- // Close the 2nd delegate and make sure cahced objects are still there
+ // Close the 2nd delegate and make sure cached objects are still there
// since there's a 3rd delegate now
org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc2);
@@ -547,6 +795,158 @@
ClientMetadataTest.restoreOriginalFactory();
}
}
+
+ /**
+ * Verify that if multiple service are sharing a service description, the release of a shared
+ * dynamic port happens when there are no more delegates using that port, even if the service
+ * can not be released yet.
+ */
+ public void testMultipleServiceMultipleDynamicPortRelease() {
+ QName svcQN = new QName(namespaceURI, svcLocalPart);
+ try {
+ ClientMetadataTest.installCachingFactory();
+ Service svc1 = Service.create(svcQN);
+ Service svc2 = Service.create(svcQN);
+ Service svc3 = Service.create(svcQN);
+
+ // Verify that when a service is closed, dynamic ports that are being shared with other
+ // still-open services are not released, while ones that are not shared are released.
+ // The ports are used by the services as follows:
+ //
+ // Service
+ // svc1 svc2 svc3
+ // Port
+ // QN1 x x
+ // QN2 x x
+ // QN3 x
+ // QN4 x x
+ // QN5 x
+ //
+ // Closing the services in the following order should have the following effect:
+ // - close(svc1) should release QN3 only, not QN1 & QN2
+ // - close(svc2) should release QN1 and QN2, not QN4
+ // - close(svc3) should release everything since it is the last service sharing the
+ // service description.
+
+ QName portQN1 = new QName(namespaceURI, dynamicPort1);
+ QName portQN2 = new QName(namespaceURI, dynamicPort1 + "_2");
+ QName portQN3 = new QName(namespaceURI, dynamicPort1 + "_3");
+ QName portQN4 = new QName(namespaceURI, dynamicPort1 + "_4");
+ QName portQN5 = new QName(namespaceURI, dynamicPort1 + "_4");
+
+ svc1.addPort(portQN1,bindingID1, epr1);
+ svc1.addPort(portQN2,bindingID1, epr1);
+ svc1.addPort(portQN3,bindingID1, epr1);
+
+ svc2.addPort(portQN1,bindingID1, epr1);
+ svc2.addPort(portQN2,bindingID1, epr1);
+ svc2.addPort(portQN4,bindingID1, epr1);
+
+ svc3.addPort(portQN4, bindingID1, epr1);
+ svc3.addPort(portQN5, bindingID1, epr1);
+
+ // Verify that things are as expected and save off information for later asserts
+ // after the closes.
+
+ // Make sure all the Service Delegates are unique.
+ ServiceDelegate sd1 = DescriptionTestUtils2.getServiceDelegate(svc1);
+ ServiceDelegate sd2 = DescriptionTestUtils2.getServiceDelegate(svc2);
+ ServiceDelegate sd3 = DescriptionTestUtils2.getServiceDelegate(svc3);
+ assertNotSame(sd1, sd2);
+ assertNotSame(sd2, sd3);
+ assertNotSame(sd1, sd3);
+
+ // Make sure the ServiceDescription is shared across the delegate instances.
+ ServiceDescription svcDesc1 = sd1.getServiceDescription();
+ ServiceDescription svcDesc2 = sd2.getServiceDescription();
+ ServiceDescription svcDesc3 = sd3.getServiceDescription();
+ AxisConfiguration axisConfig = svcDesc1.getAxisConfigContext().getAxisConfiguration();
+ assertSame(svcDesc1, svcDesc2);
+ assertSame(svcDesc1, svcDesc3);
+ // Since the services descriptions are shared, use this in the rest of the test for clarity
+ ServiceDescription svcDesc = svcDesc1;
+
+ // Make sure the endpoint descriptions for the same ports are shared across the
+ // delegate instances
+
+ EndpointDescription epDesc1_port1 = svcDesc.getEndpointDescription(portQN1, sd1);
+ EndpointDescription epDesc2_port1 = svcDesc.getEndpointDescription(portQN1, sd2);
+ assertSame(epDesc1_port1, epDesc2_port1);
+ AxisService axisSvc1_port1 = epDesc1_port1.getAxisService();
+ AxisService axisSvc2_port1 = epDesc2_port1.getAxisService();
+ assertSame(axisSvc1_port1, axisSvc2_port1);
+ AxisService portQN1_AxisService = axisSvc1_port1;
+ AxisService portQN3_AxisService = svcDesc.getEndpointDescription(portQN3, sd1).getAxisService();
+ assertNull(svcDesc.getEndpointDescription(portQN1, sd3));
+
+ EndpointDescription epDesc1_port2 = svcDesc.getEndpointDescription(portQN2, sd1);
+ EndpointDescription epDesc2_port2 = svcDesc.getEndpointDescription(portQN2, sd2);
+ assertSame(epDesc1_port2, epDesc2_port2);
+ AxisService axisSvc1_port2 = epDesc1_port2.getAxisService();
+ AxisService axisSvc2_port2 = epDesc2_port2.getAxisService();
+ assertSame(axisSvc1_port2, axisSvc2_port2);
+ AxisService portQN2_AxisService = axisSvc1_port2;
+
+ EndpointDescription epDesc3_port4 = svcDesc.getEndpointDescription(portQN4, sd3);
+ assertNotNull(epDesc3_port4);
+ AxisService portQN4_AxisService = epDesc3_port4.getAxisService();
+
+ // Make sure all the AxisServices we expect exist. When the resoureces associated
+ // with a port are release below after the closes, the AxisServices are released.
+ assertTrue(axisConfig.getServiceGroup(portQN1_AxisService.getAxisServiceGroup().getServiceGroupName()) != null);
+ assertTrue(axisConfig.getServiceGroup(portQN2_AxisService.getAxisServiceGroup().getServiceGroupName()) != null);
+ assertTrue(axisConfig.getServiceGroup(portQN3_AxisService.getAxisServiceGroup().getServiceGroupName()) != null);
+ assertTrue(axisConfig.getServiceGroup(portQN4_AxisService.getAxisServiceGroup().getServiceGroupName()) != null);
+
+ // First close
+ org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc1);
+ // Should remove the entries for this delegate
+ assertNull(svcDesc.getEndpointDescription(portQN1, sd1));
+ assertNull(svcDesc.getEndpointDescription(portQN2, sd1));
+ // Should only release port 3 since 1 and 2 are shared and 4 wasn't added to this delegate
+ assertTrue(axisConfig.getServiceGroup(portQN1_AxisService.getAxisServiceGroup().getServiceGroupName()) != null);
+ assertTrue(axisConfig.getServiceGroup(portQN2_AxisService.getAxisServiceGroup().getServiceGroupName()) != null);
+ assertTrue(axisConfig.getServiceGroup(portQN3_AxisService.getAxisServiceGroup().getServiceGroupName()) == null);
+ assertTrue(axisConfig.getServiceGroup(portQN4_AxisService.getAxisServiceGroup().getServiceGroupName()) != null);
+
+ // Make sure the close didn't change anything unexpected
+ ServiceDescription svcDesc2_afterClose = sd2.getServiceDescription();
+ assertSame(svcDesc2, svcDesc2_afterClose);
+ EndpointDescription epDesc2_port1_afterClose =
+ svcDesc2_afterClose.getEndpointDescription(portQN1, sd2);
+ assertSame(epDesc2_port1, epDesc2_port1_afterClose);
+ EndpointDescription epDesc2_port2_afterClose =
+ svcDesc2_afterClose.getEndpointDescription(portQN2, sd2);
+ assertSame(epDesc2_port2, epDesc2_port2_afterClose);
+
+ // Second close.
+ // This should remove all entries for this delegate
+ // This should cause ports 1 and 2 to be released as both service 1 and 2 that were
+ // using them are now closed. Service 4 should be unaffected
+ org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc2);
+ assertNull(svcDesc.getEndpointDescription(portQN1, sd2));
+ assertNull(svcDesc.getEndpointDescription(portQN2, sd2));
+ assertNull(svcDesc.getEndpointDescription(portQN4, sd2));
+
+ assertTrue(axisConfig.getServiceGroup(portQN1_AxisService.getAxisServiceGroup().getServiceGroupName()) == null);
+ assertTrue(axisConfig.getServiceGroup(portQN2_AxisService.getAxisServiceGroup().getServiceGroupName()) == null);
+ assertTrue(axisConfig.getServiceGroup(portQN3_AxisService.getAxisServiceGroup().getServiceGroupName()) == null);
+ assertTrue(axisConfig.getServiceGroup(portQN4_AxisService.getAxisServiceGroup().getServiceGroupName()) != null);
+
+ // Make sure the close didn't change anything unexpected
+ ServiceDescription svcDesc3_afterClose = sd3.getServiceDescription();
+ assertSame(svcDesc3_afterClose, svcDesc3);
+
+ // Close the last delegate then verify all the services have been removed
+ // from the AxisConfiguration
+ org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc3);
+ HashMap axisServices = axisConfig.getServices();
+ assertEquals(0, axisServices.size());
+
+ } finally {
+ ClientMetadataTest.restoreOriginalFactory();
+ }
+ }
// =============================================================================================
// Utility methods
@@ -599,3 +999,24 @@
super(wsdlLocation, serviceName);
}
}
+
+
+/**
+ * Subclasses Service to track how many times the finalize() method is called. This allows the
+ * tests to tell when the Service instance is garbage collected.
+ */
+class TestFinalizerService extends javax.xml.ws.Service {
+ static int finalizerCalled = 0;
+ public TestFinalizerService(QName qn) {
+ super(null, qn);
+ }
+
+ protected TestFinalizerService(java.net.URL wsdlDocumentLocation, QName serviceName) {
+ super(wsdlDocumentLocation, serviceName);
+ }
+
+ public void finalize() {
+ finalizerCalled++;
+
+ }
+}
Modified: webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/description/DescriptionTestUtils2.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/description/DescriptionTestUtils2.java?rev=759272&r1=759271&r2=759272&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/description/DescriptionTestUtils2.java (original)
+++ webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/description/DescriptionTestUtils2.java Fri Mar 27 17:08:51 2009
@@ -91,12 +91,13 @@
ServiceDelegate returnServiceDelegate = null;
try {
try {
- Field serviceDelgateField = service.getClass().getDeclaredFields()[0];
+// Field serviceDelgateField = service.getClass().getDeclaredFields()[0];
+ Field serviceDelgateField = service.getClass().getDeclaredField("delegate");
serviceDelgateField.setAccessible(true);
returnServiceDelegate = (ServiceDelegate) serviceDelgateField.get(service);
- } catch (ArrayIndexOutOfBoundsException e) {
+ } catch (NoSuchFieldException e) {
// This may be a generated service subclass, so get the delegate from the superclass
- Field serviceDelegateField = service.getClass().getSuperclass().getDeclaredFields()[0];
+ Field serviceDelegateField = service.getClass().getSuperclass().getDeclaredField("delegate");
serviceDelegateField.setAccessible(true);
returnServiceDelegate = (ServiceDelegate) serviceDelegateField.get(service);
}
@@ -106,6 +107,9 @@
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
+ } catch (NoSuchFieldException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
}
return returnServiceDelegate;
}
Modified: webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/spi/ClientMetadataTest.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/spi/ClientMetadataTest.java?rev=759272&r1=759271&r2=759272&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/spi/ClientMetadataTest.java (original)
+++ webservices/axis2/trunk/java/modules/jaxws/test/org/apache/axis2/jaxws/spi/ClientMetadataTest.java Fri Mar 27 17:08:51 2009
@@ -1098,7 +1098,7 @@
if (context == null) {
context = super.getClientConfigurationContext();
}
- System.out.println("Test version of MetadataTestCachingClientContextFactory: " + context);
+// System.out.println("Test version of MetadataTestCachingClientContextFactory: " + context);
return context;
}
Modified: webservices/axis2/trunk/java/modules/metadata/src/org/apache/axis2/jaxws/description/impl/ServiceDescriptionImpl.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/metadata/src/org/apache/axis2/jaxws/description/impl/ServiceDescriptionImpl.java?rev=759272&r1=759271&r2=759272&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/metadata/src/org/apache/axis2/jaxws/description/impl/ServiceDescriptionImpl.java (original)
+++ webservices/axis2/trunk/java/modules/metadata/src/org/apache/axis2/jaxws/description/impl/ServiceDescriptionImpl.java Fri Mar 27 17:08:51 2009
@@ -48,6 +48,7 @@
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.axis2.jaxws.util.WSDL4JWrapper;
import org.apache.axis2.jaxws.util.WSDLWrapper;
+import org.apache.axis2.jaxws.util.WeakKey;
import org.apache.axis2.AxisFault;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.commons.logging.Log;
@@ -70,6 +71,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
+import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.net.ConnectException;
@@ -114,11 +116,13 @@
new HashMap<QName, EndpointDescription>();
// Endpoints for dynamic ports
- // This needs to be a strong reference (not Weak or Soft) so that the resource release logic
- // in the finalizer still has a reference to the EndpointDescriptions so they can be
- // released. Otherwise, this collection will be null when the finalizer is called.
- private Map<Object, Map<QName, EndpointDescriptionImpl>> dynamicEndpointDescriptions =
- new HashMap<Object, Map<QName, EndpointDescriptionImpl>>();
+ // The WeakKey is an instance of a ServiceDelegate. It is a Weak Reference so it does not
+ // prevent the service delegate from being GC'd
+ private Map<WeakKey, Map<QName, EndpointDescriptionImpl>> dynamicEndpointDescriptions =
+ new HashMap<WeakKey, Map<QName, EndpointDescriptionImpl>>();
+ // Used to cleanup entries in the dynamic port collection when the ServiceDelegate is
+ // GC'd
+ private ReferenceQueue dynamicPortRefQueue = new ReferenceQueue<WeakKey>();
// Cache classes for the info for resolved handlers
private SoftReference<Map<PortInfo, ResolvedHandlersDescription>> resolvedHandlersDescription =
@@ -712,8 +716,8 @@
public Collection<EndpointDescriptionImpl> getDynamicEndpointDescriptions_AsCollection(Object serviceDelegateKey) {
Collection <EndpointDescriptionImpl> dynamicEndpoints = null;
if (serviceDelegateKey != null ) {
- if (dynamicEndpointDescriptions.get(serviceDelegateKey) != null)
- dynamicEndpoints = dynamicEndpointDescriptions.get(serviceDelegateKey).values();
+ if (dynamicEndpointDescriptions.get(WeakKey.comparisonKey(serviceDelegateKey)) != null)
+ dynamicEndpoints = dynamicEndpointDescriptions.get(WeakKey.comparisonKey(serviceDelegateKey)).values();
}
return dynamicEndpoints;
}
@@ -2323,7 +2327,7 @@
private EndpointDescriptionImpl getDynamicEndpointDescriptionImpl(QName portQName, Object key) {
Map<QName, EndpointDescriptionImpl> innerMap = null;
synchronized(dynamicEndpointDescriptions) {
- innerMap = dynamicEndpointDescriptions.get(key);
+ innerMap = dynamicEndpointDescriptions.get(WeakKey.comparisonKey(key));
if (innerMap != null) {
return innerMap.get(portQName);
}
@@ -2335,10 +2339,10 @@
Object key) {
Map<QName, EndpointDescriptionImpl> innerMap = null;
synchronized(dynamicEndpointDescriptions) {
- innerMap = dynamicEndpointDescriptions.get(key);
+ innerMap = dynamicEndpointDescriptions.get(WeakKey.comparisonKey(key));
if (innerMap == null) {
innerMap = new HashMap<QName, EndpointDescriptionImpl>();
- dynamicEndpointDescriptions.put(key, innerMap);
+ dynamicEndpointDescriptions.put(new WeakKey(key, dynamicPortRefQueue), innerMap);
}
innerMap.put(endpointDescriptionImpl.getPortQName(), endpointDescriptionImpl);
}
@@ -2565,23 +2569,40 @@
public void releaseResources(Object delegate) {
try {
- if (log.isDebugEnabled()) {
- log.debug("ServiceDescription release resources called with delegate " + delegate);
- }
- // If the service desc can be removed from the cache, which means no other service delegates
- // are using it, then we will release the resources associated with it. If it can't be
- // removed because it is still in use, then just return.
- if (!DescriptionFactoryImpl.removeFromCache(this)) {
if (log.isDebugEnabled()) {
- log.debug("ServiceDesc was not removed from cache, so it will not be released");
+ log.debug("ServiceDescription release resources called with delegate " + delegate);
}
- return;
- }
-
- if (log.isDebugEnabled()) {
- log.debug("ServiceDesc was removed from cache, so releasing associated resources");
+
+ // If the entire service desc can be removed from the cache, which means no other service delegates
+ // are using it, then we will release the resources associated with it. If it can't be
+ // removed because it is still in use, then release resources it contains associated with
+ // this particular delegate
+ if (DescriptionFactoryImpl.removeFromCache(this)) {
+ if (log.isDebugEnabled()) {
+ log.debug("ServiceDesc not in use so it was removed from cache. Releasing all resources for this ServiceDesc");
+ }
+ releaseAllResourcesForServiceDescription();
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("ServiceDesc still in use so not removed from cache. Releasing resources for delegate");
+ }
+ releaseResourcesForDelegate(delegate);
+ }
+ } catch (Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug("Release resorces in ServiceDesc caught throwable ", t);
+ }
+ throw ExceptionFactory.makeWebServiceException(t);
}
-
+ return;
+ }
+
+ /**
+ * Release all resources for the ServiceDescription. This is done when there are no
+ * active service delegates which are using the ServiceDescription. Note that multiple
+ * service delegates can share a single ServiceDescription.
+ */
+ private void releaseAllResourcesForServiceDescription() {
// Close all the endpoint descs, both declared and dynamic
Collection<EndpointDescription> definedEndpoints = definedEndpointDescriptions.values();
@@ -2606,22 +2627,144 @@
Collection<EndpointDescriptionImpl> dynamicEndpoints = mapEntry.values();
if (dynamicEndpoints != null && dynamicEndpoints.size() > 0) {
for (EndpointDescription endpointDesc : dynamicEndpoints) {
- ((EndpointDescriptionImpl) endpointDesc).releaseResources(getAxisConfigContext());
- // Remove this endpoint from the list on axis config
- removeFromDynamicEndpointCache(endpointDesc);
+ releaseEndpoint(endpointDesc);
}
}
}
dynamicEndpointDescriptions.clear();
+ }
+
+ /**
+ * Release all the resources associated with a particular endpoint description.
+ * @param endpointDesc The endpoint for which the resources are to be released.
+ */
+ private void releaseEndpoint(EndpointDescription endpointDesc) {
+ ((EndpointDescriptionImpl) endpointDesc).releaseResources(getAxisConfigContext());
+ // Remove this endpoint from the list on axis config
+ removeFromDynamicEndpointCache(endpointDesc);
+ }
+
+ /**
+ * Release resources associated with a specific service delegate. When a service delegate is
+ * closed, if the entire ServiceDescription can not be released because other services are
+ * still using it, then attempt to release any resources associated with this closed service
+ * delegate. Currently, the resources which are released are:
+ * - Any unreferenced dynamic ports
+ *
+ * @param delegate
+ */
+ private void releaseResourcesForDelegate(Object delegate) {
+ // Get the dynamic endpoint entries tied to this delegate and remove the entry.
+ // If no other delegates refer to this port, then it can be released also.
+ synchronized(dynamicEndpointDescriptions) {
+ // Note that if the delegate has already been GC'd, then there will be no entry
+ // matching the delegate in the table, since the WeakKey delegate referent will have
+ // been set to null.
+ Map<QName, EndpointDescriptionImpl> delegateEntry =
+ dynamicEndpointDescriptions.remove(WeakKey.comparisonKey(delegate));
+ if (delegateEntry != null && delegateEntry.size() > 0) {
+ if (log.isDebugEnabled()) {
+ log.debug("Removed delegate from dynamic ports: " + delegate);
+ }
+ // There were dynamic ports for this delegate. Release all ports this delegate was
+ // using that no other delegates still references
+ releaseUnreferencedDynamicPorts(delegateEntry);
+ }
+ // Remove any entries that were GC'd. The instance of the WeakKey containing the
+ // ServiceDelegate that was GC'd is placed on the reference queue, so we use that exact
+ // key value to remove the entry from the collection.
+ Object gcKey = null;
+ while ((gcKey = dynamicPortRefQueue.poll()) != null) {
+ WeakKey removeKey = (WeakKey) gcKey;
+ if (log.isDebugEnabled()) {
+ log.debug("Removing GC'd key from dynamic ports: " + removeKey);
+ }
+ Map<QName, EndpointDescriptionImpl> removeEntry =
+ dynamicEndpointDescriptions.remove(removeKey);
+ if (removeEntry != null && removeEntry.size() > 0) {
+ // There were dynamic ports for this delegate. Release all ports this delegate was
+ // using that no other delegates still references
+ if (log.isDebugEnabled()) {
+ log.debug("Releasing dynamic ports referenced by GC'd key : " + removeEntry);
+ }
+ releaseUnreferencedDynamicPorts(removeEntry);
+ }
+ }
+ }
+ }
+
+ /**
+ * Release any dynamic ports in the collection argument that are not referenced by any
+ * currently-active service delegates. When a service delegate is closed, it is deleted
+ * from the list of currently-active delegates. The dynamic ports associated with that service
+ * delegate are passed to this method, which will check all the remaining service delegates
+ * to see if any of the dynamic ports are no longer in use. Any which are no longer in use
+ * will have the resources held by the EndpointDescriptoinImpl released.
+ *
+ * IMPORTANT: This method MUST be called with the within a
+ * synchronized(dynamicEndpointDescriptions) block to prevent ConcurrentModificationExceptions
+ *
+ * @param delegateEntry Map containing the dynamic endpoints referenced by a now-deleted
+ * service delegate.
+ */
+ private void releaseUnreferencedDynamicPorts(Map<QName, EndpointDescriptionImpl> delegateEntry) {
- } catch (Throwable t) {
- if (log.isDebugEnabled()) {
- log.debug("Release resorces in ServiceDesc caught throwable ", t);
+ // The argument is a colleciton of dynamic port QNames (the key) and the associated
+ // EndpointDescription for the dynamic port (the value). For each one of these, we need
+ // to check the remaining active service delegates to see of the dynamic port is no longer
+ // in use. If it is not, it can be relesed.
+
+ // Get a list of the (QName, EndpointDescriptionImpl) entries for the delegate that has
+ // been deleted.
+ Set<Map.Entry<QName, EndpointDescriptionImpl>> delegatePortEntries = delegateEntry.entrySet();
+ Iterator<Map.Entry<QName, EndpointDescriptionImpl>> delegatePortEntriesIteator =
+ delegatePortEntries.iterator();
+
+ // Loop through the (QName, EndpointDescriptionImpl) entries for the deleted delegate
+ // to see if the dynamic port is still being used by any of the remining service
+ // delegates.
+ while (delegatePortEntriesIteator.hasNext()) {
+
+ Map.Entry<QName, EndpointDescriptionImpl> delegatePortEntry =
+ delegatePortEntriesIteator.next();
+ boolean deletePort = true;
+
+ // For this entry, loop through all the still-active service delegates and see
+ // if this (QName, EndpointDescriptionImpl) still exists. If not, it can released.
+ Collection<Map<QName, EndpointDescriptionImpl>> activeDelegatePortEntries =
+ dynamicEndpointDescriptions.values();
+ Iterator<Map<QName, EndpointDescriptionImpl>> activeDelegatePortEntriesIterator =
+ activeDelegatePortEntries.iterator();
+ while (deletePort && activeDelegatePortEntriesIterator.hasNext()) {
+
+ // Check the dynamic ports for this currently-active service delegate to see
+ // if the it shared the deleted delegate's (QName, EndpointDescriptionImpl)
+ Map<QName, EndpointDescriptionImpl> activeDelegatePorts =
+ activeDelegatePortEntriesIterator.next();
+ Set<Map.Entry<QName, EndpointDescriptionImpl>> checkActivePorts =
+ activeDelegatePorts.entrySet();
+ Iterator<Map.Entry<QName, EndpointDescriptionImpl>> checkActivePortsIterator =
+ checkActivePorts.iterator();
+ while (deletePort && checkActivePortsIterator.hasNext()) {
+ Map.Entry<QName, EndpointDescriptionImpl> checkActivePort =
+ checkActivePortsIterator.next();
+ if (checkActivePort.getKey().equals(delegatePortEntry.getKey())
+ && checkActivePort.getValue().equals(delegatePortEntry.getValue())) {
+ deletePort = false;
+ }
+ }
+ }
+
+ // The port can be deleted because no other delegates refer to it
+ if (deletePort) {
+ if (log.isDebugEnabled()) {
+ log.debug("Releasing resources for dynamic port " + delegatePortEntry.getKey());
+ }
+ releaseEndpoint(delegatePortEntry.getValue());
}
- throw ExceptionFactory.makeWebServiceException(t);
}
}
-
+
/**
* Remove the endpointDescription from the list of dynamic ports held on the
* AxisConfiguration object.
Added: webservices/axis2/trunk/java/modules/metadata/src/org/apache/axis2/jaxws/util/WeakKey.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/metadata/src/org/apache/axis2/jaxws/util/WeakKey.java?rev=759272&view=auto
==============================================================================
--- webservices/axis2/trunk/java/modules/metadata/src/org/apache/axis2/jaxws/util/WeakKey.java (added)
+++ webservices/axis2/trunk/java/modules/metadata/src/org/apache/axis2/jaxws/util/WeakKey.java Fri Mar 27 17:08:51 2009
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.axis2.jaxws.util;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+
+/**
+ * Implement a WeakReference key to be used in a collection. Being a WeakReference, it will not
+ * prevent the key from being Garbage Collected. The key can only be created with a reference queue
+ * so that users of this class provide cleanup logic which uses the items in the reference queue to
+ * cleanup entries in the collection.
+ *
+ * Note that the ReferenceQueue will contain the WeakKey instance that assocaited with the
+ * referent that was GC'd. So, the elements on the ReferenceQueue can be used to directly access
+ * and remove entries in the collection it is a key for. For example, one could do something
+ * like the following in cleanup logic:
+ * Object gcKey = null;
+ * while ((gcKey = q.poll()) != null) {
+ * WeakKey wk = (WeakKey) gcKey;
+ * <collection value type> removedEntry = collection.remove(wk);
+ * ...
+ * }
+ */
+public class WeakKey extends WeakReference<Object> {
+ // The hashcode for this object will be based on the key object it is created with
+ private int keyHashCode = 0;
+
+ // This constructor is private to prevent creating a key without a ReferenceQueue
+ private WeakKey(Object key) {
+ super(key);
+ keyHashCode = calculateHashCode(key);
+ }
+
+ public WeakKey(Object key, ReferenceQueue q) {
+ super(key, q);
+ keyHashCode = calculateHashCode(key);
+ }
+
+ private static int calculateHashCode(Object r) {
+ return r != null ? r.hashCode() : 0;
+ }
+
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ } else if (this == o) {
+ return true;
+ } else if (!(o instanceof WeakKey)) {
+ return false;
+ } else {
+ WeakKey checkIt = (WeakKey) o;
+ if (checkIt.get() != null && checkIt.get().equals(this.get())) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ public int hashCode() {
+ return keyHashCode;
+ }
+
+ /**
+ * Return an instance of WeakKey that can be used in comparsion operations. For example, it
+ * can be used to lookup a key in a collection that has a WeakKey as the key.
+ * @param checkKey the key value
+ * @return an instance of WeakKey for the value
+ */
+ public static WeakKey comparisonKey(Object checkKey) {
+ return new WeakKey(checkKey);
+ }
+}