com.ibm.jaql.lang.expr.index.HashtableServer.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.jaql.lang.expr.index.HashtableServer.java

Source

/*
 * Copyright (C) IBM Corp. 2009.
 * 
 * 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.ibm.jaql.lang.expr.index;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.ProtocolException;
import java.net.Socket;

import org.apache.hadoop.io.DataOutputBuffer;

import com.ibm.jaql.io.serialization.binary.BinaryFullSerializer;
import com.ibm.jaql.json.type.JsonSchema;
import com.ibm.jaql.json.type.JsonString;
import com.ibm.jaql.json.type.JsonValue;
import com.ibm.jaql.util.BaseUtil;

public class HashtableServer implements HashtableConstants, Runnable {
    protected Socket socket;
    protected DataInputStream in;
    protected DataOutputStream out;
    protected HashMapCache.Table table = null;
    protected static final BinaryFullSerializer defaultSerializer = BinaryFullSerializer.getDefault();

    public HashtableServer(Socket socket) throws IOException {
        this.socket = socket;
        in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
        out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
    }

    @Override
    public void run() {
        JsonValue readKey = null;
        JsonValue[] keys = new JsonValue[0];

        try {
            while (true) {
                byte command = in.readByte();
                switch (command) {
                // GET Key -> FOUND Value | NOT_FOUND
                case GET_CMD: {
                    readKey = table.keySerializer.read(in, readKey);
                    byte[] value = table.table.get(readKey);
                    if (value == null) {
                        out.write(NOT_FOUND_CMD);
                    } else {
                        out.write(FOUND_CMD);
                        out.write(value);
                    }
                    break;
                }
                // GETN n, [Key]*n -> OK n [FOUND Value | NOT_FOUND]*n  OK
                case GETN_CMD: {
                    int n = BaseUtil.readVUInt(in);
                    if (n > keys.length || // bigger array required
                            3 * n < keys.length) // array is way too big
                    {
                        keys = new JsonValue[n];
                    }
                    for (int i = 0; i < n; i++) {
                        keys[i] = table.keySerializer.read(in, keys[i]);
                    }
                    out.write(OK_CMD);
                    BaseUtil.writeVUInt(out, n);
                    for (int i = 0; i < n; i++) {
                        byte[] value = table.table.get(keys[i]);
                        if (value == null) {
                            out.write(NOT_FOUND_CMD);
                        } else {
                            out.write(FOUND_CMD);
                            out.write(value);
                        }
                    }
                    out.write(OK_CMD);
                    break;
                }
                // USE tableId string, age msec, lease msec
                //   -> OK lease, schema [ Key, Value ], 
                //    | BUILD 
                case USE_CMD: {
                    if (table != null) {
                        HashMapCache.instance.release(table);
                        table = null;
                    }
                    JsonString tableId = (JsonString) defaultSerializer.read(in, null);
                    long ageMS = BaseUtil.readVSLong(in);
                    long leaseMS = BaseUtil.readVSLong(in);

                    table = HashMapCache.instance.get(tableId.toString(), ageMS, leaseMS);
                    if (table.isBuilt()) // The table is good to go
                    {
                        out.write(OK_CMD);
                        BaseUtil.writeVSLong(out, 0); // TODO: implement leases
                        defaultSerializer.write(out, table.schema);
                    } else // We need to build the table
                    {
                        out.write(BUILD_CMD);
                        out.flush();

                        // SCHEMA schema [Key,Value] (PUT key, value)* OK -> OK
                        command = in.readByte();
                        if (command == RELEASE_CMD) {
                            // The client couldn't build the table, so just release it
                            HashMapCache.instance.release(table);
                            break;
                        }
                        if (command != SCHEMA_CMD) {
                            throw new ProtocolException("expected SCHEMA");
                        }
                        table.setSchema((JsonSchema) defaultSerializer.read(in, null));
                        DataOutputBuffer buf = new DataOutputBuffer();

                        System.err.println("building hashtable " + table.tableId);

                        while ((command = in.readByte()) == PUT_CMD) {
                            // TODO: we need to use a spilling hashtable to avoid memory overflows...
                            // TODO: we could at least pack the values more tightly 
                            buf.reset();
                            JsonValue key = table.keySerializer.read(in, null); // Be sure NOT to reuse the key here!
                            table.valueSerializer.copy(in, buf);
                            byte[] val = new byte[buf.getLength()];
                            System.arraycopy(buf.getData(), 0, val, 0, val.length);
                            table.table.put(key, val);
                        }
                        if (command != OK_CMD) {
                            throw new ProtocolException("expected OK");
                        }
                        HashMapCache.instance.doneBuilding(table);
                        out.write(OK_CMD);
                        System.err.println("built hashtable " + table.tableId);
                    }
                    break;
                }
                // RELEASE -> OK
                case RELEASE_CMD: {
                    if (table != null) {
                        HashMapCache.instance.release(table);
                        table = null;
                    }
                    out.write(OK_CMD);
                    break;
                }
                // LIST_TABLES -> (FOUND tableId built age lease schema numEntries)* OK
                // GET_ALL -> (FOUND key value)* OK
                // UNDEFINE tableId -> OK | NOT_FOUND
                // UNDEFINE_ALL -> OK
                default:
                    throw new ProtocolException("invalid command code");
                }
                out.flush();
            }
        } catch (EOFException e) {
            // ignored
        } catch (Exception e) {
            // log and exit thread
            e.printStackTrace();
        } finally {
            if (table != null) {
                HashMapCache.instance.release(table);
            }
            try {
                socket.close();
            } catch (Exception e) {
                // log and exit thread
                e.printStackTrace();
            }
        }
    }
}