/**
* 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.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import javax.jws.WebService;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Provider;
import javax.xml.ws.Service.Mode;
import javax.xml.ws.ServiceMode;
import javax.xml.xpath.XPathConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.bus.spring.SpringBusFactory;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.greeter_control.Greeter;
import org.apache.cxf.greeter_control.GreeterService;
import org.apache.cxf.helpers.XPathUtils;
import org.apache.cxf.staxutils.StaxUtils;
import org.apache.cxf.systest.ws.util.ConnectionHelper;
import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
import org.apache.cxf.ws.rm.RMManager;
import org.junit.After;
import org.junit.Test;
/**
* Tests the operation of InOrder delivery assurance for one-way messages to the server.
*/
public class DeliveryAssuranceOnewayTest extends AbstractBusClientServerTestBase {
public static final String PORT = allocatePort(DeliveryAssuranceOnewayTest.class);
private static final String GREETER_ADDRESS
= "http://localhost:" + PORT + "/SoapContext/GreeterPort";
private static final Logger LOG = LogUtils.getLogger(DeliveryAssuranceOnewayTest.class);
private Bus serverBus;
private Endpoint endpoint;
private Bus greeterBus;
private Greeter greeter;
@After
public void tearDown() throws Exception {
try {
stopClient();
} catch (Throwable t) {
//ignore
}
try {
stopServer();
} catch (Throwable t) {
//ignore
}
Thread.sleep(100);
}
@Test
public void testAtLeastOnce() throws Exception {
testOnewayAtLeastOnce(null);
}
@Test
public void testAtLeastOnceAsyncExecutor() throws Exception {
testOnewayAtLeastOnce(Executors.newSingleThreadExecutor());
}
private void testOnewayAtLeastOnce(Executor executor) throws Exception {
init("org/apache/cxf/systest/ws/rm/atleastonce.xml", executor);
greeterBus.getOutInterceptors().add(new MessageLossSimulator());
RMManager manager = greeterBus.getExtension(RMManager.class);
manager.getConfiguration().setBaseRetransmissionInterval(new Long(1000));
String[] callArgs = new String[] {"one", "two", "three", "four", "five", "six",
"seven", "eight", "nine"};
for (int i = 0; i < callArgs.length; i++) {
greeter.greetMeOneWay(callArgs[i]);
}
awaitMessages(callArgs.length, 1, 3000);
List<String> actualArgs = GreeterProvider.CALL_ARGS;
int checkCount = 0;
for (int i = 0; i < callArgs.length; i++) {
boolean match = false;
for (int j = 0; j < actualArgs.size(); j++) {
if (actualArgs.get(j).equals(callArgs[i])) {
match = true;
break;
}
}
if (!match) {
if (checkCount > 20) {
fail("No match for request " + callArgs[i]);
}
checkCount++;
awaitMessages(callArgs.length, 1, 250);
i--;
}
}
assertTrue("Too few messages " + actualArgs.size(), callArgs.length <= actualArgs.size());
}
@Test
public void testAtMostOnce() throws Exception {
testOnewayAtMostOnce(null);
}
@Test
public void testAtMostOnceAsyncExecutor() throws Exception {
testOnewayAtMostOnce(Executors.newSingleThreadExecutor());
}
private void testOnewayAtMostOnce(Executor executor) throws Exception {
init("org/apache/cxf/systest/ws/rm/atmostonce.xml", executor);
greeterBus.getOutInterceptors().add(new MessageLossSimulator());
RMManager manager = greeterBus.getExtension(RMManager.class);
manager.getConfiguration().setBaseRetransmissionInterval(new Long(2000));
String[] callArgs = new String[] {"one", "two", "three", "four"};
for (int i = 0; i < callArgs.length; i++) {
greeter.greetMeOneWay(callArgs[i]);
}
awaitMessages(callArgs.length, 1000, 60000);
List<String> actualArgs = GreeterProvider.CALL_ARGS;
assertTrue("Too many messages", callArgs.length >= actualArgs.size());
for (int i = 0; i < actualArgs.size() - 1; i++) {
for (int j = i + 1; j < actualArgs.size(); j++) {
if (actualArgs.get(j).equals(actualArgs.get(i))) {
fail("Message received more than once " + callArgs[i]);
}
}
}
}
@Test
public void testExactlyOnce() throws Exception {
testOnewayExactlyOnce(null);
}
@Test
public void testExactlyOnceAsyncExecutor() throws Exception {
testOnewayExactlyOnce(Executors.newSingleThreadExecutor());
}
private void testOnewayExactlyOnce(Executor executor) throws Exception {
init("org/apache/cxf/systest/ws/rm/exactlyonce.xml", executor);
greeterBus.getOutInterceptors().add(new MessageLossSimulator());
RMManager manager = greeterBus.getExtension(RMManager.class);
manager.getConfiguration().setBaseRetransmissionInterval(new Long(2000));
String[] callArgs = new String[] {"one", "two", "three", "four"};
for (int i = 0; i < callArgs.length; i++) {
greeter.greetMeOneWay(callArgs[i]);
}
awaitMessages(callArgs.length, 1000, 60000);
List<String> actualArgs = GreeterProvider.CALL_ARGS;
assertEquals("Wrong message count", callArgs.length, actualArgs.size());
for (int i = 0; i < callArgs.length; i++) {
boolean match = false;
for (int j = 0; j < actualArgs.size(); j++) {
if (actualArgs.get(j).equals(callArgs[i])) {
match = true;
break;
}
}
if (!match) {
fail("No match for request " + callArgs[i]);
}
}
}
@Test
public void testInOrder() throws Exception {
testOnewayInOrder(null);
}
@Test
public void testInOrderAsyncExecutor() throws Exception {
testOnewayInOrder(Executors.newSingleThreadExecutor());
}
private void testOnewayInOrder(Executor executor) throws Exception {
init("org/apache/cxf/systest/ws/rm/inorder.xml", executor);
greeterBus.getOutInterceptors().add(new MessageLossSimulator());
RMManager manager = greeterBus.getExtension(RMManager.class);
manager.getConfiguration().setBaseRetransmissionInterval(new Long(2000));
String[] callArgs = new String[] {"one", "two", "three", "four"};
for (int i = 0; i < callArgs.length; i++) {
greeter.greetMeOneWay(callArgs[i]);
}
awaitMessages(callArgs.length - 2, 1000, 60000);
List<String> actualArgs = GreeterProvider.CALL_ARGS;
int argNum = 0;
for (String actual : actualArgs) {
while (argNum < callArgs.length && !actual.equals(callArgs[argNum])) {
argNum++;
}
assertTrue("Message out of order", argNum < callArgs.length);
}
}
@Test
public void testAtMostOnceInOrder() throws Exception {
testOnewayAtMostOnceInOrder(null);
}
@Test
public void testAtMostOnceInOrderAsyncExecutor() throws Exception {
testOnewayAtMostOnceInOrder(Executors.newSingleThreadExecutor());
}
private void testOnewayAtMostOnceInOrder(Executor executor) throws Exception {
init("org/apache/cxf/systest/ws/rm/atmostonce-inorder.xml", executor);
greeterBus.getOutInterceptors().add(new MessageLossSimulator());
RMManager manager = greeterBus.getExtension(RMManager.class);
manager.getConfiguration().setBaseRetransmissionInterval(new Long(2000));
String[] callArgs = new String[] {"one", "two", "three", "four"};
for (int i = 0; i < callArgs.length; i++) {
greeter.greetMeOneWay(callArgs[i]);
}
awaitMessages(callArgs.length - 2, 1000, 60000);
List<String> actualArgs = GreeterProvider.CALL_ARGS;
assertTrue("Too many messages", callArgs.length >= actualArgs.size());
int argNum = 0;
for (String actual : actualArgs) {
while (argNum < callArgs.length && !actual.equals(callArgs[argNum])) {
argNum++;
}
assertTrue("Message out of order", argNum < callArgs.length);
}
}
@Test
public void testExactlyOnceInOrder() throws Exception {
testOnewayExactlyOnceInOrder(null);
}
@Test
public void testExactlyOnceInOrderAsyncExecutor() throws Exception {
testOnewayExactlyOnceInOrder(Executors.newSingleThreadExecutor());
}
private void testOnewayExactlyOnceInOrder(Executor executor) throws Exception {
init("org/apache/cxf/systest/ws/rm/exactlyonce-inorder.xml", executor);
greeterBus.getOutInterceptors().add(new MessageLossSimulator());
RMManager manager = greeterBus.getExtension(RMManager.class);
manager.getConfiguration().setBaseRetransmissionInterval(new Long(2000));
String[] callArgs = new String[] {"one", "two", "three", "four"};
for (int i = 0; i < callArgs.length; i++) {
greeter.greetMeOneWay(callArgs[i]);
}
awaitMessages(callArgs.length, 1000, 60000);
List<String> actualArgs = GreeterProvider.CALL_ARGS;
assertEquals("Wrong number of messages", callArgs.length, actualArgs.size());
int argNum = 0;
for (String actual : actualArgs) {
while (argNum < callArgs.length && !actual.equals(callArgs[argNum])) {
argNum++;
}
assertTrue("Message out of order", argNum < callArgs.length);
}
}
// --- test utilities ---
private void init(String cfgResource, Executor executor) {
SpringBusFactory bf = new SpringBusFactory();
initServer(bf, cfgResource);
initGreeterBus(bf, cfgResource);
initProxy(executor);
}
private void initServer(SpringBusFactory bf, String cfgResource) {
String derbyHome = System.getProperty("derby.system.home");
try {
synchronized (GreeterProvider.CALL_ARGS) {
GreeterProvider.CALL_ARGS.clear();
}
System.setProperty("derby.system.home", derbyHome + "-server");
serverBus = bf.createBus(cfgResource);
BusFactory.setDefaultBus(serverBus);
LOG.info("Initialised bus " + serverBus + " with cfg file resource: " + cfgResource);
LOG.info("serverBus inInterceptors: " + serverBus.getInInterceptors());
endpoint = Endpoint.publish(GREETER_ADDRESS, new GreeterProvider());
} finally {
if (derbyHome != null) {
System.setProperty("derby.system.home", derbyHome);
} else {
System.clearProperty("derby.system.home");
}
}
}
private void initGreeterBus(SpringBusFactory bf,
String cfgResource) {
greeterBus = bf.createBus(cfgResource);
BusFactory.setDefaultBus(greeterBus);
LOG.fine("Initialised greeter bus with configuration: " + cfgResource);
}
private void initProxy(Executor executor) {
GreeterService gs = new GreeterService();
if (null != executor) {
gs.setExecutor(executor);
}
greeter = gs.getGreeterPort();
try {
updateAddressPort(greeter, PORT);
} catch (Exception e) {
//ignore
}
LOG.fine("Created greeter client.");
ConnectionHelper.setKeepAliveConnection(greeter, false);
}
private void stopClient() {
if (null != greeterBus) {
//ensure we close the decoupled destination of the conduit,
//so that release the port if the destination reference count hit zero
if (greeter != null) {
ClientProxy.getClient(greeter).getConduit().close();
}
greeterBus.shutdown(true);
greeter = null;
greeterBus = null;
}
}
private void stopServer() {
if (null != endpoint) {
LOG.info("Stopping Greeter endpoint");
endpoint.stop();
} else {
LOG.info("No endpoint active.");
}
endpoint = null;
if (null != serverBus) {
serverBus.shutdown(true);
serverBus = null;
}
}
/**
* @param nExpectedIn number of messages to wait for
* @param delay added delay before return (in case more are coming)
* @param timeout maximum time to wait for expected messages
*/
private void awaitMessages(int nExpectedIn, int delay, int timeout) {
int waited = 0;
int nIn = 0;
long start = System.currentTimeMillis();
while (waited <= timeout) {
synchronized (GreeterProvider.CALL_ARGS) {
nIn = GreeterProvider.CALL_ARGS.size();
}
if (nIn >= nExpectedIn) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
// ignore
}
waited += 100;
}
// we'll use the delay amount or at least double the original amount
// of time, which ever is less, to wait for additional messages.
long total = System.currentTimeMillis() - start;
if (delay > total) {
delay = (int)total;
}
try {
Thread.sleep(delay);
} catch (InterruptedException ex) {
// ignore
}
}
@WebService(serviceName = "GreeterService",
portName = "GreeterPort",
targetNamespace = "http://cxf.apache.org/greeter_control",
wsdlLocation = "/wsdl/greeter_control.wsdl")
@ServiceMode(Mode.PAYLOAD)
public static class GreeterProvider implements Provider<Source> {
public static final List<String> CALL_ARGS = new ArrayList<>();
public Source invoke(Source obj) {
Node el;
try {
el = StaxUtils.read(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (el instanceof Document) {
el = ((Document)el).getDocumentElement();
}
Map<String, String> ns = new HashMap<>();
ns.put("ns", "http://cxf.apache.org/greeter_control/types");
XPathUtils xp = new XPathUtils(ns);
String s = (String)xp.getValue("/ns:greetMe/ns:requestType",
el,
XPathConstants.STRING);
if (s == null || "".equals(s)) {
s = (String)xp.getValue("/ns:greetMeOneWay/ns:requestType",
el,
XPathConstants.STRING);
synchronized (CALL_ARGS) {
CALL_ARGS.add(s);
}
return null;
} else {
synchronized (CALL_ARGS) {
CALL_ARGS.add(s);
}
String resp =
"<greetMeResponse "
+ "xmlns=\"http://cxf.apache.org/greeter_control/types\">"
+ "<responseType>" + s.toUpperCase() + "</responseType>"
+ "</greetMeResponse>";
return new StreamSource(new StringReader(resp));
}
}
}
}