tajo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ji...@apache.org
Subject [1/3] tajo git commit: TAJO-1338: Defines RESTful API for Clients
Date Mon, 06 Apr 2015 02:49:34 GMT
Repository: tajo
Updated Branches:
  refs/heads/master 696d2aa7f -> 32b524d74


http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestQueryResource.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestQueryResource.java b/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestQueryResource.java
new file mode 100644
index 0000000..47a7bcb
--- /dev/null
+++ b/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestQueryResource.java
@@ -0,0 +1,170 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tajo.ws.rs.resources;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.tajo.QueryTestCaseBase;
+import org.apache.tajo.TajoConstants;
+import org.apache.tajo.conf.TajoConf.ConfVars;
+import org.apache.tajo.ipc.ClientProtos.ResultCode;
+import org.apache.tajo.master.QueryInfo;
+import org.apache.tajo.ws.rs.netty.gson.GsonFeature;
+import org.apache.tajo.ws.rs.requests.NewSessionRequest;
+import org.apache.tajo.ws.rs.requests.SubmitQueryRequest;
+import org.apache.tajo.ws.rs.responses.NewSessionResponse;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.filter.LoggingFilter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.gson.internal.StringMap;
+
+import static org.junit.Assert.*;
+
+public class TestQueryResource extends QueryTestCaseBase {
+  
+  private URI restServiceURI;
+  private URI sessionsURI;
+  private URI queriesURI;
+  private Client restClient;
+  
+  private static final String tajoSessionIdHeaderName = "X-Tajo-Session";
+  
+  public TestQueryResource() {
+    super(TajoConstants.DEFAULT_DATABASE_NAME);
+  }
+  
+  @Before
+  public void setUp() throws Exception {
+    int restPort = testBase.getTestingCluster().getConfiguration().getIntVar(ConfVars.REST_SERVICE_PORT);
+    restServiceURI = new URI("http", null, "127.0.0.1", restPort, "/rest", null, null);
+    sessionsURI = new URI(restServiceURI + "/sessions");
+    queriesURI = new URI(restServiceURI + "/databases/" + TajoConstants.DEFAULT_DATABASE_NAME + "/queries");
+    restClient = ClientBuilder.newBuilder()
+        .register(new GsonFeature(RestTestUtils.registerTypeAdapterMap()))
+        .register(LoggingFilter.class)
+        .property(ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true)
+        .property(ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE, true)
+        .build();
+  }
+  
+  @After
+  public void tearDown() throws Exception {
+    restClient.close();
+  }
+  
+  private SubmitQueryRequest createNewQueryRequest(String query) throws Exception {
+    SubmitQueryRequest request = new SubmitQueryRequest();
+    request.setQuery(query);
+    return request;
+  }
+  
+  private String generateNewSessionAndGetId() throws Exception {
+    NewSessionRequest request = new NewSessionRequest();
+    request.setUserName("tajo-user");
+    request.setDatabaseName(TajoConstants.DEFAULT_DATABASE_NAME);
+    
+    NewSessionResponse response = restClient.target(sessionsURI)
+        .request().post(Entity.entity(request, MediaType.APPLICATION_JSON), NewSessionResponse.class);
+    
+    assertNotNull(response);
+    assertTrue(ResultCode.OK.equals(response.getResultCode()));
+    assertTrue(response.getId() != null && !response.getId().isEmpty());
+    
+    return response.getId();
+  }
+  
+  @Test
+  public void testGetAllQueries() throws Exception {
+    String sessionId = generateNewSessionAndGetId();
+    SubmitQueryRequest queryRequest = createNewQueryRequest("select * from lineitem");
+    
+    Response response = restClient.target(queriesURI)
+        .request().header(tajoSessionIdHeaderName, sessionId)
+        .post(Entity.entity(queryRequest, MediaType.APPLICATION_JSON));
+    
+    assertNotNull(response);
+    assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+    String locationHeader = response.getHeaderString("Location");
+    assertTrue(locationHeader != null && !locationHeader.isEmpty());
+    
+    String queryId = locationHeader.lastIndexOf('/') >= 0?
+        locationHeader.substring(locationHeader.lastIndexOf('/')+1):null;
+        
+    assertTrue(queryId != null && !queryId.isEmpty());
+    
+    Map<String, List<StringMap>> queriesMap = restClient.target(queriesURI)
+        .request().get(new GenericType<Map<String, List<StringMap>>>(Map.class));
+    
+    assertNotNull(queriesMap);
+    
+    List<StringMap> queryInfoList = queriesMap.get("queries");
+    assertNotNull(queryInfoList);
+    
+    boolean assertQueryIdFound = false;
+    for (StringMap queryInfo: queryInfoList) {
+      if (queryId.equals(queryInfo.get("queryIdStr"))) {
+        assertQueryIdFound = true;
+      }
+    }
+    
+    assertTrue(assertQueryIdFound);
+  }
+  
+  @Test
+  public void testSubmitQuery() throws Exception {
+    String sessionId = generateNewSessionAndGetId();
+    SubmitQueryRequest queryRequest = createNewQueryRequest("select * from lineitem");
+    
+    Response response = restClient.target(queriesURI)
+        .request().header(tajoSessionIdHeaderName, sessionId)
+        .post(Entity.entity(queryRequest, MediaType.APPLICATION_JSON));
+    
+    assertNotNull(response);
+    assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+    String locationHeader = response.getHeaderString("Location");
+    assertTrue(locationHeader != null && !locationHeader.isEmpty());
+    
+    String queryId = locationHeader.lastIndexOf('/') >= 0?
+        locationHeader.substring(locationHeader.lastIndexOf('/')+1):null;
+        
+    assertTrue(queryId != null && !queryId.isEmpty());
+    
+    QueryInfo queryInfo = restClient.target(queriesURI)
+        .path("/{queryId}")
+        .resolveTemplate("queryId", queryId)
+        .queryParam("print", "BRIEF")
+        .request().get(new GenericType<QueryInfo>(QueryInfo.class));
+    
+    assertNotNull(queryInfo);
+    assertEquals(queryId, queryInfo.getQueryIdStr());
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestQueryResultResource.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestQueryResultResource.java b/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestQueryResultResource.java
new file mode 100644
index 0000000..6fc4ea1
--- /dev/null
+++ b/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestQueryResultResource.java
@@ -0,0 +1,287 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tajo.ws.rs.resources;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.InputStream;
+import java.net.URI;
+import java.security.MessageDigest;
+import java.util.List;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.compress.compressors.CompressorInputStream;
+import org.apache.commons.compress.compressors.CompressorStreamFactory;
+import org.apache.tajo.QueryTestCaseBase;
+import org.apache.tajo.TajoConstants;
+import org.apache.tajo.conf.TajoConf.ConfVars;
+import org.apache.tajo.ipc.ClientProtos.ResultCode;
+import org.apache.tajo.storage.RowStoreUtil;
+import org.apache.tajo.storage.Tuple;
+import org.apache.tajo.util.TUtil;
+import org.apache.tajo.ws.rs.netty.gson.GsonFeature;
+import org.apache.tajo.ws.rs.requests.NewSessionRequest;
+import org.apache.tajo.ws.rs.requests.SubmitQueryRequest;
+import org.apache.tajo.ws.rs.responses.GetQueryResultDataResponse;
+import org.apache.tajo.ws.rs.responses.NewSessionResponse;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.filter.LoggingFilter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class TestQueryResultResource extends QueryTestCaseBase {
+
+  private URI restServiceURI;
+  private URI sessionsURI;
+  private URI queriesURI;
+  private Client restClient;
+  
+  private static final String tajoSessionIdHeaderName = "X-Tajo-Session";
+  private static final String tajoDigestHeaderName = "X-Tajo-Digest";
+  
+  public TestQueryResultResource() {
+    super(TajoConstants.DEFAULT_DATABASE_NAME);
+  }
+  
+  @Before
+  public void setUp() throws Exception {
+    int restPort = testBase.getTestingCluster().getConfiguration().getIntVar(ConfVars.REST_SERVICE_PORT);
+    restServiceURI = new URI("http", null, "127.0.0.1", restPort, "/rest", null, null);
+    sessionsURI = new URI(restServiceURI + "/sessions");
+    queriesURI = new URI(restServiceURI + "/databases/" + TajoConstants.DEFAULT_DATABASE_NAME + "/queries");
+    restClient = ClientBuilder.newBuilder()
+        .register(new GsonFeature(RestTestUtils.registerTypeAdapterMap()))
+        .register(LoggingFilter.class)
+        .property(ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true)
+        .property(ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE, true)
+        .build();
+  }
+  
+  @After
+  public void tearDown() throws Exception {
+    restClient.close();
+  }
+  
+  private String generateNewSessionAndGetId() throws Exception {
+    NewSessionRequest request = new NewSessionRequest();
+    request.setUserName("tajo-user");
+    request.setDatabaseName(TajoConstants.DEFAULT_DATABASE_NAME);
+    
+    NewSessionResponse response = restClient.target(sessionsURI)
+        .request().post(Entity.entity(request, MediaType.APPLICATION_JSON), NewSessionResponse.class);
+    
+    assertNotNull(response);
+    assertTrue(ResultCode.OK.equals(response.getResultCode()));
+    assertTrue(response.getId() != null && !response.getId().isEmpty());
+    
+    return response.getId();
+  }
+  
+  private URI sendNewQueryResquest(String sessionId, String query) throws Exception {
+    
+    SubmitQueryRequest request = new SubmitQueryRequest();
+    request.setQuery(query);
+    
+    Response response = restClient.target(queriesURI)
+        .request().header(tajoSessionIdHeaderName, sessionId)
+        .post(Entity.entity(request, MediaType.APPLICATION_JSON));
+    
+    assertNotNull(response);
+    assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+    String locationHeader = response.getHeaderString("Location");
+    assertTrue(locationHeader != null && !locationHeader.isEmpty());
+    
+    URI queryIdURI = new URI(locationHeader);
+
+    assertNotNull(queryIdURI);
+    
+    return queryIdURI;
+  }
+
+  @Test
+  public void testGetQueryResult() throws Exception {
+    String sessionId = generateNewSessionAndGetId();
+    URI queryIdURI = sendNewQueryResquest(sessionId, "select * from lineitem");
+    URI queryResultURI = new URI(queryIdURI + "/result");
+
+    GetQueryResultDataResponse response = restClient.target(queryResultURI)
+        .request().header(tajoSessionIdHeaderName, sessionId)
+        .get(new GenericType<GetQueryResultDataResponse>(GetQueryResultDataResponse.class));
+
+    assertNotNull(response);
+    assertNotNull(response.getResultCode());
+    assertEquals(ResultCode.OK, response.getResultCode());
+    assertNotNull(response.getSchema());
+    assertEquals(16, response.getSchema().getColumns().size());
+    assertNotNull(response.getResultset());
+    assertTrue(response.getResultset().getId() != 0);
+    assertNotNull(response.getResultset().getLink());
+  }
+
+  @Test
+  public void testGetQueryResultNotFound() throws Exception {
+    String sessionId = generateNewSessionAndGetId();
+    URI queryIdURI = new URI(queriesURI + "/q_11111_0001");
+    URI queryResultURI = new URI(queryIdURI + "/result");
+
+    Response response = restClient.target(queryResultURI)
+        .request().header(tajoSessionIdHeaderName, sessionId)
+        .get();
+
+    assertNotNull(response);
+    assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+  }
+
+  @Test
+  public void testGetQueryResultSet() throws Exception {
+    String sessionId = generateNewSessionAndGetId();
+    URI queryIdURI = sendNewQueryResquest(sessionId, "select * from lineitem");
+    URI queryResultURI = new URI(queryIdURI + "/result");
+
+    GetQueryResultDataResponse response = restClient.target(queryResultURI)
+        .request().header(tajoSessionIdHeaderName, sessionId)
+        .get(new GenericType<GetQueryResultDataResponse>(GetQueryResultDataResponse.class));
+
+    assertNotNull(response);
+    assertNotNull(response.getResultCode());
+    assertEquals(ResultCode.OK, response.getResultCode());
+    assertNotNull(response.getSchema());
+    assertEquals(16, response.getSchema().getColumns().size());
+    assertNotNull(response.getResultset());
+    assertTrue(response.getResultset().getId() != 0);
+    assertNotNull(response.getResultset().getLink());
+
+    URI queryResultSetURI = response.getResultset().getLink();
+
+    Response queryResultSetResponse = restClient.target(queryResultSetURI)
+        .queryParam("count", 100)
+        .request().header(tajoSessionIdHeaderName, sessionId)
+        .get();
+
+    assertNotNull(queryResultSetResponse);
+    String tajoDigest = queryResultSetResponse.getHeaderString(tajoDigestHeaderName);
+    assertTrue(tajoDigest != null && !tajoDigest.isEmpty());
+
+    DataInputStream queryResultSetInputStream =
+        new DataInputStream(new BufferedInputStream(queryResultSetResponse.readEntity(InputStream.class)));
+
+    assertNotNull(queryResultSetInputStream);
+
+    boolean isFinished = false;
+    List<Tuple> tupleList = TUtil.newList();
+    RowStoreUtil.RowStoreDecoder decoder = RowStoreUtil.createDecoder(response.getSchema());
+    MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
+    while (!isFinished) {
+      try {
+        int length = queryResultSetInputStream.readInt();
+        byte[] dataByteArray = new byte[length];
+        int readBytes = queryResultSetInputStream.read(dataByteArray);
+
+        assertEquals(length, readBytes);
+
+        tupleList.add(decoder.toTuple(dataByteArray));
+        messageDigest.update(dataByteArray);
+      } catch (EOFException eof) {
+        isFinished = true;
+      }
+    }
+
+    assertEquals(5, tupleList.size());
+    assertEquals(tajoDigest, Base64.encodeBase64String(messageDigest.digest()));
+
+    for (Tuple aTuple: tupleList) {
+      assertTrue(aTuple.getInt4(response.getSchema().getColumnId("l_orderkey")) > 0);
+    }
+  }
+
+  @Test
+  public void testGetQueryResultSetWithOffset() throws Exception {
+    String sessionId = generateNewSessionAndGetId();
+    URI queryIdURI = sendNewQueryResquest(sessionId, "select * from lineitem");
+    URI queryResultURI = new URI(queryIdURI + "/result");
+
+    GetQueryResultDataResponse response = restClient.target(queryResultURI)
+        .request().header(tajoSessionIdHeaderName, sessionId)
+        .get(new GenericType<GetQueryResultDataResponse>(GetQueryResultDataResponse.class));
+
+    assertNotNull(response);
+    assertNotNull(response.getResultCode());
+    assertEquals(ResultCode.OK, response.getResultCode());
+    assertNotNull(response.getSchema());
+    assertEquals(16, response.getSchema().getColumns().size());
+    assertNotNull(response.getResultset());
+    assertTrue(response.getResultset().getId() != 0);
+    assertNotNull(response.getResultset().getLink());
+
+    URI queryResultSetURI = response.getResultset().getLink();
+
+    Response queryResultSetResponse = restClient.target(queryResultSetURI)
+        .queryParam("count", 100)
+        .queryParam("offset", 3)
+        .request().header(tajoSessionIdHeaderName, sessionId)
+        .get();
+
+    assertNotNull(queryResultSetResponse);
+    String tajoDigest = queryResultSetResponse.getHeaderString(tajoDigestHeaderName);
+    assertTrue(tajoDigest != null && !tajoDigest.isEmpty());
+
+    DataInputStream queryResultSetInputStream =
+        new DataInputStream(new BufferedInputStream(queryResultSetResponse.readEntity(InputStream.class)));
+
+    assertNotNull(queryResultSetInputStream);
+
+    boolean isFinished = false;
+    List<Tuple> tupleList = TUtil.newList();
+    RowStoreUtil.RowStoreDecoder decoder = RowStoreUtil.createDecoder(response.getSchema());
+    MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
+    while (!isFinished) {
+      try {
+        int length = queryResultSetInputStream.readInt();
+        byte[] dataByteArray = new byte[length];
+        int readBytes = queryResultSetInputStream.read(dataByteArray);
+
+        assertEquals(length, readBytes);
+
+        tupleList.add(decoder.toTuple(dataByteArray));
+        messageDigest.update(dataByteArray);
+      } catch (EOFException eof) {
+        isFinished = true;
+      }
+    }
+
+    assertEquals(3, tupleList.size());
+    assertEquals(tajoDigest, Base64.encodeBase64String(messageDigest.digest()));
+
+    for (Tuple aTuple: tupleList) {
+      assertTrue(aTuple.getInt4(response.getSchema().getColumnId("l_orderkey")) > 0);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestSessionsResource.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestSessionsResource.java b/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestSessionsResource.java
new file mode 100644
index 0000000..7b293d8
--- /dev/null
+++ b/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestSessionsResource.java
@@ -0,0 +1,263 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tajo.ws.rs.resources;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.tajo.QueryTestCaseBase;
+import org.apache.tajo.TajoConstants;
+import org.apache.tajo.conf.TajoConf.ConfVars;
+import org.apache.tajo.ws.rs.netty.gson.GsonFeature;
+import org.apache.tajo.ws.rs.requests.NewSessionRequest;
+import org.apache.tajo.ws.rs.responses.NewSessionResponse;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.filter.LoggingFilter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class TestSessionsResource extends QueryTestCaseBase {
+  
+  private URI restServiceURI;
+  private URI sessionsURI;
+  private Client restClient;
+
+  public TestSessionsResource() {
+    super(TajoConstants.DEFAULT_DATABASE_NAME);
+  }
+  
+  @Before
+  public void setUp() throws Exception {
+    int restPort = testBase.getTestingCluster().getConfiguration().getIntVar(ConfVars.REST_SERVICE_PORT);
+    restServiceURI = new URI("http", null, "127.0.0.1", restPort, "/rest", null, null);
+    sessionsURI = new URI(restServiceURI + "/sessions");
+    restClient = ClientBuilder.newBuilder()
+        .register(new GsonFeature(RestTestUtils.registerTypeAdapterMap()))
+        .register(LoggingFilter.class)
+        .property(ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true)
+        .property(ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE, true)
+        .build();
+  }
+  
+  @After
+  public void tearDown() throws Exception {
+    restClient.close();
+  }
+  
+  private NewSessionRequest createNewSessionRequest() {
+    NewSessionRequest request = new NewSessionRequest();
+    
+    request.setUserName("tajo-user1");
+    request.setDatabaseName("default");
+    
+    return request;
+  }
+  
+  @Test
+  public void testNewSession() throws Exception {    
+    NewSessionRequest request = createNewSessionRequest();
+    
+    assertNotNull(request);
+    
+    NewSessionResponse response = restClient.target(sessionsURI)
+        .request().post(Entity.entity(request, MediaType.APPLICATION_JSON), NewSessionResponse.class);
+    
+    assertNotNull(response);
+    assertNotNull(response.getId());
+    assertTrue(response.getMessage() == null || response.getMessage().isEmpty());
+    assertFalse(response.getId().isEmpty());
+  }
+  
+  @Test
+  public void testNewSessionWithoutDBName() throws Exception {
+    NewSessionRequest request = new NewSessionRequest();
+    request.setUserName("tajo-user");
+    
+    NewSessionResponse response = restClient.target(sessionsURI)
+        .request().post(Entity.entity(request, MediaType.APPLICATION_JSON), NewSessionResponse.class);
+    
+    assertNotNull(response);
+    assertNotNull(response.getId());
+    assertTrue(response.getMessage() == null || response.getMessage().isEmpty());
+    assertFalse(response.getId().isEmpty());
+  }
+  
+  @Test
+  public void testNewSessionWithBadRequest() throws Exception {
+    NewSessionRequest request = new NewSessionRequest();
+    request.setDatabaseName("default");
+    
+    Response response = restClient.target(sessionsURI)
+        .request().post(Entity.entity(request, MediaType.APPLICATION_JSON));
+    
+    assertNotNull(response);
+    assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+  }
+
+  @Test
+  public void testRemoveSession() throws Exception {
+    NewSessionRequest request = new NewSessionRequest();
+    request.setUserName("tajo-user");
+
+    NewSessionResponse response = restClient.target(sessionsURI)
+        .request().post(Entity.entity(request, MediaType.APPLICATION_JSON), NewSessionResponse.class);
+
+    assertNotNull(response);
+    assertTrue(response.getId() != null && !response.getId().isEmpty());
+
+    Response restResponse = restClient.target(sessionsURI)
+        .path("/{session-id}").resolveTemplate("session-id", response.getId())
+        .request().delete();
+
+    assertNotNull(restResponse);
+    assertEquals(Status.OK.getStatusCode(), restResponse.getStatus());
+  }
+
+  @Test
+  public void testRemoveSessionNotFound() throws Exception {
+    String invalidSessionId = "invalid";
+
+    Response response = restClient.target(sessionsURI)
+        .path("/{session-id}").resolveTemplate("session-id", invalidSessionId)
+        .request().delete();
+
+    assertNotNull(response);
+    assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+  }
+
+  @Test
+  public void testGetAllSessionVariables() throws Exception {
+    NewSessionRequest request = new NewSessionRequest();
+    request.setUserName("tajo-user");
+
+    NewSessionResponse response = restClient.target(sessionsURI)
+        .request().post(Entity.entity(request, MediaType.APPLICATION_JSON), NewSessionResponse.class);
+
+    assertNotNull(response);
+    assertTrue(response.getId() != null && !response.getId().isEmpty());
+
+    Map<String, Map<String, String>> variablesMap = restClient.target(sessionsURI)
+        .path("/{session-id}/variables").resolveTemplate("session-id", response.getId())
+        .request().get(new GenericType<Map<String, Map<String, String>>>(Map.class));
+
+    assertNotNull(variablesMap);
+    assertTrue(variablesMap.containsKey("variables"));
+  }
+
+  @Test
+  public void testGetAllSessionVariablesNotFound() throws Exception {
+    String invalidSessionId = "invalid";
+
+    Response response = restClient.target(sessionsURI)
+        .path("/{session-id}/variables").resolveTemplate("session-id", invalidSessionId)
+        .request().get();
+
+    assertNotNull(response);
+    assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+  }
+
+  @Test
+  public void testUpdateSessionVariables() throws Exception {
+    NewSessionRequest request = new NewSessionRequest();
+    request.setUserName("tajo-user");
+
+    NewSessionResponse response = restClient.target(sessionsURI)
+        .request().post(Entity.entity(request, MediaType.APPLICATION_JSON), NewSessionResponse.class);
+
+    assertNotNull(response);
+    assertTrue(response.getId() != null && !response.getId().isEmpty());
+
+    Map<String, String> variablesMap = new HashMap<String, String>();
+    variablesMap.put("variableA", "valueA");
+    variablesMap.put("variableB", "valueB");
+    Map<String, Map<String, String>> variables = new HashMap<String, Map<String, String>>();
+    variables.put("variables", variablesMap);
+    Response restResponse = restClient.target(sessionsURI)
+        .path("/{session-id}/variables").resolveTemplate("session-id", response.getId())
+        .request().put(Entity.entity(variables, MediaType.APPLICATION_JSON));
+
+    assertNotNull(restResponse);
+    assertEquals(Status.OK.getStatusCode(), restResponse.getStatus());
+    
+    Map<String, Map<String, String>> retrievedVariables = restClient.target(sessionsURI)
+        .path("/{session-id}/variables").resolveTemplate("session-id", response.getId())
+        .request().get(new GenericType<Map<String, Map<String, String>>>(Map.class));
+    
+    assertNotNull(retrievedVariables);
+    assertFalse(retrievedVariables.isEmpty());
+    
+    Map<String, String> retrievedVariablesMap = retrievedVariables.get("variables");
+    
+    assertNotNull(retrievedVariablesMap);
+    assertFalse(retrievedVariablesMap.isEmpty());
+    
+    assertTrue(retrievedVariablesMap.containsKey("variableA"));
+    assertTrue(retrievedVariablesMap.containsKey("variableB"));
+    assertTrue("valueA".equals(retrievedVariablesMap.get("variableA")));
+    assertTrue("valueB".equals(retrievedVariablesMap.get("variableB")));
+  }
+  
+  @Test
+  public void testUpdateSessionVariable() throws Exception {
+    NewSessionRequest request = new NewSessionRequest();
+    request.setUserName("tajo-user");
+
+    NewSessionResponse response = restClient.target(sessionsURI)
+        .request().post(Entity.entity(request, MediaType.APPLICATION_JSON), NewSessionResponse.class);
+
+    assertNotNull(response);
+    assertTrue(response.getId() != null && !response.getId().isEmpty());
+
+    Map<String, String> variablesMap = new HashMap<String, String>();
+    variablesMap.put("variableA", "valueA");
+    Response restResponse = restClient.target(sessionsURI)
+        .path("/{session-id}/variables").resolveTemplate("session-id", response.getId())
+        .request().put(Entity.entity(variablesMap, MediaType.APPLICATION_JSON));
+
+    assertNotNull(restResponse);
+    assertEquals(Status.OK.getStatusCode(), restResponse.getStatus());
+    
+    Map<String, Map<String, String>> retrievedVariables = restClient.target(sessionsURI)
+        .path("/{session-id}/variables").resolveTemplate("session-id", response.getId())
+        .request().get(new GenericType<Map<String, Map<String, String>>>(Map.class));
+    
+    assertNotNull(retrievedVariables);
+    assertFalse(retrievedVariables.isEmpty());
+    
+    Map<String, String> retrievedVariablesMap = retrievedVariables.get("variables");
+    
+    assertNotNull(retrievedVariablesMap);
+    assertFalse(retrievedVariablesMap.isEmpty());
+    
+    assertTrue(retrievedVariablesMap.containsKey("variableA"));
+    assertTrue("valueA".equals(retrievedVariablesMap.get("variableA")));
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestTablesResource.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestTablesResource.java b/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestTablesResource.java
new file mode 100644
index 0000000..6144534
--- /dev/null
+++ b/tajo-core/src/test/java/org/apache/tajo/ws/rs/resources/TestTablesResource.java
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tajo.ws.rs.resources;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.tajo.QueryTestCaseBase;
+import org.apache.tajo.catalog.CatalogUtil;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.TableDesc;
+import org.apache.tajo.catalog.TableMeta;
+import org.apache.tajo.catalog.proto.CatalogProtos.StoreType;
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.conf.TajoConf.ConfVars;
+import org.apache.tajo.storage.StorageConstants;
+import org.apache.tajo.util.KeyValueSet;
+import org.apache.tajo.ws.rs.netty.gson.GsonFeature;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.filter.LoggingFilter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.gson.internal.StringMap;
+
+import static org.junit.Assert.*;
+
+public class TestTablesResource extends QueryTestCaseBase {
+  
+  private URI restServiceURI;
+  private URI tablesURI;
+  private Client restClient;
+  
+  private static final String defaultDatabaseName = "TestTablesDB";
+
+  public TestTablesResource() {
+    super(defaultDatabaseName);
+  }
+
+  @Before
+  public void setUp() throws Exception {
+    int restPort = testBase.getTestingCluster().getConfiguration().getIntVar(ConfVars.REST_SERVICE_PORT);
+    restServiceURI = new URI("http", null, "127.0.0.1", restPort, "/rest", null, null);
+    tablesURI = new URI(restServiceURI + "/databases/" + defaultDatabaseName + "/tables");
+    restClient = ClientBuilder.newBuilder()
+        .register(new GsonFeature(RestTestUtils.registerTypeAdapterMap()))
+        .register(LoggingFilter.class)
+        .property(ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true)
+        .property(ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE, true)
+        .build();
+  }
+  
+  @After
+  public void tearDown() throws Exception {
+    restClient.close();
+  }
+  
+  private TableDesc createNewTableForTestCreateTable(String tableName) throws Exception {
+    Schema tableSchema = new Schema(new Column[] {new Column("column1", TajoDataTypes.Type.TEXT)});
+    KeyValueSet tableOptions = new KeyValueSet();
+    tableOptions.set(StorageConstants.TEXT_DELIMITER, StorageConstants.DEFAULT_FIELD_DELIMITER);
+    TableMeta tableMeta = new TableMeta(StoreType.CSV, tableOptions);
+    return new TableDesc(tableName, tableSchema, tableMeta, null);
+  }
+  
+  @Test
+  public void testCreateTable() throws Exception {
+    String tableName = "TestCreateTable";
+    TableDesc tableDesc = createNewTableForTestCreateTable(tableName);
+    
+    Response response = restClient.target(tablesURI)
+        .request().post(Entity.entity(tableDesc, MediaType.APPLICATION_JSON));
+    
+    assertNotNull(response);
+    assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+  }
+  
+  @Test
+  public void testGetAllTable() throws Exception {
+    String tableName = "TestGetAllTable";
+    TableDesc tableDesc = createNewTableForTestCreateTable(tableName);
+    
+    Response response = restClient.target(tablesURI)
+        .request().post(Entity.entity(tableDesc, MediaType.APPLICATION_JSON));
+    
+    assertNotNull(response);
+    assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+    
+    List<StringMap<String>> tables = restClient.target(tablesURI)
+        .request().get(new GenericType<List<StringMap<String>>>(List.class));
+    
+    assertNotNull(tables);
+    assertTrue(!tables.isEmpty());
+    
+    boolean tableFound = false;
+    for (StringMap<String> table: tables) {
+      if (tableName.equals(CatalogUtil.extractSimpleName(table.get("tableName")))) {
+        tableFound = true;
+        break;
+      }
+    }
+    
+    assertTrue(tableFound);
+  }
+  
+  @Test
+  public void testGetTable() throws Exception {
+    String tableName = "TestGetTable";
+    TableDesc tableDesc = createNewTableForTestCreateTable(tableName);
+    
+    Response response = restClient.target(tablesURI)
+        .request().post(Entity.entity(tableDesc, MediaType.APPLICATION_JSON));
+    
+    assertNotNull(response);
+    assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+    
+    TableDesc selectedTable = restClient.target(tablesURI)
+        .path("/{tableName}").resolveTemplate("tableName", tableName)
+        .request().get(new GenericType<TableDesc>(TableDesc.class));
+    
+    assertNotNull(selectedTable);
+    assertEquals(tableName, CatalogUtil.extractSimpleName(selectedTable.getName()));
+  }
+  
+  @Test
+  public void testGetTableNotFound() throws Exception {
+    Response response = restClient.target(tablesURI)
+        .path("/{tableName}").resolveTemplate("tableName", "TestGetTableNotFound")
+        .request().get();
+    
+    assertNotNull(response);
+    assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+  }
+  
+  @Test
+  public void testDropTable() throws Exception {
+    String tableName = "TestDropTable";
+    TableDesc tableDesc = createNewTableForTestCreateTable(tableName);
+    
+    Response response = restClient.target(tablesURI)
+        .request().post(Entity.entity(tableDesc, MediaType.APPLICATION_JSON));
+    
+    assertNotNull(response);
+    assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+    
+    TableDesc selectedTable = restClient.target(tablesURI)
+        .path("/{tableName}").resolveTemplate("tableName", tableName)
+        .request().get(new GenericType<TableDesc>(TableDesc.class));
+    
+    assertNotNull(selectedTable);
+    assertEquals(tableName, CatalogUtil.extractSimpleName(selectedTable.getName()));
+    
+    response = restClient.target(tablesURI)
+        .path("/{tableName}").resolveTemplate("tableName", tableName)
+        .request().delete();
+    
+    assertNotNull(response);
+    assertEquals(Status.OK.getStatusCode(), response.getStatus());
+  }
+  
+  @Test
+  public void testDropTableNotFound() throws Exception {
+    Response response = restClient.target(tablesURI)
+        .path("/{tableName}").resolveTemplate("tableName", "TestDropTableNotFound")
+        .request().delete();
+    
+    assertNotNull(response);
+    assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-project/pom.xml
----------------------------------------------------------------------
diff --git a/tajo-project/pom.xml b/tajo-project/pom.xml
index 7ad4ae0..e2b0a0e 100644
--- a/tajo-project/pom.xml
+++ b/tajo-project/pom.xml
@@ -1012,6 +1012,11 @@
         <version>2.6</version>
       </dependency>
       <dependency>
+        <groupId>commons-codec</groupId>
+        <artifactId>commons-codec</artifactId>
+        <version>1.10</version>
+      </dependency>
+      <dependency>
         <groupId>com.google.guava</groupId>
         <artifactId>guava</artifactId>
         <version>11.0.2</version>

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-rpc/tajo-ws-rs/pom.xml
----------------------------------------------------------------------
diff --git a/tajo-rpc/tajo-ws-rs/pom.xml b/tajo-rpc/tajo-ws-rs/pom.xml
index a87a67a..271d29a 100644
--- a/tajo-rpc/tajo-ws-rs/pom.xml
+++ b/tajo-rpc/tajo-ws-rs/pom.xml
@@ -95,6 +95,10 @@
       <artifactId>tajo-rpc-common</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.apache.tajo</groupId>
+      <artifactId>tajo-common</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.glassfish.jersey.core</groupId>
       <artifactId>jersey-common</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/ResourceConfigUtil.java
----------------------------------------------------------------------
diff --git a/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/ResourceConfigUtil.java b/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/ResourceConfigUtil.java
new file mode 100644
index 0000000..10c680c
--- /dev/null
+++ b/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/ResourceConfigUtil.java
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tajo.ws.rs;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+public class ResourceConfigUtil {
+
+  public static Application getJAXRSApplication(Application application) {
+    Application result = application;
+    
+    if (application instanceof ResourceConfig) {
+      ResourceConfig resourceConfig = (ResourceConfig) application;
+      result = resourceConfig.getApplication();
+    }
+    
+    return result;
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonFeature.java
----------------------------------------------------------------------
diff --git a/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonFeature.java b/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonFeature.java
index 26086d4..20d7181 100644
--- a/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonFeature.java
+++ b/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonFeature.java
@@ -18,17 +18,38 @@
 
 package org.apache.tajo.ws.rs.netty.gson;
 
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
 import javax.ws.rs.core.Feature;
 import javax.ws.rs.core.FeatureContext;
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.MessageBodyWriter;
 
+import org.apache.tajo.json.GsonSerDerAdapter;
+
 public class GsonFeature implements Feature {
+  
+  private Map<Type, GsonSerDerAdapter<?>> adapterMap;
+  
+  public GsonFeature() {
+  }
+  
+  public GsonFeature(Map<Type, GsonSerDerAdapter<?>> adapterMap) {
+    this.adapterMap = new HashMap<Type, GsonSerDerAdapter<?>>(adapterMap.size());
+    this.adapterMap.putAll(adapterMap);
+  }
 
   @Override
   public boolean configure(FeatureContext featureContext) {
-    featureContext.register(GsonReader.class, MessageBodyReader.class);
-    featureContext.register(GsonWriter.class, MessageBodyWriter.class);
+    if (adapterMap != null && !adapterMap.isEmpty()) {
+      featureContext.register(new GsonReader().setAdapterMap(adapterMap), MessageBodyReader.class);
+      featureContext.register(new GsonWriter().setAdapterMap(adapterMap), MessageBodyWriter.class);
+    } else {
+      featureContext.register(GsonReader.class, MessageBodyReader.class);
+      featureContext.register(GsonWriter.class, MessageBodyWriter.class);
+    }
     return true;
   }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonReader.java
----------------------------------------------------------------------
diff --git a/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonReader.java b/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonReader.java
index 4d6e440..fdaf811 100644
--- a/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonReader.java
+++ b/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonReader.java
@@ -26,15 +26,27 @@ import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyReader;
+
+import org.apache.tajo.json.GsonSerDerAdapter;
+
 import java.io.*;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.Map.Entry;
 
 /**
  * Custom message body reader with Gson feature.
  */
 @Consumes(MediaType.APPLICATION_JSON)
 public class GsonReader<T> implements MessageBodyReader<T> {
+  
+  private Map<Type, GsonSerDerAdapter<?>> adapterMap;
+  
+  public GsonReader<T> setAdapterMap(Map<Type, GsonSerDerAdapter<?>> adapterMap) {
+    this.adapterMap = adapterMap;
+    return this;
+  }
 
   @Override
   public boolean isReadable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
@@ -45,7 +57,16 @@ public class GsonReader<T> implements MessageBodyReader<T> {
   public T readFrom(Class<T> aClass, Type type, Annotation[] annotations, MediaType mediaType,
                     MultivaluedMap<String, String> multivaluedMap, InputStream inputStream)
       throws IOException, WebApplicationException {
-    Gson gson = new GsonBuilder().create();
+    Gson gson;
+    if (adapterMap != null && !adapterMap.isEmpty()) {
+      GsonBuilder gsonBuilder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation();
+      for (Entry<Type, GsonSerDerAdapter<?>> adapter: adapterMap.entrySet()) {
+        gsonBuilder.registerTypeAdapter(adapter.getKey(), adapter.getValue());
+      }
+      gson = gsonBuilder.create();
+    } else {
+      gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
+    }
     Reader reader = new BufferedReader(new InputStreamReader(inputStream));
     return gson.fromJson(reader, type);
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonUtil.java
----------------------------------------------------------------------
diff --git a/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonUtil.java b/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonUtil.java
index f16cb96..9994bfa 100644
--- a/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonUtil.java
+++ b/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonUtil.java
@@ -21,7 +21,7 @@ package org.apache.tajo.ws.rs.netty.gson;
 import javax.ws.rs.core.MediaType;
 
 public class GsonUtil {
-
+  
   public static boolean isJsonType(MediaType mediaType) {
     if (mediaType != null) {
       String subType = mediaType.getSubtype();

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonWriter.java
----------------------------------------------------------------------
diff --git a/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonWriter.java b/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonWriter.java
index d215611..29a4bbf 100644
--- a/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonWriter.java
+++ b/tajo-rpc/tajo-ws-rs/src/main/java/org/apache/tajo/ws/rs/netty/gson/GsonWriter.java
@@ -26,15 +26,27 @@ import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.apache.tajo.json.GsonSerDerAdapter;
+
 import java.io.*;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.Map.Entry;
 
 /**
  * custom message body writer with Gson feature.
  */
 @Produces(MediaType.APPLICATION_JSON)
 public class GsonWriter<T> implements MessageBodyWriter<T> {
+  
+  private Map<Type, GsonSerDerAdapter<?>> adapterMap;
+  
+  public GsonWriter<T> setAdapterMap(Map<Type, GsonSerDerAdapter<?>> adapterMap) {
+    this.adapterMap = adapterMap;
+    return this;
+  }
 
   @Override
   public boolean isWriteable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
@@ -50,7 +62,16 @@ public class GsonWriter<T> implements MessageBodyWriter<T> {
   public void writeTo(T t, Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType,
                       MultivaluedMap<String, Object> multivaluedMap, OutputStream outputStream)
       throws IOException, WebApplicationException {
-    Gson gson = new GsonBuilder().create();
+    Gson gson;
+    if (adapterMap != null && !adapterMap.isEmpty()) {
+      GsonBuilder gsonBuilder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation();
+      for (Entry<Type, GsonSerDerAdapter<?>> adapter: adapterMap.entrySet()) {
+        gsonBuilder.registerTypeAdapter(adapter.getKey(), adapter.getValue());
+      }
+      gson = gsonBuilder.create();
+    } else {
+      gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
+    }
     Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream));
 
     gson.toJson(t, type, writer);

http://git-wip-us.apache.org/repos/asf/tajo/blob/32b524d7/tajo-rpc/tajo-ws-rs/src/test/java/org/apache/tajo/ws/rs/netty/testapp2/Directory.java
----------------------------------------------------------------------
diff --git a/tajo-rpc/tajo-ws-rs/src/test/java/org/apache/tajo/ws/rs/netty/testapp2/Directory.java b/tajo-rpc/tajo-ws-rs/src/test/java/org/apache/tajo/ws/rs/netty/testapp2/Directory.java
index 3851020..4ddc2b2 100644
--- a/tajo-rpc/tajo-ws-rs/src/test/java/org/apache/tajo/ws/rs/netty/testapp2/Directory.java
+++ b/tajo-rpc/tajo-ws-rs/src/test/java/org/apache/tajo/ws/rs/netty/testapp2/Directory.java
@@ -20,11 +20,13 @@ package org.apache.tajo.ws.rs.netty.testapp2;
 
 import java.io.Serializable;
 
+import com.google.gson.annotations.Expose;
+
 public class Directory implements Serializable {
 
-  private String name;
-  private String owner;
-  private String group;
+  @Expose private String name;
+  @Expose private String owner;
+  @Expose private String group;
 
   public String getName() {
     return name;


Mime
View raw message