/** * 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.activemq.transport.ws; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import org.apache.activemq.broker.BrokerService; import org.apache.activemq.transport.SocketConnectorFactory; import org.apache.activemq.transport.stomp.StompConnection; import org.apache.activemq.util.Wait; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxProfile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WSTransportTest extends WSTransportTestSupport { private static final Logger LOG = LoggerFactory.getLogger(WSTransportTest.class); private static final int MESSAGE_COUNT = 1000; private Server server; private WebDriver driver; private File profileDir; private String stompUri; private StompConnection stompConnection = new StompConnection(); protected final int port = 61623; @Override protected void addAdditionalConnectors(BrokerService service) throws Exception { stompUri = service.addConnector("stomp://localhost:0").getPublishableConnectString(); } @Override protected String getWSConnectorURI() { return "ws://127.0.0.1:" + port + "?websocket.maxTextMessageSize=99999&transport.maxIdleTime=1001"; } protected Server createWebServer() throws Exception { Server server = new Server(); Connector connector = createJettyConnector(server); WebAppContext context = new WebAppContext(); context.setResourceBase("src/test/webapp"); context.setContextPath("/"); context.setServer(server); server.setHandler(context); server.setConnectors(new Connector[] { connector }); server.start(); return server; } protected Connector createJettyConnector(Server server) throws Exception { Connector c = new SocketConnectorFactory().createConnector(server); c.getClass().getMethod("setPort", Integer.TYPE).invoke(c, getProxyPort()); return c; } @Override protected void stopBroker() throws Exception { if (broker != null) { broker.stop(); broker.waitUntilStopped(); broker = null; } } @Override @Before public void setUp() throws Exception { super.setUp(); profileDir = new File("activemq-data/profiles"); stompConnect(); server = createWebServer(); } @Override @After public void tearDown() throws Exception { try { stompDisconnect(); } catch(Exception e) { // Some tests explicitly disconnect from stomp so can ignore } finally { try { super.tearDown(); } catch (Exception ex) { LOG.warn("Error on super tearDown()"); } if (driver != null) { try { driver.quit(); } catch (Exception e) {} driver = null; } if (server != null) { try { server.stop(); } catch (Exception e) {} } } } @Test public void testBrokerStart() throws Exception { assertTrue(broker.isStarted()); } @Test(timeout=10000) public void testGet() throws Exception { testGet("http://127.0.0.1:" + port, null); } protected void testGet(final String uri, SslContextFactory sslContextFactory) throws Exception { HttpClient httpClient = sslContextFactory != null ? new HttpClient(sslContextFactory) : new HttpClient(new SslContextFactory()); httpClient.start(); final CountDownLatch latch = new CountDownLatch(1); Request request = httpClient.newRequest(uri).method(HttpMethod.GET); final AtomicInteger status = new AtomicInteger(); request.send(new BufferingResponseListener() { @Override public void onComplete(Result result) { status.set(result.getResponse().getStatus()); latch.countDown(); } }); latch.await(); assertEquals(HttpStatus.OK_200, status.get()); } @Ignore @Test public void testFireFoxWebSockets() throws Exception { driver = createFireFoxWebDriver(); doTestWebSockets(driver); } @Ignore @Test public void testChromeWebSockets() throws Exception { driver = createChromeWebDriver(); doTestWebSockets(driver); } protected WebDriver createChromeWebDriver() throws Exception { File profile = new File(profileDir, "chrome"); profile.mkdirs(); ChromeOptions options = new ChromeOptions(); options.addArguments("--enable-udd-profiles", "--user-data-dir=" + profile, "--allow-file-access-from-files"); return new ChromeDriver(options); } protected WebDriver createFireFoxWebDriver() throws Exception { File profile = new File(profileDir, "firefox"); profile.mkdirs(); return new FirefoxDriver(new FirefoxProfile(profile)); } private void stompConnect() throws IOException, URISyntaxException, UnknownHostException { URI connectUri = new URI(stompUri); stompConnection.open(createSocket(connectUri)); } private Socket createSocket(URI connectUri) throws IOException { return new Socket("127.0.0.1", connectUri.getPort()); } private void stompDisconnect() throws IOException { if (stompConnection != null) { stompConnection.close(); stompConnection = null; } } protected String getTestURI() { int port = getProxyPort(); return "http://localhost:" + port + "/websocket.html#" + wsConnectUri; } public void doTestWebSockets(WebDriver driver) throws Exception { driver.get(getTestURI()); final WebElement webStatus = driver.findElement(By.id("status")); final WebElement webReceived = driver.findElement(By.id("received")); while ("Loading" == webStatus.getText()) { Thread.sleep(100); } // Skip test if browser does not support websockets.. if (webStatus.getText() != "No WebSockets") { assertTrue("Should have connected", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return webStatus.getText().equals("Connected"); } })); stompConnection.connect("system", "manager"); stompConnection.send("/queue/websocket", "Hello"); assertTrue("Should have received message by now.", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return webReceived.getText().equals("Hello"); } })); for (int i = 1; i <= MESSAGE_COUNT; ++i) { stompConnection.send("/queue/websocket", "messages #" + i); } assertTrue("Should have received messages by now.", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return webReceived.getText().equals("messages #" + MESSAGE_COUNT); } })); Thread.sleep(1000); assertTrue("Should have disconnected", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return webStatus.getText().equals("Disconnected"); } })); } } }