/**
* 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.http;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.bus.spring.SpringBusFactory;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
import org.apache.hello_world.Greeter;
import org.apache.hello_world.services.SOAPService;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* This class tests several issues and Conduit policies based
* on a set up of redirecting servers.
* <pre>
*
* Http Redirection:
*
* Rethwel(http:9004) ----> Mortimer (http:9000)
*
* Redirect Loop:
*
* Hurlon (http:9006) ----> Abost(http:9007) ----\
* ^ |
* |-------------------------------------------/
*/
public class HTTPConduitTest extends AbstractBusClientServerTestBase {
private static final boolean IN_PROCESS = true;
private static List<String> servers = new ArrayList<>();
private static Map<String, String> addrMap = new TreeMap<String, String>();
private final QName serviceName =
new QName("http://apache.org/hello_world", "SOAPService");
private final QName rethwelQ =
new QName("http://apache.org/hello_world", "Rethwel");
private final QName mortimerQ =
new QName("http://apache.org/hello_world", "Mortimer");
private final QName hurlonQ =
new QName("http://apache.org/hello_world", "Hurlon");
public HTTPConduitTest() {
}
public static String getPort(String s) {
return BusServer.PORTMAP.get(s);
}
@BeforeClass
public static void allocatePorts() {
BusServer.resetPortMap();
addrMap.clear();
addrMap.put("Mortimer", "http://localhost:" + getPort("PORT0") + "/");
addrMap.put("Rethwel", "http://localhost:" + getPort("PORT1") + "/");
addrMap.put("Abost", "http://localhost:" + getPort("PORT2") + "/");
addrMap.put("Hurlon", "http://localhost:" + getPort("PORT3") + "/");
servers.clear();
}
/**
* This function is used to start up a server. It only "starts" a
* server if it hasn't been started before, hence its static nature.
* <p>
* This approach is used to start the needed servers for a particular test
* instead of starting them all in "startServers". This single needed
* server approach allieviates the pain in starting them all just to run
* a particular test in the debugger.
*/
public synchronized boolean startServer(String name) {
if (servers.contains(name)) {
return true;
}
Bus bus = BusFactory.getThreadDefaultBus(false);
URL serverC =
Server.class.getResource(name + ".cxf");
BusFactory.setDefaultBus(null);
BusFactory.setThreadDefaultBus(null);
boolean server = launchServer(Server.class, null,
new String[] {
name,
addrMap.get(name),
serverC.toString() },
IN_PROCESS);
if (server) {
servers.add(name);
}
BusFactory.setDefaultBus(null);
BusFactory.setThreadDefaultBus(bus);
return server;
}
@AfterClass
public static void cleanUp() {
Bus b = BusFactory.getDefaultBus(false);
if (b != null) {
b.shutdown(true);
}
b = BusFactory.getThreadDefaultBus(false);
if (b != null) {
b.shutdown(true);
}
}
//methods that a subclass can override to inject a Proxy into the flow
//and assert the proxy was appropriately called
public void configureProxy(Client c) {
}
public void resetProxyCount() {
}
public void assertProxyRequestCount(int i) {
}
private Greeter getMortimerGreeter() throws MalformedURLException {
URL wsdl = getClass().getResource("greeting.wsdl");
assertNotNull("WSDL is null", wsdl);
SOAPService service = new SOAPService(wsdl, serviceName);
assertNotNull("Service is null", service);
Greeter mortimer = service.getPort(mortimerQ, Greeter.class);
assertNotNull("Port is null", mortimer);
updateAddressPort(mortimer, getPort("PORT0"));
configureProxy(ClientProxy.getClient(mortimer));
return mortimer;
}
@Test
public void testBasicConnection() throws Exception {
startServer("Mortimer");
Greeter mortimer = getMortimerGreeter();
String answer = mortimer.sayHi();
answer = mortimer.sayHi();
answer = mortimer.sayHi();
assertTrue("Unexpected answer: " + answer,
"Bonjour from Mortimer".equals(answer));
assertProxyRequestCount(3);
}
@Test
public void testLogLevelIssueCXF3466() throws Exception {
startServer("Mortimer");
Greeter mortimer = getMortimerGreeter();
Logger rootLogger = LogManager.getLogManager().getLogger("");
Level oldLevel = rootLogger.getLevel();
rootLogger.setLevel(Level.FINE);
try {
// Will throw exception Stream is closed if bug is present
mortimer.sayHi();
} finally {
rootLogger.setLevel(oldLevel);
}
assertProxyRequestCount(1);
}
/**
* This methods tests that a conduit that is not configured
* to follow redirects will not. The default is not to
* follow redirects.
* Rethwel redirects to Mortimer.
*
* Note: Unfortunately, the invocation will
* "fail" for any number of other reasons.
*/
@Test
public void testHttp2HttpRedirectFail() throws Exception {
startServer("Mortimer");
startServer("Rethwel");
URL wsdl = getClass().getResource("greeting.wsdl");
assertNotNull("WSDL is null", wsdl);
SOAPService service = new SOAPService(wsdl, serviceName);
assertNotNull("Service is null", service);
Greeter rethwel = service.getPort(rethwelQ, Greeter.class);
assertNotNull("Port is null", rethwel);
updateAddressPort(rethwel, getPort("PORT1"));
configureProxy(ClientProxy.getClient(rethwel));
String answer = null;
try {
answer = rethwel.sayHi();
fail("Redirect didn't fail. Got answer: " + answer);
} catch (Exception e) {
//e.printStackTrace();
}
assertProxyRequestCount(1);
}
/**
* We use this class to reset the default bus.
* Note: This may not always work in the future.
* I was lucky in that "defaultBus" is actually a
* protected static.
*/
class DefaultBusFactory extends SpringBusFactory {
public Bus createBus(URL config) {
Bus bus = super.createBus(config, true);
BusFactory.setDefaultBus(bus);
BusFactory.setThreadDefaultBus(bus);
return bus;
}
}
/**
* This method tests if http to http redirects work.
* Rethwel redirects to Mortimer.
*/
@Test
public void testHttp2HttpRedirect() throws Exception {
startServer("Mortimer");
startServer("Rethwel");
URL config = getClass().getResource("Http2HttpRedirect.cxf");
// We go through the back door, setting the default bus.
new DefaultBusFactory().createBus(config);
URL wsdl = getClass().getResource("greeting.wsdl");
assertNotNull("WSDL is null", wsdl);
SOAPService service = new SOAPService(wsdl, serviceName);
assertNotNull("Service is null", service);
Greeter rethwel = service.getPort(rethwelQ, Greeter.class);
updateAddressPort(rethwel, getPort("PORT1"));
assertNotNull("Port is null", rethwel);
configureProxy(ClientProxy.getClient(rethwel));
String answer = rethwel.sayHi();
assertTrue("Unexpected answer: " + answer,
"Bonjour from Mortimer".equals(answer));
assertProxyRequestCount(2);
}
/**
* This methods tests that a redirection loop will fail.
* Hurlon redirects to Abost, which redirects to Hurlon.
*
* Note: Unfortunately, the invocation may "fail" for any
* number of reasons.
*/
@Test
public void testHttp2HttpLoopRedirectFail() throws Exception {
startServer("Abost");
startServer("Hurlon");
URL config = getClass().getResource("Http2HttpLoopRedirectFail.cxf");
// We go through the back door, setting the default bus.
new DefaultBusFactory().createBus(config);
URL wsdl = getClass().getResource("greeting.wsdl");
assertNotNull("WSDL is null", wsdl);
SOAPService service = new SOAPService(wsdl, serviceName);
assertNotNull("Service is null", service);
Greeter hurlon = service.getPort(hurlonQ, Greeter.class);
assertNotNull("Port is null", hurlon);
updateAddressPort(hurlon, getPort("PORT3"));
configureProxy(ClientProxy.getClient(hurlon));
String answer = null;
try {
answer = hurlon.sayHi();
fail("Redirect didn't fail. Got answer: " + answer);
} catch (Exception e) {
// This exception will be one of not being able to
// read from the StreamReader
//e.printStackTrace();
}
assertProxyRequestCount(2);
}
}