/** * 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.client.v2; import com.google.common.collect.Maps; import com.google.common.util.concurrent.AbstractFuture; import org.apache.tajo.QueryId; import org.apache.tajo.TajoProtos; import org.apache.tajo.annotation.ThreadSafe; import org.apache.tajo.auth.UserRoleInfo; import org.apache.tajo.client.DummyServiceTracker; import org.apache.tajo.client.QueryClientImpl; import org.apache.tajo.client.SessionConnection; import org.apache.tajo.client.TajoClientUtil; import org.apache.tajo.conf.TajoConf; import org.apache.tajo.exception.*; import org.apache.tajo.ipc.ClientProtos; import org.apache.tajo.ipc.ClientProtos.GetQueryStatusResponse; import org.apache.tajo.service.ServiceTracker; import org.apache.tajo.service.ServiceTrackerException; import org.apache.tajo.service.TajoMasterInfo; import org.apache.tajo.util.KeyValueSet; import org.apache.tajo.util.NetUtils; import java.io.IOException; import java.net.InetSocketAddress; import java.sql.ResultSet; import java.util.HashMap; import java.util.List; import java.util.Properties; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import static org.apache.tajo.exception.ReturnStateUtil.ensureOk; @ThreadSafe public class LegacyClientDelegate extends SessionConnection implements ClientDelegate { private QueryClientImpl queryClient; private final ExecutorService executor = Executors.newFixedThreadPool(8); public LegacyClientDelegate(String host, int port, Properties clientParams) { super(new DummyServiceTracker(NetUtils.createSocketAddr(host, port)), null, new KeyValueSet(clientParams == null ? new HashMap<String, String>() : Maps.fromProperties(clientParams))); queryClient = new QueryClientImpl(this); } public LegacyClientDelegate(ServiceDiscovery discovery, Properties clientParams) { super(new DelegateServiceTracker(discovery), null, new KeyValueSet(clientParams == null ? new HashMap<String, String>() : Maps.fromProperties(clientParams))); queryClient = new QueryClientImpl(this); } @Override public void close() { executor.shutdown(); super.close(); } @Override public int executeUpdate(String sql) throws TajoException { queryClient.updateQuery(sql); return 0; } @Override public ResultSet executeSQL(String sql) throws TajoException, QueryFailedException, QueryKilledException { try { return executeSQLAsync(sql).get(); } catch (ExecutionException e) { if (e.getCause() instanceof TajoException) { throw (TajoException) e.getCause(); } else if (e.getCause() instanceof TajoRuntimeException) { throw new TajoException((TajoRuntimeException)e.getCause()); } else { throw new TajoInternalError(e); } } catch (InterruptedException e) { throw new RuntimeException(e); } } @Override public QueryFuture executeSQLAsync(String sql) throws TajoException { ClientProtos.SubmitQueryResponse response = queryClient.executeQuery(sql); ExceptionUtil.throwIfError(response.getState()); QueryId queryId = new QueryId(response.getQueryId()); switch (response.getResultType()) { case ENCLOSED: return new QueryFutureForEnclosed(queryId, TajoClientUtil.createResultSet(this.queryClient, response, 200)); case FETCH: AsyncQueryFuture future = new AsyncQueryFuture(queryId); executor.execute(future); return future; default: return new QueryFutureForNoFetch(queryId); } } @Override public String currentDB() { return getCurrentDatabase(); } @Override public void selectDB(String db) throws UndefinedDatabaseException { selectDatabase(db); } private class QueryFutureForNoFetch implements QueryFuture { protected final QueryId id; private final long now = System.currentTimeMillis(); QueryFutureForNoFetch(QueryId id) { this.id = id; } @Override public String id() { return id.toString(); } @Override public String queue() { return "default"; } @Override public QueryState state() { return QueryState.COMPLETED; } @Override public float progress() { return 1.0f; } @Override public boolean isOk() { return true; } @Override public boolean isSuccessful() { return true; } @Override public boolean isFailed() { return false; } @Override public boolean isKilled() { return false; } @Override public UserRoleInfo user() { return UserRoleInfo.getCurrentUser(); } @Override public void kill() { } @Override public long submitTime() { return 0; } @Override public long startTime() { return now; } @Override public long finishTime() { return now; } @Override public void close() { queryClient.closeQuery(id); } @Override public void addListener(FutureListener<QueryFuture> future) { future.processingCompleted(this); } @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return true; } @Override public ResultSet get() { return TajoClientUtil.NULL_RESULT_SET; } @Override public ResultSet get(long timeout, TimeUnit unit) { return TajoClientUtil.NULL_RESULT_SET; } } private class QueryFutureForEnclosed extends QueryFutureForNoFetch { private final ResultSet resultSet; QueryFutureForEnclosed(QueryId id, ResultSet resultSet) { super(id); this.resultSet = resultSet; } @Override public ResultSet get() { return resultSet; } @Override public ResultSet get(long timeout, TimeUnit unit) { return resultSet; } } private class AsyncQueryFuture extends AbstractFuture<ResultSet> implements QueryFuture, Runnable { private final QueryId queryId; private volatile QueryState lastState; private volatile float progress; private final long submitTime = System.currentTimeMillis(); private volatile long startTime = 0; private volatile long finishTime = 0; public AsyncQueryFuture(QueryId queryId) { this.queryId = queryId; this.lastState = QueryState.SCHEDULED; } @Override public String id() { return queryId.toString(); } @Override public boolean isOk() { return ClientUtil.isOk(lastState); } @Override public boolean isSuccessful() { return lastState == QueryState.COMPLETED; } @Override public boolean isFailed() { return ClientUtil.isFailed(lastState); } @Override public boolean isKilled() { try { return queryClient.getQueryStatus(queryId).getState() == TajoProtos.QueryState.QUERY_KILLED; } catch (QueryNotFoundException e) { throw new TajoInternalError(e); } } @Override public QueryState state() { return lastState; } @Override public String queue() { return "default"; } @Override public UserRoleInfo user() { return UserRoleInfo.getCurrentUser(); } @Override public float progress() { return progress; } @Override public void kill() { try { queryClient.killQuery(queryId).getState(); } catch (QueryNotFoundException e) { throw new TajoInternalError(e); } } @Override public long submitTime() { return submitTime; } @Override public long startTime() { return startTime; } @Override public long finishTime() { return finishTime; } @Override public void close() { queryClient.closeQuery(queryId); } @Override public void addListener(final FutureListener<QueryFuture> listener) { final QueryFuture f = this; addListener(new Runnable() { @Override public void run() { listener.processingCompleted(f); }}, executor); } private void updateState(GetQueryStatusResponse lastState) { this.startTime = lastState.getSubmitTime(); this.finishTime = lastState.getFinishTime(); this.progress = lastState.getProgress(); this.lastState = convert(lastState.getQueryState()); } GetQueryStatusResponse waitCompletion() { GetQueryStatusResponse response; response = queryClient.getRawQueryStatus(queryId); ensureOk(response.getState()); updateState(response); while(!TajoClientUtil.isQueryComplete(response.getQueryState())) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } response = queryClient.getRawQueryStatus(queryId); updateState(response); ensureOk(response.getState()); } return response; } @Override public void run() { GetQueryStatusResponse finalResponse; try { finalResponse = waitCompletion(); } catch (Throwable t) { setException(t); return; } if (finalResponse.getQueryState() == TajoProtos.QueryState.QUERY_SUCCEEDED) { if (finalResponse.hasHasResult()) { try { set(queryClient.getQueryResult(queryId)); } catch (QueryNotFoundException e) { setException(e); return; } } else { // when update set(TajoClientUtil.NULL_RESULT_SET); } } else if (finalResponse.getQueryState() == TajoProtos.QueryState.QUERY_KILLED) { setException(new QueryKilledException()); } else { if (finalResponse.hasErrorMessage()) { setException(new QueryFailedException(finalResponse.getErrorMessage())); } else { setException(new QueryFailedException( "internal error. See master and worker logs in ${tajo-install-dir}/logs for the cause of this error")); } } } } public static class DelegateServiceTracker implements ServiceTracker { private final ServiceDiscovery discovery; DelegateServiceTracker(ServiceDiscovery discovery) { this.discovery = discovery; } @Override public boolean isHighAvailable() { return false; } @Override public InetSocketAddress getUmbilicalAddress() throws ServiceTrackerException { return null; } @Override public InetSocketAddress getClientServiceAddress() throws ServiceTrackerException { return discovery.clientAddress(); } @Override public InetSocketAddress getResourceTrackerAddress() throws ServiceTrackerException { throw new TajoRuntimeException(new NotImplementedException()); } @Override public InetSocketAddress getCatalogAddress() throws ServiceTrackerException { throw new TajoRuntimeException(new NotImplementedException()); } @Override public InetSocketAddress getMasterHttpInfo() throws ServiceTrackerException { throw new TajoRuntimeException(new NotImplementedException()); } @Override public int getState(String masterName, TajoConf conf) throws ServiceTrackerException { throw new TajoRuntimeException(new NotImplementedException()); } @Override public int formatHA(TajoConf conf) throws ServiceTrackerException { throw new TajoRuntimeException(new NotImplementedException()); } @Override public List<String> getMasters(TajoConf conf) throws ServiceTrackerException { throw new TajoRuntimeException(new NotImplementedException()); } @Override public void register() throws IOException { throw new TajoRuntimeException(new NotImplementedException()); } @Override public void delete() throws IOException { throw new TajoRuntimeException(new NotImplementedException()); } @Override public boolean isActiveMaster() { throw new TajoRuntimeException(new NotImplementedException()); } @Override public List<TajoMasterInfo> getMasters() throws IOException { throw new TajoRuntimeException(new NotImplementedException()); } } public static QueryState convert(TajoProtos.QueryState state) { switch (state) { case QUERY_NEW: case QUERY_INIT: case QUERY_NOT_ASSIGNED: return QueryState.SCHEDULED; case QUERY_MASTER_INIT: case QUERY_MASTER_LAUNCHED: case QUERY_RUNNING: return QueryState.RUNNING; case QUERY_KILL_WAIT: return QueryState.KILLING; case QUERY_KILLED: return QueryState.KILLED; case QUERY_FAILED: return QueryState.FAILED; case QUERY_ERROR: return QueryState.ERROR; case QUERY_SUCCEEDED: return QueryState.COMPLETED; default: throw new RuntimeException("Unknown state:" + state.name()); } } }