och.util.socket.json.server.JsonProtocolSocketHandler.java Source code

Java tutorial

Introduction

Here is the source code for och.util.socket.json.server.JsonProtocolSocketHandler.java

Source

/*
 * Copyright 2015 Evgeny Dolganov (evgenij.dolganov@gmail.com).
 *
 * 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 och.util.socket.json.server;

import static och.util.StringUtil.*;
import static och.util.Util.*;
import static och.util.json.GsonUtil.*;
import static och.util.model.SecureProvider.*;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import och.util.model.SecureProvider;
import och.util.socket.SocketUtil;
import och.util.socket.json.exception.InvalidReqException;
import och.util.socket.json.exception.ValidationException;
import och.util.socket.server.SocketHandler;
import och.util.socket.server.SocketServer;

import org.apache.commons.logging.Log;

public class JsonProtocolSocketHandler implements SocketHandler {

    public static final String PROTOCOL_PREFIX = "sckt_jsn:";
    public static final String OK = "OK:";
    public static final String VALIDATION_ERROR = "VALIDATION_ERROR:";
    public static final String UNEXPECTED_ERROR = "UNEXPECTED_ERROR:";

    private Log log = getLog(getClass());

    private Map<Class<?>, ReqController<? super Object, ?>> controllers;
    private SecureProvider secureProvider;

    public JsonProtocolSocketHandler(SecureProvider secureProvider) {
        this(secureProvider, false);
    }

    public JsonProtocolSocketHandler(SecureProvider secureProvider, boolean threadSafe) {

        this.secureProvider = secureProvider == null ? DUMMY_IMPL : secureProvider;

        if (!threadSafe)
            controllers = new HashMap<>();
        else
            controllers = new ConcurrentHashMap<>();
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public <T> void put(Class<T> type, ReqController<T, ?> controller) {
        controllers.put((Class) type, (ReqController) controller);
    }

    public void remove(Class<?> type) {
        controllers.remove(type);
    }

    @Override
    public void process(Socket openedSocket, InputStream socketIn, OutputStream socketOut, SocketServer owner)
            throws Throwable {

        SocketAddress remoteAddress = openedSocket.getRemoteSocketAddress();

        BufferedReader reader = SocketUtil.getReaderUTF8(socketIn);
        BufferedOutputStream os = new BufferedOutputStream(socketOut);
        while (!owner.wasShutdown()) {

            String line = reader.readLine();
            if (line == null || owner.wasShutdown())
                break;

            //process req
            try {

                try {
                    line = secureProvider.decode(line);
                } catch (Throwable t) {
                    throw new InvalidReqException("decode exception: " + t.getMessage());
                }

                if (!line.startsWith(PROTOCOL_PREFIX))
                    throw new InvalidReqException("unknown protocol: " + line);

                String data = line.substring(PROTOCOL_PREFIX.length());
                int typeSepIndex = data.indexOf(':');
                if (typeSepIndex < 1)
                    throw new InvalidReqException("no data type in req: " + line);

                String typeStr = data.substring(0, typeSepIndex);
                Class<?> type = null;
                try {
                    type = Class.forName(typeStr);
                } catch (Exception e) {
                    throw new InvalidReqException("not java class: " + typeStr);
                }

                ReqController<? super Object, ?> controller = controllers.get(type);
                if (controller == null)
                    throw new InvalidReqException("no controller for type: " + type);

                Object req = null;
                try {

                    String jsonReq = data.substring(typeSepIndex + 1);

                    if (hasText(jsonReq))
                        req = defaultGson.fromJson(jsonReq, type);
                } catch (Exception e) {
                    throw new InvalidReqException("invalid json data in req: " + data);
                }

                //process
                Object resp = controller.processReq(req, remoteAddress);

                //send resp
                String jsonResp = resp == null ? "" : defaultGson.toJson(resp);

                writeLine(os, OK + controller.respType + ":" + jsonResp);
                os.flush();

            } catch (Exception e) {
                //connection closed
                if (e instanceof SocketException)
                    throw e;
                //validation error
                else if (e instanceof ValidationException)
                    sendAndFlush(os, validationError((ValidationException) e));
                //unexpected error
                else {
                    log.error("unexpected exception: ", e);
                    sendAndFlush(os, UNEXPECTED_ERROR + e);
                }
            }
        }
    }

    private void sendAndFlush(OutputStream os, Object obj) throws IOException {
        String resp = String.valueOf(obj);
        writeLine(os, resp);
        os.flush();
    }

    private void writeLine(OutputStream os, String resp) throws IOException {

        //to normal
        if (resp.endsWith("\n"))
            resp = resp.substring(0, resp.length() - 1);

        //secure
        resp = secureProvider.encode(resp);
        if (resp.indexOf('\n') > 0)
            throw new IOException("secureProvider encoded msg with '\n' char");
        resp += "\n";

        //write
        os.write(getBytesUTF8(resp));
    }

    private static String validationError(ValidationException e) throws IOException {
        return VALIDATION_ERROR + e.getMessage();
    }

}