The following dependencies are being used from the maven central repository in this example:
<!-- provides HAPI library -->
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-base</artifactId>
<version>2.2</version>
</dependency>
<!-- provides HAPI library message version -->
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-structures-v22</artifactId>
<version>2.2</version>
</dependency>
<!-- provides ByteString -->
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.10</artifactId>
<version>2.3.3</version>
</dependency>
Here is an example of my parsing code, written in scala:
import akka.util.ByteString
import ca.uhn.hl7v2.model.Message
import ca.uhn.hl7v2.model.v22.datatype.{CM_PAT_ID, ST, TN, TSComponentOne}
import ca.uhn.hl7v2.model.v22.segment.{EVN, MRG, PID}
import ca.uhn.hl7v2.parser.CanonicalModelClassFactory
import ca.uhn.hl7v2.{DefaultHapiContext, ErrorCode, HL7Exception}
lazy val parser = {
val context = new DefaultHapiContext()
context.setModelClassFactory(new CanonicalModelClassFactory("2.2"))
context.getGenericParser
}
def parseHL7Message(message: ByteString) = Try[Message] {
val msg: String = message.utf8String.trim
parser.parse(msg)
}
This code can successfully parse the following HL7 message.
"MSH|^~\\&|XXXX|S|XXXXXX|S|201410280931||ADT^A31|123456|P|2.2\r" +
"EVN|A31|201410280930\r" +
"PID|||9999999^^^S^MR~88888888^^^^PI||xxxx^xxxxxxxxx||11111111||||||(123)456-7890\r" +
"PV1\r"
However, when a phone number with an extension is supplied in the message, the hapi parser fails to parse the message. Here is an example of the input message I am trying to parse with an extension in the phone number:
"MSH|^~\\&|XXXX|S|XXXXXX|S|201410280931||ADT^A31|123456|P|2.2\r" +
"EVN|A31|201410280930\r" +
"PID|||9999999^^^S^MR~88888888^^^^PI||xxxx^xxxxxxxxx||11111111||||||(123)456-7890 1\r" +
"PV1\r"
Trying to parse this message fails with the following error message:
ca.uhn.hl7v2.validation.ValidationException: Validation failed:
Primitive value '(123)456-7890 1' requires to be empty or a US phone
number at PID-13
I read everything I could find at http://hl7api.sourceforge.net/index.html to look for documentation on how to modify the validation rules but have not found anything useful.
An example would be most appreciated, but even pointing to the proper documentation, or a simple working example project will be sufficient.
How can the validation rules used by the HAPI parser be configured to allow a phone number extension to be included in a valid US phone number in the PID-13 field?
EDIT
With a little more searching, through this hapi developer mailing list thread, I figured out how to disable validation altogether. Here is an example:
lazy val parser = {
val context = new DefaultHapiContext()
context.setModelClassFactory(new CanonicalModelClassFactory("2.2"))
context.setValidationContext(new NoValidation)
context.getGenericParser
}
But if possible, I would like to continue validating the messages. If I have to disable validation I guess that will have to work, but I'd prefer to specify that validation remain turned on, but that phone numbers can include extensions.
I must work with 3rd party service, and this service send to me invalid phones. Unfortunatly, I can't understand, how to do it as 'best practice'. But I found one hack:
#PostConstruct
public void postConstruct() {
List<RuleBinding<PrimitiveTypeRule>> rules = ((ValidationContextImpl)applicationRouter.getParser().getHapiContext().getValidationContext()).getPrimitiveRuleBindings();
//initially was published with this line, but think it was mistake
//for(int i = rules.size() - 1; i > 0; i--) {
for(int i = rules.size() - 1; i >= 0; i--) {
RuleBinding<PrimitiveTypeRule> item = rules.get(i);
if("TN".equals(item.getScope())){
rules.remove(i);
}
}
}
If somebody will know more good way to resolve it, please write.
Phone numbers can include extensions. The problem is that you have the extension in the wrong format. See the HL7 Messaging Standard Version 2.2, section 2.8.10.9. Telephone numbers must be in the following format.
[NN] [(999)]999-9999[X99999][B99999][C any text]
Put the extension after the 'X'.
Related
I added conversion and revenue columns to the script which can be found here.
This is the error I'm getting in the logs (the spreadsheet is updating fine with no issues):
TypeError: newValue.indexOf is not a function
at formatChangeString (Code:366:33)
at emailRow (Code:314:11)
at Code:285:15
at Array.forEach (<anonymous>)
at sendEmail (Code:284:17)
at main (Code:106:7)
formatChangeString(newValue,oldValue) expects newValue to be a string (so that newValue.indexOf('%') can be called.
It seems that one of the metrics you added is returned by the AdsApp.report call as a different type (most likely Number).
If you want a quick workaround, just change the line
const isPercentage = newValue.indexOf('%') >= 0;
to
const isPercentage = String(newValue).indexOf('%') >= 0;
I understand eval string-to-function is impossible to use on the browsers' application programming interfaces, but there must be another strategy to use third party dependencies without node.js on v8 engine, given Cloudflare does it in-house, unless they disable the exclusive method by necessity or otherwise on their edge servers for Workers. I imagine I could gather the AST of the commonjs module, as I was able to by rollup watch, but what might the actual steps be, by tooling? I mention AMD for it seems to rely on string-to-function (to-which I've notice Mozilla MDN says nothing much about it).
I have been exploring the require.js repositories, and they either use eval or AST
function DEFNODE(type, props, methods, base) {
if (arguments.length < 4) base = AST_Node;
if (!props) props = [];
else props = props.split(/\s+/);
var self_props = props;
if (base && base.PROPS) props = props.concat(base.PROPS);
var code = "return function AST_" + type + "(props){ if (props) { ";
for (var i = props.length; --i >= 0; ) {
code += "this." + props[i] + " = props." + props[i] + ";";
}
var proto = base && new base();
if ((proto && proto.initialize) || (methods && methods.initialize))
code += "this.initialize();";
code += "}}";
//constructor
var cnstor = new Function(code)();
if (proto) {
cnstor.prototype = proto;
cnstor.BASE = base;
}
if (base) base.SUBCLASSES.push(cnstor);
cnstor.prototype.CTOR = cnstor;
cnstor.PROPS = props || null;
cnstor.SELF_PROPS = self_props;
cnstor.SUBCLASSES = [];
if (type) {
cnstor.prototype.TYPE = cnstor.TYPE = type;
}
if (methods)
for (i in methods)
if (HOP(methods, i)) {
if (/^\$/.test(i)) {
cnstor[i.substr(1)] = methods[i];
} else {
cnstor.prototype[i] = methods[i];
}
}
//a function that returns an object with [name]:method
cnstor.DEFMETHOD = function (name, method) {
this.prototype[name] = method;
};
if (typeof exports !== "undefined") exports[`AST_${type}`] = cnstor;
return cnstor;
}
var AST_Token = DEFNODE(
"Token",
"type value line col pos endline endcol endpos nlb comments_before file raw",
{},
null
);
https://codesandbox.io/s/infallible-darwin-8jcl2k?file=/src/mastercard-backbank/uglify/index.js
https://www.youtube.com/watch?v=EF7UW9HxOe4
Is it possible to make a C++ addon just to add a default object for
node.js named exports or am I Y’ing up the wrong X
'.so' shared library for C++ dlopen/LoadLibrary (or #include?)
“I have to say that I'm amazed that there is code out there that loads one native addon from another native addon! Is it done by acquiring and then calling an instance of the require() function, or perhaps by using uv_dlopen() directly?”
N-API: An api for embedding Node in applications
"[there is no ]napi_env[ just yet]."
node-api: allow retrieval of add-on file name - Missing module in Init
Andreas Rossberg - is AST parsing, or initialize node.js abstraction for native c++, enough?
v8::String::NewFromUtf8(isolate, "Index from C++!");
Rising Stack - Node Source
"a macro implicit" parameter - bridge object between
C++ and JavaScript runtimes
extract a function's parameters and set the return value.
#include <nan.h>
int build () {
NAN_METHOD(Index) {
info.GetReturnValue().Set(
Nan::New("Index from C++!").ToLocalChecked()
);
}
}
// Module initialization logic
NAN_MODULE_INIT(Initialize) {
/*Export the `Index` function
(equivalent to `export function Index (...)` in JS)*/
NAN_EXPORT(target, Index);
}
New module "App" Initialize function from NAN_MODULE_INIT (an atomic?-macro)
"__napi_something doesn't exist."
"node-addon-API module for C++ code (N-API's C code's headers)"
NODE_MODULE(App, Initialize);
Sep 17, 2013, 4:42:17 AM to v8-u...#googlegroups.com "This comes up
frequently, but the answer remains the same: scrap the idea. ;)
Neither the V8 parser nor its AST are designed for external
interfacing. In particular (1) V8's AST does not necessarily reflect
JavaScript syntax 1-to-1, (2) we change it all the time, and (3) it
depends on various V8 internals. And since all these points are
important for V8, don't expect the situation to change.
/Andreas"
V8 c++: How to import module via code to script context (5/28/22, edit)
"The export keyword may only be used in a module interface unit.
The keyword is attached to a declaration of an entity, and causes that
declaration (and sometimes the definition) to become visible to module
importers[ - except for] the export keyword in the module-declaration, which is just a re-use of the keyword (and does not actually “export” ...entities)."
SyntheticModule::virtual
ScriptCompiler::CompileModule() - "Corresponds to the ParseModule abstract operation in the ECMAScript specification."
Local<Function> foo_func = ...;//external
Local<Module> module = Module::CreateSyntheticModule(
isolate, name,
{String::NewFromUtf8(isolate, "foo")},
[](Local<Context> context, Local<Module> module) {
module->SetSyntheticModuleExport(
String::NewFromUtf8(isolate, "foo"), foo_func
);
});
Context-Aware addons from node.js' commonjs modules
export module index;
export class Index {
public:
const char* app() {
return "done!";
}
};
import index;
import <iostream>;
int main() {
std::cout << Index().app() << '\n';
}
node-addon-api (new)
native abstractions (old)
"Thanks to the crazy changes in V8 (and some in Node core), keeping native addons compiling happily across versions, particularly 0.10 to 0.12 to 4.0, is a minor nightmare. The goal of this project is to store all logic necessary to develop native Node.js addons without having to inspect NODE_MODULE_VERSION and get yourself into a macro-tangle[ macro = extern atomics?]."
Scope Isolate (v8::Isolate), variable Local (v8::Local)
typed_array_to_native.cc
"require is part of the Asynchronous Module Definition AMD API[, without "string-to-function" eval/new Function()],"
node.js makes objects, for it is written in C++.
"According to the algorithm, before finding
./node_modules/_/index.js, it tried looking for express in the
core Node.js modules. This didn’t exist, so it looked in node_modules,
and found a directory called _. (If there was a
./node_modules/_.js, it would load that directly.) It then
loaded ./node_modules/_/package.json, and looked for an exports
field, but this didn’t exist. It also looked for a main field, but
this didn’t exist either. It then fell back to index.js, which it
found. ...require() looks for node_modules in all of the parent directories of the caller."
But java?
I won't accept this answer until it works, but this looks promising:
https://developer.oracle.com/databases/nashorn-javascript-part1.html
If not to run a jar file or something, in the Worker:
https://github.com/nodyn/jvm-npm
require and build equivalent in maven, first, use "dist/index.js".
Specifically: [ScriptEngineManager][21]
https://stackoverflow.com/a/15787930/11711280
Actually: js.commonjs-require experimental
https://docs.oracle.com/en/graalvm/enterprise/21/docs/reference-manual/js/Modules/
Alternatively/favorably: commonjs builder in C (v8 and node.js)
https://www.reddit.com/r/java/comments/u7elf4/what_are_your_thoughts_on_java_isolates_on_graalvm/
Here I will explore v8/node.js src .h and .cc for this purpose
https://codesandbox.io/s/infallible-darwin-8jcl2k?file=/src/c.cpp
I'm curious why there is near machine-level C operability in Workers if not to use std::ifstream, and/or build-locally, without node.js require.
how can I Print adittional information to Command line Console?
Output now is:
C:\Users\admin\Desktop\java>java -jar pdf.jar
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at readDataIn.main(readDataIn.java:31)
Code:
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
try {
String arg = args[0];
fileNameSource = "import/" + arg + ".xml";
fileNameTarget = "export/" + arg + ".pdf";
} catch (Exception e) {
// TODO: handle exception
**System.out.println("Personal-Number is missing");**
e.printStackTrace();
}
How can i give the information out, that the Personal Number ist Missing?
First of all, as a general rule you should check for possible exceptions before they actually occur if that is possible, which in your case it definitely is.
So instead of catching the ArrayIndexOutOfBounds insert an if statement that checks the length of the args array before accessing it.
if(args.length == 0){
// no argument has been provided
// handle error here
}
In terms of how to handle the error, there are many options available and depending of what you want to do either could be a good fit.
IllegalArgumentException
It is a common idiom in Java that whenever a function receives an invalid/ illegal argument to throw an IllegalArgumentException.
if (args.length == 0){
throw new IllegalArgumentException("Personal number is missing");
}
This will print the message that you have provided and the stack trace. However if your application should be a Command Line Interface (CLI) you should not use this kind of error handling.
Print message & exit program
if (args.length == 0){
// notice: "err" instead of "out": print to stderr instead of stdout
System.err.println("Personal number is missing");
// exit program with non-zero exit code as exit code == 0 means everything is fine
System.exit(1);
}
For more information on stdout and stderr see this StackOverflow question.
This is what many CLI applications and e.g. java itself does. When you type java fdsdfsdfs or some similar nonsense as an argument Java will give you an error message and exit with some non-zero return code ("1" in this case).
It is also common that CLI applications print an error message and following some usage information on how to correctly use the application or provide a help command so a user can get more information. This happens for example if you just enter java without any parameters.
So it is really up to you what you want to do.
If you are thinking of implementing a full featured CLI application with more (complex) commands with multiple options etc. you should consider using a CLI library like JCommander or Apache Commons CLI as parsing command line arguments can quickly get ugly. All these common things are already handled there.
Logging
In case your application is some script that will be executed in a non-interactive way logging the error to a file and exiting with a non-zero exit code might also be an option.
PS
Your code looks to me like it should not compile at all as you are not declaring a type for your variables fileNameSource and fileNameTarget.
Use String or var here (assuming you're running > Java 11).
String fileNameSource = "import/" + arg + ".xml";
var fileNameTarget = "export/" + arg + ".pdf";
You might also need to consider that your program name is part of the args array, so you might have more than 0 values in the array and therefore might need to adjust the if statements above.
You may be interested in picocli, which is a modern CLI library for Java and other JVM languages.
Picocli does some basic validation automatically, and results in very compact code that produces user-friendly applications. For example:
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
#Command(name = "myapp", mixinStandardHelpOptions = true, version = "1.0",
description = "This command does something useful.")
class MyApp implements Runnable {
#Parameters(description = "File name (without extension) of the file to import and export.")
private String personalNumber;
#Override
public void run() {
String fileNameSource = "import/" + personalNumber + ".xml";
String fileNameTarget = "export/" + personalNumber + ".pdf";
// remaining business logic
}
public static void main(String[] args) {
System.exit(new CommandLine(new MyApp()).execute(args));
}
}
If I run this class without any parameters, the following message is printed to the standard error stream, and the process finished with exit code 2. (Exit codes are customizable.)
Missing required parameter: '<personalNumber>'
Usage: myapp [-hV] <personalNumber>
This command does something useful.
<personalNumber> File name (without extension) of the file to import
and export.
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
The usage help message is created automatically from the descriptions of the command, and the descriptions of its options and positional parameters, but can be further customized.
Note how the mixinStandardHelpOptions = true annotation adds --help and --version options to the command. These options are handled by the library without requiring any further logic in the application.
Picocli comes with an annotation processor that makes it very easy to turn your application into a native image with GraalVM. Native images have faster startup time and lower runtime memory overhead compared to a Java VM.
In a spring boot application I read an image file from a remote service, which returns byte array and in headers I can check what is file extension:
ResponseEntity<byte[]> result = restTemplate.exchange(url, HttpMethod.GET, entity, byte[].class);
Now I want to put this byte array in a S3 bucket in a folder which I decide during run time, for example folder name can base don current timestamp.
I checked AmazonS3 class, but it doesnt seem to have any such API which can help me?
How can this be done?
As per example from documentation:
https://docs.aws.amazon.com/sdk-for-java/v2/developer-guide/examples-s3-objects.html#upload-object
// Put Object. here 'bytes' is byte array.
PutObjectResponse response = s3.putObject(PutObjectRequest.builder().bucket(bucketName).key(filePathLocation).build(),RequestBody.fromBytes(bytes));
You can use the MinIO java S3 client. Here you can find the documentation.
The code will look something like the following one:
MinioClient minioClient =
MinioClient.builder()
.endpoint("https://play.min.io")
.credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG")
.build();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 1000; i++) {
builder.append(
"Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. ");
builder.append("(29 letters)\n");
builder.append(
"Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. ");
builder.append("(31 letters)\n");
builder.append(
"Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on ");
builder.append("NASAs Space Shuttle. (32 letters)\n");
builder.append(
"The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. ");
builder.append("(39 letters)\n");
builder.append(
"How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity ");
builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n");
builder.append(
"Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac ");
builder.append("computers after System 7.\n");
builder.append(
"A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my ");
builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n");
builder.append("---\n");
// Create a InputStream for object upload.
ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8"));
// Create object 'my-objectname' in 'my-bucketname' with content from the input stream.
minioClient.putObject(
PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream(
bais, bais.available(), -1)
.build());
bais.close();
System.out.println("my-objectname is uploaded successfully");
The full code can be found here.
Checkout the AWS JAVA SDK:
Here the getting started section:
https://docs.aws.amazon.com/sdk-for-java/v2/developer-guide/getting-started.html
In order to use in Spring context use the Maven dependency:
https://docs.aws.amazon.com/sdk-for-java/v2/developer-guide/setup-project-maven.html
Uploading an object to S3 Bucket:
https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/examples-s3-objects.html#upload-object
import com.amazonaws.AmazonServiceException;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
System.out.format("Uploading %s to S3 bucket %s...\n", file_path, bucket_name);
final AmazonS3 s3 = AmazonS3ClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
try {
s3.putObject(bucket_name, key_name, new File(file_path));
} catch (AmazonServiceException e) {
System.err.println(e.getErrorMessage());
System.exit(1);
I am trying to upgrade from version 0.18 to 0.19 of Elm. My project depends on elm-lang/websocket in 0.18? I cannot seem to find the equivalent package in 0.19. What am I missing?
Here is a minimal working example of an interactive form to echo input from echo.websocket.org using 2 simple input/output ports for communicating with a JavaScript WebSocket object external to the elm 0.19 module:
File: echo.elm. Compile with: elm make echo.elm --output=echo.js
port module Main exposing (main)
import Browser
import Html exposing (Html)
import Html.Attributes as HA
import Html.Events as HE
import Json.Encode as JE
-- JavaScript usage: app.ports.websocketIn.send(response);
port websocketIn : (String -> msg) -> Sub msg
-- JavaScript usage: app.ports.websocketOut.subscribe(handler);
port websocketOut : String -> Cmd msg
main = Browser.element
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
{- MODEL -}
type alias Model =
{ responses : List String
, input : String
}
init : () -> (Model, Cmd Msg)
init _ =
( { responses = []
, input = ""
}
, Cmd.none
)
{- UPDATE -}
type Msg = Change String
| Submit String
| WebsocketIn String
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Change input ->
( { model | input = input }
, Cmd.none
)
Submit value ->
( model
, websocketOut value
)
WebsocketIn value ->
( { model | responses = value :: model.responses }
, Cmd.none
)
{- SUBSCRIPTIONS -}
subscriptions : Model -> Sub Msg
subscriptions model =
websocketIn WebsocketIn
{- VIEW -}
li : String -> Html Msg
li string = Html.li [] [Html.text string]
view : Model -> Html Msg
view model = Html.div []
--[ Html.form [HE.onSubmit (WebsocketIn model.input)] -- Short circuit to test without ports
[ Html.form [HE.onSubmit (Submit model.input)]
[ Html.input [HA.placeholder "Enter some text.", HA.value model.input, HE.onInput Change] []
, model.responses |> List.map li |> Html.ol []
]
]
Embed the compiled echo.js into echo.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Echo</title>
<script src="echo.js"></script>
</head>
<body>
<div id="elm-node"></div>
<script>
var app = Elm.Main.init({node: document.getElementById("elm-node")});
var ws = new WebSocket("wss://echo.websocket.org");
ws.onmessage = function(message)
{
console.log(message);
app.ports.websocketIn.send(JSON.stringify({data:message.data,timeStamp:message.timeStamp}));
};
app.ports.websocketOut.subscribe(function(msg) { ws.send(msg); });
</script>
</body>
</html>
This works on Firefox 60.2.0esr on Linux but has not been tested on other platforms.
Again, this is only a minimal example to demonstrate how to use ports with WebSockets for Elm 0.19. It does not included closing the WebSocket, error handling, etc. but hopefully this example can help you get started in that direction. It is expected that WebSockets will be directly supported by Elm again soon, so this is only a temporary work-around. If you don't need to upgrade to 0.19, then consider staying with 0.18 instead.
The websocket package is currently redesigned for Elm 0.19, see this issue:
This package has not been updated for 0.19 yet. I have heard lots of folks saying they need more features from this package, so I'd rather take that into consideration in the update rather than just doing the same stuff. I recommend using ports or 0.18 if you absolutely need this right this second.
EDIT: April 15, 2020 update
The package has been archived and the Readme file updated as
follows:
The recommended way to use WebSockets with Elm for now is through
ports. You can see a minimal example in the js-integration-examples
repo [IMAGE CLIPPED]
History
We had a bare bones version of WebSockets in within Elm in versions
0.17 and 0.18, part of the introduction of subscriptions to Elm. But users found that the API was not able to cover a lot of situations
they faced in practice. How can this work with Elixir Pheonix?
Firebase? How can I use a different backoff strategy for reconnecting?
How can I hear about when the connection goes down or comes back? How
about sub-protocols?
In trying to expand the API to cover all the cases people were facing
in practice, I came to think that it may not be possible with the
current subscriptions infrastructure. (My feeling is that effect
managers may not be a great fit for web sockets because they do not
have great mechanisms for uniquely identifying resources. Do we have
one connections or two? How do we tell the difference? If it requires
function pointer equality, how can we make that reliable when an
anonymous function is used?) I did not understand this problem as well
in 2016, and I think it manifested most clearly with web sockets.
So facing the prospect of either (1) having an API that many
eventually had to leave behind for ports or (2) recommending that
people go with ports from the start, we figured that (2) was probably
the best for someone new coming to Elm. That way they would hook up to
their preferred web socket manager without the intermediate step of
learning a promising but incomplete API.