/**
* Copyright 2013-2014 Recruit Technologies Co., Ltd. and contributors
* (see CONTRIBUTORS.md)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. A copy of the
* License is distributed with this work in the LICENSE.md file. You may
* also obtain a copy of the License from
*
* 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.gennai.gungnir.server;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.gennai.gungnir.GungnirManager;
import org.gennai.gungnir.UserEntity;
import org.gennai.gungnir.metastore.MetaStoreException;
import org.gennai.gungnir.metastore.NotStoredException;
import org.gennai.gungnir.ql.CommandProcessor;
import org.gennai.gungnir.ql.CommandProcessorException;
import org.gennai.gungnir.ql.CommandProcessorFactory;
import org.gennai.gungnir.ql.UploadProcessor;
import org.gennai.gungnir.ql.UploadProcessorException;
import org.gennai.gungnir.ql.session.InvalidSessionException;
import org.gennai.gungnir.ql.session.SessionStore;
import org.gennai.gungnir.ql.session.SessionStoreException;
import org.gennai.gungnir.ql.session.StatementEntity;
import org.gennai.gungnir.thrift.ErrorCode;
import org.gennai.gungnir.thrift.GungnirServerException;
import org.gennai.gungnir.thrift.GungnirService;
import org.gennai.gungnir.utils.GungnirUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.twitter.util.Future;
import com.twitter.util.Promise;
public class GungnirServiceProcessor implements GungnirService.ServiceIface {
private static final Logger LOG = LoggerFactory.getLogger(GungnirServiceProcessor.class);
private Executor executor;
private SessionStore sessionStore;
private CommandProcessorFactory processorFactory;
public GungnirServiceProcessor() {
executor = Executors.newCachedThreadPool(
GungnirUtils.createThreadFactory("GungnirServiceProcessor"));
try {
sessionStore = GungnirManager.getManager().getClusterManager().getSessionStore();
} catch (SessionStoreException e) {
throw new RuntimeException(e);
}
processorFactory = new CommandProcessorFactory();
}
@Override
public Future<String> createConnection(final String userName, final String password) {
final Promise<String> promise = new Promise<String>();
executor.execute(new Runnable() {
@Override
public void run() {
try {
UserEntity user = GungnirManager.getManager().getMetaStore()
.findUserAccountByName(userName);
if (user.validatePassword(password)) {
String sessionId = sessionStore.createSession(user);
promise.setValue(sessionId);
LOG.info("Established the session with '{}' session: '{}'", userName, sessionId);
} else {
promise.setException(new GungnirServerException(ErrorCode.ERROR_ACCESS_DENIED,
"Access denied for user '" + userName + "'"));
}
} catch (MetaStoreException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INTERNAL_ERROR,
e.getMessage()));
} catch (NotStoredException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_ACCESS_DENIED,
"Access denied for user '" + userName + "'"));
} catch (SessionStoreException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INTERNAL_ERROR,
e.getMessage()));
}
}
});
return promise;
}
@Override
public Future<String> createStatement(final String sessionId) {
final Promise<String> promise = new Promise<String>();
executor.execute(new Runnable() {
@Override
public void run() {
try {
promise.setValue(sessionStore.createStatement(sessionId));
} catch (SessionStoreException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INTERNAL_ERROR,
e.getMessage()));
} catch (InvalidSessionException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INVALID_SESSIONID,
"This session has been disconnected"));
}
}
});
return promise;
}
@Override
public Future<String> execute(final String statementId, final String command) {
final Promise<String> promise = new Promise<String>();
executor.execute(new Runnable() {
@Override
public void run() {
LOG.info("Execute command '{}' statement: '{}'", command, statementId);
try {
StatementEntity statement = sessionStore.getStatement(statementId);
String cmd = command.trim();
CommandProcessor processor = processorFactory.getProcessor(statement, cmd);
if (processor != null) {
LOG.debug("Compile '{}'", cmd);
try {
String res = processor.run(statement, cmd);
sessionStore.setStatement(statementId, statement);
promise.setValue(res);
} catch (CommandProcessorException e) {
LOG.error("Failed to execute command '{}'", cmd, e);
promise.setException(new GungnirServerException(
ErrorCode.ERROR_EXECUTE_COMMAND_FAILED, e.getCause().getMessage()));
}
} else {
promise.setException(new GungnirServerException(ErrorCode.ERROR_EXECUTE_COMMAND_FAILED,
"Command isn't supported"));
}
} catch (SessionStoreException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INTERNAL_ERROR,
e.getMessage()));
} catch (InvalidSessionException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INVALID_SESSIONID,
"This session has been disconnected"));
}
}
});
return promise;
}
@Override
public Future<Void> beginFileUpload(final String statementId, final String fileName) {
final Promise<Void> promise = new Promise<Void>();
executor.execute(new Runnable() {
@Override
public void run() {
try {
StatementEntity statement = sessionStore.getStatement(statementId);
try {
UploadProcessor.beginUpload(statement, fileName);
sessionStore.setStatement(statementId, statement);
promise.setValue(null);
} catch (UploadProcessorException e) {
LOG.error("Failed to upload file '{}'", fileName, e);
promise.setException(new GungnirServerException(
ErrorCode.ERROR_UPLOAD_FILE_FAILED, e.getCause().getMessage()));
}
} catch (SessionStoreException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INTERNAL_ERROR,
e.getMessage()));
} catch (InvalidSessionException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INVALID_SESSIONID,
"This session has been disconnected"));
}
}
});
return promise;
}
@Override
public Future<Void> uploadChunk(final String statementId, final ByteBuffer chunk) {
final Promise<Void> promise = new Promise<Void>();
executor.execute(new Runnable() {
@Override
public void run() {
try {
StatementEntity statement = sessionStore.getStatement(statementId);
try {
UploadProcessor.writeChunk(statement, chunk);
sessionStore.setStatement(statementId, statement);
promise.setValue(null);
} catch (UploadProcessorException e) {
LOG.error("Failed to upload file '{}'", statement.getUploadingFileName(), e);
promise.setException(new GungnirServerException(
ErrorCode.ERROR_UPLOAD_FILE_FAILED, e.getCause().getMessage()));
}
} catch (SessionStoreException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INTERNAL_ERROR,
e.getMessage()));
} catch (InvalidSessionException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INVALID_SESSIONID,
"This session has been disconnected"));
}
}
});
return promise;
}
@Override
public Future<Void> finishFileUpload(final String statementId, final int fileSize,
final long checksum) {
final Promise<Void> promise = new Promise<Void>();
executor.execute(new Runnable() {
@Override
public void run() {
try {
StatementEntity statement = sessionStore.getStatement(statementId);
try {
UploadProcessor.finishUpload(statement, fileSize, checksum);
sessionStore.setStatement(statementId, statement);
promise.setValue(null);
} catch (UploadProcessorException e) {
LOG.error("Failed to upload file '{}'", statement.getUploadingFileName(), e);
promise.setException(new GungnirServerException(
ErrorCode.ERROR_UPLOAD_FILE_FAILED, e.getCause().getMessage()));
}
} catch (SessionStoreException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INTERNAL_ERROR,
e.getMessage()));
} catch (InvalidSessionException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INVALID_SESSIONID,
"This session has been disconnected"));
}
}
});
return promise;
}
@Override
public Future<Void> closeStatement(final String statementId) {
final Promise<Void> promise = new Promise<Void>();
executor.execute(new Runnable() {
@Override
public void run() {
try {
sessionStore.deleteStatement(statementId);
promise.setValue(null);
} catch (SessionStoreException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INTERNAL_ERROR,
e.getMessage()));
}
}
});
return promise;
}
@Override
public Future<Void> closeConnection(final String sessionId) {
final Promise<Void> promise = new Promise<Void>();
executor.execute(new Runnable() {
@Override
public void run() {
try {
sessionStore.deleteSession(sessionId);
promise.setValue(null);
} catch (SessionStoreException e) {
promise.setException(new GungnirServerException(ErrorCode.ERROR_INTERNAL_ERROR,
e.getMessage()));
}
}
});
return promise;
}
}