/* * -----------------------------------------------------------------------\ * PerfCake *   * Copyright (C) 2010 - 2016 the original author or authors. *   * Licensed 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.perfcake.message.sender; import org.perfcake.PerfCakeException; import org.perfcake.message.Message; import org.perfcake.reporting.MeasurementUnit; import org.perfcake.util.Utils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.util.Properties; import javax.websocket.ClientEndpoint; import javax.websocket.CloseReason; import javax.websocket.ContainerProvider; import javax.websocket.DeploymentException; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.RemoteEndpoint; import javax.websocket.Session; import javax.websocket.WebSocketContainer; /** * Sends a simple messages via websocket protocol to a remote websocket server endpoint. * * @author Jiří Sviták */ public class WebSocketSender extends AbstractSender { private static final Logger logger = LogManager.getLogger(WebSocketSender.class); private WebSocketContainer container; private Session session; /** * Remote endpoint type. * * @author Jiří Sviták */ public enum RemoteEndpointType { BASIC, ASYNC } private RemoteEndpointType remoteEndpointType = RemoteEndpointType.BASIC; /** * Payload type. * * @author Jiří Sviták */ public enum PayloadType { TEXT, BINARY, PING } private PayloadType payloadType = PayloadType.TEXT; /** * Sets remote endpoint type. * * @param remoteEndpointType * The remote endpoint type. The value should be one of <code>basic</code> or <code>async</code>. * @return Instance of this to support fluent API. */ public WebSocketSender setRemoteEndpointType(final RemoteEndpointType remoteEndpointType) { this.remoteEndpointType = remoteEndpointType; return this; } /** * Returns remote endpoint type. * * @return One of <code>basic</code> or <code>async</code>. */ public RemoteEndpointType getRemoteEndpointType() { return this.remoteEndpointType; } /** * Sets payload type. * * @param payloadType * The payload type. The value should be one of <code>text</code>, <code>binary</code> or <code>ping</code>. * @return Instance of this to support fluent API. */ public WebSocketSender setPayloadType(PayloadType payloadType) { this.payloadType = payloadType; return this; } /** * Returns payload type. * * @return One of <code>text</code>, <code>binary</code> or <code>ping</code>. */ public PayloadType getPayloadType() { return this.payloadType; } @Override public void doInit(final Properties messageAttributes) throws PerfCakeException { container = ContainerProvider.getWebSocketContainer(); try { final String safeTarget = safeGetTarget(messageAttributes); if (logger.isTraceEnabled()) { logger.trace("Connecting to URI " + safeTarget); } container.connectToServer(new PerfCakeClientEndpoint(), new URI(safeTarget)); } catch (IOException | DeploymentException | URISyntaxException e) { throw new PerfCakeException("Cannot open web socket: ", e); } if (session == null) { throw new PerfCakeException("Web socket session cannot be null before the scenario run."); } } @Override public void doClose() throws PerfCakeException { try { session.close(); } catch (final IOException e) { throw new PerfCakeException("Cannot close web socket session.", e); } } @Override public Serializable doSend(final Message message, final MeasurementUnit measurementUnit) throws Exception { RemoteEndpoint endpoint; switch (remoteEndpointType) { default: // to get rid of a compiler warning case BASIC: endpoint = session.getBasicRemote(); break; case ASYNC: endpoint = session.getAsyncRemote(); break; } switch (payloadType) { case TEXT: if (endpoint instanceof RemoteEndpoint.Basic) { ((RemoteEndpoint.Basic) endpoint).sendText(message.getPayload().toString()); } else { ((RemoteEndpoint.Async) endpoint).sendText(message.getPayload().toString()); } break; case BINARY: if (endpoint instanceof RemoteEndpoint.Basic) { ((RemoteEndpoint.Basic) endpoint).sendBinary(ByteBuffer.wrap(message.getPayload().toString().getBytes(Utils.getDefaultEncoding()))); } else { ((RemoteEndpoint.Async) endpoint).sendBinary(ByteBuffer.wrap(message.getPayload().toString().getBytes(Utils.getDefaultEncoding()))); } break; case PING: endpoint.sendPing(ByteBuffer.wrap(message.getPayload().toString().getBytes(Utils.getDefaultEncoding()))); break; } return null; } /** * Represents web socket client endpoint. * * @author Jiří Sviták */ @ClientEndpoint public class PerfCakeClientEndpoint { /** * Is called when a new web socket session is open. * * @param session * Web socket session. */ @OnOpen public void onOpen(final Session session) { if (logger.isTraceEnabled()) { logger.trace("Connected with session id: " + session.getId()); } WebSocketSender.this.session = session; } /** * Receives incoming web socket messages. * * @param message * Incomming message. * @param session * Web socket session. */ @OnMessage public void onMessage(final String message, final Session session) { if (logger.isTraceEnabled()) { logger.trace("Received message: " + message); } } /** * Is called when a web socket session is closing. * * @param session * Web socket session. * @param closeReason * The reason why a web socket has been closed, or why it is being asked to close */ @OnClose public void onClose(final Session session, final CloseReason closeReason) { if (logger.isTraceEnabled()) { logger.trace(String.format("Session %s closed because of %s", session.getId(), closeReason)); } } @OnError public void onError(final Session session, final Throwable throwable) { if (logger.isErrorEnabled()) { logger.error(throwable.getMessage()); } } } }