package ca.uhn.fhir.cli; import static org.apache.commons.lang3.StringUtils.isBlank; import java.net.URI; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; import ca.uhn.fhir.model.primitive.IdDt; public class WebsocketSubscribeCommand extends BaseCommand { private static final org.slf4j.Logger LOG_RECV = org.slf4j.LoggerFactory.getLogger("websocket.RECV"); private static final org.slf4j.Logger LOG_SEND = org.slf4j.LoggerFactory.getLogger("websocket.SEND"); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(WebsocketSubscribeCommand.class); private boolean myQuit; @Override public String getCommandDescription() { return "Opens a websocket client connection to a server for the purposes of binding to a Subscription, and then outputs the results"; } @Override public String getCommandName() { return "subscription-client"; } @Override public Options getOptions() { Options options = new Options(); options.addOption("t", "target", true, "Target URL, e.g. \"ws://fhirtest.uhn.ca/websocket/dstu2\""); options.addOption("i", "subscriptionid", true, "Subscription ID, e.g. \"5235\""); return options; } @Override public void run(CommandLine theCommandLine) throws ParseException, Exception { String target = theCommandLine.getOptionValue("t"); if (isBlank(target) || (!target.startsWith("ws://") && !target.startsWith("wss://"))) { throw new ParseException("Target (-t) needs to be in the form \"ws://foo\" or \"wss://foo\""); } IdDt subsId = new IdDt(theCommandLine.getOptionValue("i")); WebSocketClient client = new WebSocketClient(); SimpleEchoSocket socket = new SimpleEchoSocket(subsId.getIdPart()); try { client.start(); URI echoUri = new URI(target); ClientUpgradeRequest request = new ClientUpgradeRequest(); ourLog.info("Connecting to : {}", echoUri); client.connect(socket, echoUri, request); while (!myQuit) { Thread.sleep(500L); } ourLog.info("Shutting down websocket client"); } finally { try { client.stop(); } catch (Exception e) { ourLog.error("Failure", e); } } } /** * Basic Echo Client Socket */ @WebSocket(maxTextMessageSize = 64 * 1024) public class SimpleEchoSocket { private String mySubsId; @SuppressWarnings("unused") private Session session; public SimpleEchoSocket(String theSubsId) { mySubsId = theSubsId; } @OnWebSocketError public void onError(Throwable theError) { ourLog.error("Websocket error: ", theError); myQuit = true; } @OnWebSocketFrame public void onFrame(Frame theFrame) { ourLog.debug("Websocket frame: {}", theFrame); } @OnWebSocketClose public void onClose(int statusCode, String reason) { ourLog.info("Received CLOSE status={} reason={}", statusCode, reason); } @OnWebSocketConnect public void onConnect(Session theSession) { ourLog.info("Successfully connected"); this.session = theSession; try { String sending = "bind " + mySubsId; LOG_SEND.info("{}", sending); theSession.getRemote().sendString(sending); } catch (Throwable t) { ourLog.error("Failure", t); myQuit = true; } } @OnWebSocketMessage public void onMessage(String theMsg) { LOG_RECV.info("{}", theMsg); } } }