com.vmware.dcp.common.WebSocketService.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.dcp.common.WebSocketService.java

Source

/*
 * Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.
 *
 * 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 com.vmware.dcp.common;

import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

/**
 * A service which allow to attach remote DCP services to a node via WebSocket connection. For every attached service
 * actual service implementation lives on a remote client attached to a not via WebSocket (for example, in a web
 * browser).
 * <p/>
 * All such services are exposed by DCP node as /core/ws-service/{some-uuid}. Service links are always generated by
 * the node. When a request to a such remote service comes in, it is forwarded to client over a WebSocket and its
 * response is forwarded to original caller. Hence these services have a real link and remote implementation body.
 * <p/>
 * This service provides an easy way to support pushing updates to a web client, such as observing needed services
 * or service factories for changes.
 * <p/>
 * Remote services may subscribe for updates using special web socket based API and these subscriptions are
 * automatically removed whenever web socket connection is closed.
 * <p/>
 * See {@link com.vmware.dcp.common.http.netty.NettyHttpClientRequestHandler} for more details on WebSocket interaction
 * protocol.
 */
public final class WebSocketService extends StatelessService {
    private final ChannelHandlerContext ctx;
    private final URI uri;
    private Map<Long, Operation> pendingOperations = new ConcurrentHashMap<>();

    private static class OperationResponse {
        long id;
        Map<String, String> responseHeaders;
        int statusCode;
        String responseJsonBody;
    }

    public WebSocketService(ChannelHandlerContext ctx, URI uri) {
        this.ctx = ctx;
        this.uri = uri;
    }

    public void handleWebSocketMessage(String body) {
        OperationResponse or = Utils.fromJson(body, OperationResponse.class);
        Operation op = this.pendingOperations.remove(or.id);
        if (op == null) {
            logFine("Unknown operation id: %d", or.id);
            return;
        }
        for (Map.Entry<String, String> entry : or.responseHeaders.entrySet()) {
            op.addResponseHeader(entry.getKey(), entry.getValue());
        }
        op.setStatusCode(or.statusCode);
        if (or.responseJsonBody != null) {
            op.setContentType(Operation.MEDIA_TYPE_APPLICATION_JSON);
            op.setBodyNoCloning(or.responseJsonBody);
        }
        op.complete();
    }

    @Override
    public void handleRequest(Operation op) {
        prepareRequest(op);
        Operation.SerializedOperation serializedOperation = Operation.SerializedOperation.create(op);
        this.pendingOperations.put(op.getId(), op);
        ChannelFuture promise = this.ctx.writeAndFlush(new TextWebSocketFrame(
                "POST " + this.uri.toString() + Operation.CR_LF + Utils.toJson(serializedOperation)));
        promise.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    op.fail(future.cause());
                    WebSocketService.this.pendingOperations.remove(op.getId());
                }
            }
        });
    }
}