/**
 * 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.cxf.systest.ws.rm;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Service;
import javax.xml.ws.handler.MessageContext;

import org.w3c.dom.Document;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.bus.spring.SpringBusFactory;
import org.apache.cxf.greeter_control.Greeter;
import org.apache.cxf.greeter_control.GreeterService;
import org.apache.cxf.systest.ws.util.ConnectionHelper;
import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
import org.apache.cxf.testutil.common.TestUtil;
import org.apache.cxf.ws.rm.RMEndpoint;
import org.apache.cxf.ws.rm.RMManager;
import org.apache.cxf.ws.rm.manager.AcksPolicyType;

import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;


/**
 * 
 */
public class SequenceTimeoutTest extends AbstractBusClientServerTestBase {
    public static final String PORT = TestUtil.getPortNumber(SequenceTimeoutTest.class);
    private static final String ADDRESS = "http://localhost:" + PORT + "/SoapContext/GreeterPort";
    private static final QName GREETME_NAME
        = new QName("http://cxf.apache.org/greeter_control", "greetMe");
    private static final QName GREETME_SERVICE_NAME
        = new QName("http://cxf.apache.org/greeter_control", "GreeterService");

    private static RMManager rmManager;
    
    private Bus greeterBus;
    private Greeter greeter;


    public static class Server extends AbstractBusTestServerBase {
        Endpoint endpoint;

        protected void run()  {
            SpringBusFactory bf = new SpringBusFactory();
            System.setProperty("db.name", "rdbm");
            Bus bus = bf.createBus("org/apache/cxf/systest/ws/rm/rminterceptors.xml");
            System.clearProperty("db.name");
            BusFactory.setDefaultBus(bus);
            
            setBus(bus);
            
            rmManager = bus.getExtension(RMManager.class);
            rmManager.getConfiguration().setInactivityTimeout(1000L);

            //System.out.println("Created control bus " + bus);
            GreeterImpl greeterImplementor = new GreeterImpl();
            endpoint = Endpoint.publish(ADDRESS, greeterImplementor);

            BusFactory.setDefaultBus(null);
            BusFactory.setThreadDefaultBus(null);
        }

        public void tearDown() throws Exception {
            endpoint.stop();
        }
    }
    
    
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        assertTrue("server did not launch correctly", launchServer(Server.class, true));
    }


    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    private void init(String cfgResource, boolean useDecoupledEndpoint, boolean useDispatchClient) {
        init(cfgResource, useDecoupledEndpoint, useDispatchClient, null);
    }

    private void init(String cfgResource,
                      boolean useDecoupledEndpoint,
                      boolean useDispatchClient,
                      Executor executor) {

        SpringBusFactory bf = new SpringBusFactory();
        initGreeterBus(bf, cfgResource);
        if (useDispatchClient) {
            initDispatch();
        } else {
            initProxy(useDecoupledEndpoint, executor);
        }
    }
    private void initGreeterBus(SpringBusFactory bf,
                                String cfgResource) {
        greeterBus = bf.createBus(cfgResource);
        BusFactory.setDefaultBus(greeterBus);
    }
    
    
    private Dispatch<DOMSource> initDispatch() {
        GreeterService gs = new GreeterService();
        Dispatch<DOMSource> dispatch = gs.createDispatch(GreeterService.GreeterPort,
                                                         DOMSource.class,
                                                         Service.Mode.MESSAGE);
        try {
            updateAddressPort(dispatch, PORT);
        } catch (Exception e) {
            //ignore
        }
        dispatch.getRequestContext().put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.FALSE);
        dispatch.getRequestContext().put(MessageContext.WSDL_OPERATION, GREETME_NAME);

        return dispatch;
    }

    private void initProxy(boolean useDecoupledEndpoint, Executor executor) {
        GreeterService gs = new GreeterService();

        if (null != executor) {
            gs.setExecutor(executor);
        }

        greeter = gs.getGreeterPort();
        try {
            updateAddressPort(greeter, PORT);
        } catch (Exception e) {
            //ignore
        }

        ConnectionHelper.setKeepAliveConnection(greeter, true);
    }
    @Test
    public void testTimeout() throws Exception {
        init("org/apache/cxf/systest/ws/rm/rminterceptors.xml", true, true);

        List<Dispatch<DOMSource>> dispatches = new ArrayList<>(5);
        int count = 5;
        for (int x = 0; x < count; x++) {
            Dispatch<DOMSource> dispatch = initDispatch();
            AcksPolicyType ap = new AcksPolicyType();            
            //don't send the acks to cause a memory leak - CXF-7096
            ap.setImmediaAcksTimeout(500000L);
            greeterBus.getExtension(RMManager.class).getDestinationPolicy().setAcksPolicy(ap);          
            dispatch.invoke(getDOMRequest("One"));
            dispatches.add(dispatch);
        }
        RMEndpoint ep = rmManager.findReliableEndpoint(GREETME_SERVICE_NAME);
        Assert.assertNotNull(ep);
        Assert.assertEquals(count, ep.getDestination().getAllSequences().size());
        Assert.assertEquals(count, ep.getSource().getAllSequences().size());
        Thread.sleep(2500);
        System.gc();
        Assert.assertEquals(0, ep.getDestination().getAllSequences().size());
        Assert.assertEquals(0, ep.getSource().getAllSequences().size());
        try {
            dispatches.get(0).invoke(getDOMRequest("One"));
            fail("The sequence should have been terminated");
        } catch (Throwable t) {
            //expected
            Assert.assertTrue(t.getMessage().contains("not a known Sequence identifier"));
        }
        rmManager.getStore();
    }
    private DOMSource getDOMRequest(String n)
        throws Exception {
        InputStream is =
            getClass().getResourceAsStream("twoway"
                                           + "Req" + n + ".xml");
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document newDoc = builder.parse(is);
        return new DOMSource(newDoc);
    }
}
