/*
 * 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.client;

import org.apache.axis2.description.AxisService;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.jaxws.description.DescriptionTestUtils2;
import org.apache.axis2.jaxws.description.EndpointDescription;
import org.apache.axis2.jaxws.description.ServiceDescription;
import org.apache.axis2.jaxws.spi.ClientMetadataTest;
import org.apache.axis2.jaxws.spi.ServiceDelegate;

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;
import java.util.Map;

import junit.framework.TestCase;

/**
 * Verify that when a Service (i.e. a JAXWS Service Delegate instance) is no longer needed
 * the resources it uses are cleaned up (i.e the EndpointDescription, AxisService, etc).
 * The release can be driven by an explicit proprietary call or via the finalizer on the
 * ServiceDelegate that is run during garbage collection.
 */
public class ReleaseServiceTests extends TestCase {
    static final String namespaceURI = "http://dispatch.client.jaxws.axis2.apache.org";
    static final String svcLocalPart = "svcLocalPart";
    
    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.
     */
    public void testServiceReleaseDynamicPort() {
        QName svcQN = new QName(namespaceURI, svcLocalPart);
        try {
            ClientMetadataTest.installCachingFactory();

            Service svc1 = Service.create(svcQN);
            QName portQN = new QName(namespaceURI, dynamicPort1);
            svc1.addPort(portQN, bindingID1, epr1);

            // User internal state to verify the port information before and after the close
            ServiceDelegate delegate = DescriptionTestUtils2.getServiceDelegate(svc1);
            ServiceDescription svcDesc = delegate.getServiceDescription();
            EndpointDescription epDesc= svcDesc.getEndpointDescription(portQN, delegate);
            assertNotNull(epDesc);
            AxisConfiguration axisConfig = svcDesc.getAxisConfigContext().getAxisConfiguration();
            HashMap axisServices = axisConfig.getServices();
            assertEquals(1, axisServices.size());

            org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc1);

            axisServices = axisConfig.getServices();
            assertEquals(0, axisServices.size());

            epDesc= svcDesc.getEndpointDescription(portQN, delegate);
            assertNull(epDesc);                

        } finally {
            ClientMetadataTest.restoreOriginalFactory();
        }
    }

    /**
     * Simple test to create a service, add quite a few dynamic ports under it, then release
     * the service. This test is mostly for debugging the release logic outside the tests that 
     * create a lot of services to test an OOM isn't produced.
     */
    public void testServiceReleaseSingleServiceDescriptionRelease() {
        try {
            ClientMetadataTest.installCachingFactory();

            QName svcQN = new QName(namespaceURI, svcLocalPart);
            Service svc1 = Service.create(svcQN);
            for (int i = 0; i < 100; i++) {
                QName portQN = new QName(namespaceURI, dynamicPort1 + "_" + i);
                svc1.addPort(portQN, bindingID1, epr1);
            }
            org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc1);
        } finally {
            ClientMetadataTest.restoreOriginalFactory();
        }

    }
    
    /**
     * Create a bunch of services with a bunch of ports under each, closing the service
     * before the next one is created.  This should release the resources for that service.  The
     * number of services and ports created is large enough to cause an OOM if the release isn't
     * being done correctly.
     * 
     * IMPORTANT NOTE!  Due to a change where dynamic ports are not shared across services,
     * it *may* be that too many ports are being added, which could result in an OOM before
     * they can be released.  The OOM does not necessarily indicate that the services are
     * not being released; it may indicate that the given number of dynamic ports, now not
     * shared across services, is too large. Even though the dynamic ports are being relased
     * the memory is not freed up for re-use until garbage collection is run.
     * 
     * 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.  
     */
    public void _DISABLED_testMultipleServiceMultiplePortReleaseLoop() {
        // Create a bunch of different services, make sure the service desc finalizer is called
        try {
            ClientMetadataTest.installCachingFactory();
            for (int i = 0; i < 1000; i++) {
                QName svcQN = new QName(namespaceURI, svcLocalPart + "_" + i);
                Service svc1 = Service.create(svcQN);
                for (int j = 0; j < 100; j++) {
                    QName portQN = new QName(namespaceURI, dynamicPort1 + "_svc_" + i + "_port_" + j);
                    svc1.addPort(portQN, bindingID1, epr1);
                }
                org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc1);
            }
        } finally {
            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 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 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() {
        // Create a bunch of different services, make sure the service desc finalizer is called
        try {
            ClientMetadataTest.installCachingFactory();

            final int MAX_OOM_COUNT = 5;
            int oomCountService = 0;
            for (int i = 0; i < 1000; i++) {
                try {
                    int oomCount = 0;
                    QName svcQN = new QName(namespaceURI, svcLocalPart + "_" + i);
                    System.out.println("Creating service " + svcQN);
                    Service svc1 = Service.create(svcQN);
                    for (int j = 0; j < 200; j++) {
                        try {
                            QName portQN = new QName(namespaceURI, dynamicPort1 + "_svc_" + i + "_port_" + j);
                            System.out.println("Adding port " + portQN);
                            svc1.addPort(portQN, bindingID1, epr1);
                            // Pause every so often to give the garbage collection thread a chance to run
                            if ((j > 0) && (j % 50) == 0) {
                                System.out.println("Pausing port add for GC to run");
                                Thread.sleep(500);
                            }

                        } catch (OutOfMemoryError e) {
                            System.out.println("Caught OOM number " + ++oomCount);
                            if (oomCount <= MAX_OOM_COUNT) {
                                System.out.println("Sleeping to allow for GC after OOM caught");
                                Thread.sleep(15000);
                                System.out.println("Waking up and focing gc");
                                System.gc();
                                System.out.println("gc() method returned; continuing loop");
                            } else {
                                fail ("Maximum OOM count exceeded " + MAX_OOM_COUNT);
                            }
                        }
                    }
                    // don't call release; the finalizer should do it
//                  org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc1);

                    // Pause to give the garbage collection thread a chance to run
                    System.out.println("Pausing service add for GC to run");
                    Thread.sleep(500);
                } catch (OutOfMemoryError e) {
                    System.out.println("Caught Service OOM number " + ++oomCountService);
                    if (oomCountService <= MAX_OOM_COUNT) {
                        System.out.println("Sleeping to allow for GC after Service OOM caught");
                        Thread.sleep(15000);
                        System.out.println("Waking up and forcing gc");
                        System.gc();
                        System.out.println("Forced gc complete, continuing service loop");
                    } else {
                        fail ("Maximum Servcice OOM count exceeded " + MAX_OOM_COUNT);
                    }
                }
            }

        } catch (Throwable t) {
            System.out.println("Test failed, caught: " + t.toString());
            t.printStackTrace();
            fail("Caught throwable " + t);
        } finally {
            ClientMetadataTest.restoreOriginalFactory();
        }
    }
    
    /**
     * Verify that after a service is released, it can be re-used with the same dynamic ports
     * being added 
     */
    public void testServiceReuseDynamicPort() {
        QName svcQN = new QName(namespaceURI, svcLocalPart);
        try {
            ClientMetadataTest.installCachingFactory();

            Service svc1 = Service.create(svcQN);
            QName portQN = new QName(namespaceURI, dynamicPort1);
            svc1.addPort(portQN, bindingID1, epr1);

            // Use internal state to verify all is well
            ServiceDelegate delegate1 = DescriptionTestUtils2.getServiceDelegate(svc1);
            ServiceDescription svcDesc1 = delegate1.getServiceDescription();
            assertNotNull(svcDesc1);
            EndpointDescription epDesc1= svcDesc1.getEndpointDescription(portQN, delegate1);
            assertNotNull(epDesc1);
            AxisService axisService1 = epDesc1.getAxisService();
            assertNotNull(axisService1);
            AxisConfiguration axisConfig1 = svcDesc1.getAxisConfigContext().getAxisConfiguration();
            HashMap axisServices1 = axisConfig1.getServices();
            assertEquals(1, axisServices1.size());

            // Close the delegate, which should release resources and remove objects from caches
            org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc1);
            
            Service svc2 = Service.create(svcQN);
            svc2.addPort(portQN, bindingID1, epr1);

            // Use internal state to verify all is well; compare to values from the first time
            // around to make sure the cache values for things like the ServiceDescription got 
            // cleared out when the last ServiceDelegate (in the test the only one) relesaed
            // the resources.
            ServiceDelegate delegate2 = DescriptionTestUtils2.getServiceDelegate(svc2);
            assertNotSame(delegate1, delegate2);
            ServiceDescription svcDesc2 = delegate2.getServiceDescription();
            assertNotNull(svcDesc2);
            assertNotSame(svcDesc1, svcDesc2);
            EndpointDescription epDesc2= svcDesc2.getEndpointDescription(portQN, delegate2);
            assertNotNull(epDesc2);
            assertNotSame(epDesc1, epDesc2);
            AxisService axisService2 = epDesc2.getAxisService();
            assertNotNull(axisService2);
            assertNotSame(axisService1, axisService2);
            AxisConfiguration axisConfig2 = svcDesc2.getAxisConfigContext().getAxisConfiguration();
            HashMap axisServices2 = axisConfig2.getServices();
            assertEquals(1, axisServices2.size());
            
            // Verify the service from the map and EndpointDesc are the same
            // Since there's only one element in the map, we can get it directly off the iterator
            assertSame(axisService2, ((Map.Entry) axisServices2.entrySet().iterator().next()).getValue());

        } finally {
            ClientMetadataTest.restoreOriginalFactory();
        }
        
    }
    
    /**
     * Verify that if multiple service are sharing a service description, the release of
     * resources does not happen on the first close.
     */
    public void testMultipleServiceMultiplePortRelease() {
        QName svcQN = new QName(namespaceURI, svcLocalPart);
        try {
            ClientMetadataTest.installCachingFactory();
            Service svc1 = Service.create(svcQN);
            Service svc2 = Service.create(svcQN);

            QName portQN1 = new QName(namespaceURI, dynamicPort1);
            QName portQN2 = new QName(namespaceURI, dynamicPort1 + "_2");
            svc1.addPort(portQN1,bindingID1, epr1);
            svc1.addPort(portQN2, bindingID1, epr1);

            svc2.addPort(portQN1,bindingID1, epr1);
            svc2.addPort(portQN2, bindingID1, epr1);
            
            ServiceDelegate sd1 = DescriptionTestUtils2.getServiceDelegate(svc1);
            ServiceDelegate sd2 = DescriptionTestUtils2.getServiceDelegate(svc2);
            assertNotSame(sd1, sd2);
            
            ServiceDescription svcDesc1 = sd1.getServiceDescription();
            ServiceDescription svcDesc2 = sd2.getServiceDescription();
            AxisConfiguration axisConfig = svcDesc1.getAxisConfigContext().getAxisConfiguration();
            assertSame(svcDesc1, svcDesc2);
            
            EndpointDescription epDesc1_port1 = svcDesc1.getEndpointDescription(portQN1, sd1);
            EndpointDescription epDesc2_port1 = svcDesc1.getEndpointDescription(portQN1, sd2);
            assertNotSame(epDesc1_port1, epDesc2_port1);
            AxisService axisSvc1_port1 = epDesc1_port1.getAxisService();
            AxisService axisSvc2_port1 = epDesc2_port1.getAxisService();
            assertNotSame(axisSvc1_port1, axisSvc2_port1);

            EndpointDescription epDesc1_port2 = svcDesc1.getEndpointDescription(portQN2, sd1);
            EndpointDescription epDesc2_port2 = svcDesc1.getEndpointDescription(portQN2, sd2);
            assertNotSame(epDesc1_port2, epDesc2_port2);
            AxisService axisSvc1_port2 = epDesc1_port2.getAxisService();
            AxisService axisSvc2_port2 = epDesc2_port2.getAxisService();
            assertNotSame(axisSvc1_port2, axisSvc2_port2);

            // 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);
            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);
            
            // Add a third, should use the same
            Service svc3 = Service.create(svcQN);
            svc3.addPort(portQN1,bindingID1, epr1);
            svc3.addPort(portQN2, bindingID1, epr1);
            ServiceDelegate sd3 = DescriptionTestUtils2.getServiceDelegate(svc3);
            assertNotSame(sd2, sd3);
            ServiceDescription svcDesc3 = sd3.getServiceDescription();
            assertSame(svcDesc2_afterClose, svcDesc3);
            EndpointDescription epDesc3_port1 = svcDesc3.getEndpointDescription(portQN1, sd3);
            assertNotSame(epDesc3_port1, epDesc2_port1_afterClose);
            EndpointDescription epDesc3_port2 = svcDesc3.getEndpointDescription(portQN2, sd3);
            assertNotSame(epDesc3_port2, epDesc2_port2_afterClose);

            // Close the 2nd delegate and make sure cahced objects are still there
            // since there's a 3rd delegate now
            org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc2);

            ServiceDescription svcDesc3_afterClose = sd3.getServiceDescription();
            assertSame(svcDesc3, svcDesc3_afterClose);
            EndpointDescription epDesc3_port1_afterClose = 
                svcDesc3_afterClose.getEndpointDescription(portQN1, sd3);
            assertSame(epDesc3_port1, epDesc3_port1_afterClose);
            EndpointDescription epDesc3_port2_afterClose = 
                svcDesc3_afterClose.getEndpointDescription(portQN2, sd3);
            assertSame(epDesc3_port2, epDesc3_port2_afterClose);
            
            // 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();
        }
    }
    

    public void testSeviceUseAfterClose() {
        QName svcQN = new QName(namespaceURI, svcLocalPart);
        try {
            ClientMetadataTest.installCachingFactory();

            Service svc1 = Service.create(svcQN);
            QName portQN = new QName(namespaceURI, dynamicPort1);
            svc1.addPort(portQN, bindingID1, epr1);
        
            org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc1);
            
            svc1.addPort(portQN, bindingID1, epr1);
            fail("Should have caught an exception");

        } catch (WebServiceException e) {
            // expected path
        } catch (Exception e) {
            fail("Caught wrong exception " + e);
        } finally {
            ClientMetadataTest.restoreOriginalFactory();
        }
    }

    static final String declared_namespaceURI = "http://description.jaxws.axis2.apache.org";
    static final String declared_svcLocalPart = "svcLocalPart";
    static final String multiPortWsdl = "ClientMetadataMultiPort.wsdl";
    static final String multiPortWsdl_portLocalPart1 = "portLocalPartMulti1";
    static final String multiPortWsdl_portLocalPart2 = "portLocalPartMulti2";
    static final String multiPortWsdl_portLocalPart3 = "portLocalPartMulti3";
    public void testMultipleServiceMultipeDeclaredPorts() {
        QName serviceQName = new QName(declared_namespaceURI , declared_svcLocalPart);
        URL wsdlUrl = getWsdlURL(multiPortWsdl);
        QName portQN1 = new QName(namespaceURI, multiPortWsdl_portLocalPart1);
        QName portQN2 = new QName(namespaceURI, multiPortWsdl_portLocalPart2);
        QName portQN3 = new QName(namespaceURI, multiPortWsdl_portLocalPart3);

        try {
            ClientMetadataTest.installCachingFactory();
            // Open 2 services
            Service svc1 = Service.create(wsdlUrl, serviceQName);
            Service svc2 = Service.create(wsdlUrl, serviceQName);

            ClientMetadataPortSEI svc1_port1 = svc1.getPort(portQN1, ClientMetadataPortSEI.class);
            ClientMetadataPortSEI svc1_port2 = svc1.getPort(portQN2, ClientMetadataPortSEI.class);
            ClientMetadataPortSEI svc1_port3 = svc1.getPort(portQN3, ClientMetadataPortSEI.class);

            ClientMetadataPortSEI svc2_port1 = svc2.getPort(portQN1, ClientMetadataPortSEI.class);
            ClientMetadataPortSEI svc2_port2 = svc2.getPort(portQN2, ClientMetadataPortSEI.class);
            ClientMetadataPortSEI svc2_port3 = svc2.getPort(portQN3, ClientMetadataPortSEI.class);

            ServiceDelegate sd1 = DescriptionTestUtils2.getServiceDelegate(svc1);
            ServiceDelegate sd2 = DescriptionTestUtils2.getServiceDelegate(svc2);
            assertNotSame(sd1, sd2);

            ServiceDescription svcDesc1 = sd1.getServiceDescription();
            ServiceDescription svcDesc2 = sd2.getServiceDescription();
            AxisConfiguration axisConfig = svcDesc1.getAxisConfigContext().getAxisConfiguration();
            assertSame(svcDesc1, svcDesc2);
            
            EndpointDescription epDesc1_port1 = svcDesc1.getEndpointDescription(portQN1, sd1);
            EndpointDescription epDesc2_port1 = svcDesc1.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);

            EndpointDescription epDesc1_port2 = svcDesc1.getEndpointDescription(portQN2, sd1);
            EndpointDescription epDesc2_port2 = svcDesc1.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);

            // First close should NOT cleanup the endpoints since the other service is
            // still using them.
            org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(svc1);
            
            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);

            // Add a third, should use the same
            Service svc3 = Service.create(wsdlUrl, serviceQName);
            ClientMetadataPortSEI svc3_port1 = svc3.getPort(portQN1, ClientMetadataPortSEI.class);
            ClientMetadataPortSEI svc3_port2 = svc3.getPort(portQN2, ClientMetadataPortSEI.class);
            ServiceDelegate sd3 = DescriptionTestUtils2.getServiceDelegate(svc3);
            assertNotSame(sd2, sd3);
            ServiceDescription svcDesc3 = sd3.getServiceDescription();
            assertSame(svcDesc2_afterClose, svcDesc3);
            EndpointDescription epDesc3_port1 = svcDesc3.getEndpointDescription(portQN1, sd3);
            assertSame(epDesc3_port1, epDesc2_port1_afterClose);
            EndpointDescription epDesc3_port2 = svcDesc3.getEndpointDescription(portQN2, sd3);
            assertSame(epDesc3_port2, epDesc2_port2_afterClose);

            // 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);

            ServiceDescription svcDesc3_afterClose = sd3.getServiceDescription();
            assertSame(svcDesc3, svcDesc3_afterClose);
            EndpointDescription epDesc3_port1_afterClose = 
                svcDesc3_afterClose.getEndpointDescription(portQN1, sd3);
            assertSame(epDesc3_port1, epDesc3_port1_afterClose);
            EndpointDescription epDesc3_port2_afterClose = 
                svcDesc3_afterClose.getEndpointDescription(portQN2, sd3);
            assertSame(epDesc3_port2, epDesc3_port2_afterClose);
            
            // 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();
        }

    }

    static final String GENERATED_SERVICE_WSDL = "ClientMetadata.wsdl";
    static final String GENERATED_SERVICE_NS = "http://description.jaxws.axis2.apache.org";
    static final String GENERATED_SERVICE_LP = "svcLocalPart";
    
    public void testGeneratedServiceRelease() {
        try {
            ClientMetadataTest.installCachingFactory();
            
            ClientMetadataGeneratedService genSvc = new ClientMetadataGeneratedService();
            assertNotNull(genSvc);
            ClientMetadataPortSEI port = genSvc.getPort(ClientMetadataPortSEI.class);
            assertNotNull(port);
            
            // User internal state to verify the port information before and after the close
            ServiceDelegate delegate = DescriptionTestUtils2.getServiceDelegate(genSvc);
            ServiceDescription svcDesc = delegate.getServiceDescription();
            EndpointDescription[] epDescArray= svcDesc.getEndpointDescriptions();
            assertNotNull(epDescArray);
            assertEquals(1, epDescArray.length);
            
            AxisConfiguration axisConfig = svcDesc.getAxisConfigContext().getAxisConfiguration();
            HashMap axisServices = axisConfig.getServices();
            assertEquals(1, axisServices.size());

            org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(genSvc);

            axisServices = axisConfig.getServices();
            assertEquals(0, axisServices.size());

            epDescArray= svcDesc.getEndpointDescriptions();
            assertEquals(0, epDescArray.length);                
        } finally {
            ClientMetadataTest.restoreOriginalFactory();
        }
    }
    
    public void testGeneratedServiceRelaseLoop() {
        // Create a bunch of different services, make sure the service desc finalizer is called
        try {
            ClientMetadataTest.installCachingFactory();

            for (int i = 0; i < 1000; i++) {
                ClientMetadataGeneratedService genSvc = new ClientMetadataGeneratedService();
                ClientMetadataPortSEI port = genSvc.getPort(ClientMetadataPortSEI.class);
                org.apache.axis2.jaxws.spi.ServiceDelegate.releaseService(genSvc);
            }
        } finally {
            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 + "_5");

            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 not shared across the 
            // delegate instances
            
            EndpointDescription epDesc1_port1 = svcDesc.getEndpointDescription(portQN1, sd1);
            EndpointDescription epDesc2_port1 = svcDesc.getEndpointDescription(portQN1, sd2);
            assertNotSame(epDesc1_port1, epDesc2_port1);
            AxisService axisSvc1_port1 = epDesc1_port1.getAxisService();
            AxisService axisSvc2_port1 = epDesc2_port1.getAxisService();
            assertNotSame(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);
            assertNotSame(epDesc1_port2, epDesc2_port2);
            AxisService axisSvc1_port2 = epDesc1_port2.getAxisService();
            AxisService axisSvc2_port2 = epDesc2_port2.getAxisService();
            assertNotSame(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 all ports added to this service; 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
    // =============================================================================================
    /**
     * Given a simple file name (with no base dictory or path), returns a URL to the WSDL file
     * with the base directory and path prepended.
     * 
     * @param wsdlFileName
     * @return
     */
    static URL getWsdlURL(String wsdlFileName) {
        URL url = null;
        String wsdlLocation = getWsdlLocation(wsdlFileName);
        try {
            File file = new File(wsdlLocation);
            url = file.toURI().toURL();
        } catch (MalformedURLException e) {
            e.printStackTrace();
            fail("Exception converting WSDL file to URL: " + e.toString());
        }
        return url;
    }

    /**
     * Prepends the base directory and the path where the test WSDL lives to a filename.
     * @param wsdlFileName
     * @return
     */
    static String getWsdlLocation(String wsdlFileName) {
        String wsdlLocation = null;
        String baseDir = System.getProperty("basedir",".");
        wsdlLocation = baseDir + "/test-resources/wsdl/" + wsdlFileName;
        return wsdlLocation;
    }
}

@WebService(name="EchoMessagePortType", targetNamespace="http://description.jaxws.axis2.apache.org")
interface ClientMetadataPortSEI {
    public String echoMessage(String string);
}

@WebServiceClient()
class ClientMetadataGeneratedService extends javax.xml.ws.Service {
    public ClientMetadataGeneratedService() {
        super(ReleaseServiceTests.getWsdlURL(ReleaseServiceTests.GENERATED_SERVICE_WSDL),
              new QName(ReleaseServiceTests.GENERATED_SERVICE_NS, ReleaseServiceTests.GENERATED_SERVICE_LP));
    }
    public ClientMetadataGeneratedService(URL wsdlLocation, QName serviceName) {
        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++;
        
    }
}
