package org.yaac.server.service;
import java.util.logging.Logger;
import javax.inject.Inject;
import org.antlr.runtime.RecognitionException;
import org.yaac.client.service.EGQLService;
import org.yaac.server.delegate.UserServiceDelegate;
import org.yaac.server.egql.Command;
import org.yaac.server.egql.EGQLUtil;
import org.yaac.server.egql.InsertItem;
import org.yaac.server.egql.InsertStatement;
import org.yaac.server.egql.Statement;
import org.yaac.server.egql.exception.EGQLException;
import org.yaac.server.egql.processor.ProcessContext;
import org.yaac.server.egql.processor.ProcessData.ProcessDataRecord;
import org.yaac.server.egql.processor.ProcessorManager;
import org.yaac.server.util.DatastoreUtil;
import org.yaac.shared.YaacException;
import org.yaac.shared.egql.Request;
import org.yaac.shared.egql.Response;
import org.yaac.shared.egql.ResponseStatus;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.tools.pipeline.PipelineService;
/**
* @author Max Zhu (thebbsky@gmail.com)
*
*/
public class EGQLServiceImpl implements EGQLService {
/**
*
*/
private final Logger logger;
private final PipelineService pipeline;
private final UserServiceDelegate userService;
@Inject
EGQLServiceImpl(Logger logger,
PipelineService pipeline,
UserServiceDelegate userService) {
super();
this.logger = logger;
this.pipeline = pipeline;
this.userService = userService;
}
@Override
public Response execute(Request req) {
try {
Command cmd = EGQLUtil.parser(req.getCommand()).command().cmd;
// validate all statements
for (Statement stmt : cmd.getStatements()) {
stmt.validate();
}
// validation passed, kick off all statements
Response resp = new Response();
for (Statement stmt : cmd.getStatements()) {
// TODO : how to handle exceptions for simple statement?
if (stmt.isSimpleStatement()) {
if (stmt instanceof InsertStatement) {
InsertStatement insertStmt = (InsertStatement) stmt;
resp.getImmediateStmts().add(insertStmt.getRawStatement());
executeSimpleInsert(insertStmt);
} else {
// TODO supports other simple statements
}
} else {
ProcessContext context = new ProcessContext();
context.setClientId(userService.getCurrentUser().getEmail());
String pipelineId = pipeline.startNewPipeline(new ProcessorManager(), stmt.generateProcessors(), context);
resp.add(pipelineId, stmt.getRawStatement());
logger.info("started pipeline " + pipelineId + " for query: " + stmt.getRawStatement());
}
}
resp.setStatus(ResponseStatus.SUCCESS);
return resp;
} catch (RecognitionException e) { // syntax error (should not happen as ANTLR will always try to recover from error)
Response response = new Response();
response.setStatus(ResponseStatus.SYNTAX_ERROR);
response.setMessage(e.getMessage());
return response;
} catch (YaacException e) { // syntax error (throw from EGQLParser.displayRecognitionError )
Response response = new Response();
response.setStatus(ResponseStatus.SYNTAX_ERROR);
response.setMessage(e.getErrorMsg());
return response;
} catch (EGQLException e) { // logical exception
Response response = new Response();
response.setStatus(ResponseStatus.LOGICAL_ERROR);
response.setErrorCode(e.getErrorCode());
return response;
}
}
private void executeSimpleInsert(InsertStatement stmt) {
Entity e = null;
InsertItem keyItem = stmt.keyItem();
if (keyItem == null) {
e = new Entity(stmt.getKind());
} else {
e = new Entity((Key) keyItem.getE().evaluate((ProcessDataRecord)null).getPayload()); // it should return a key
}
for(InsertItem item : stmt.propertyItems()) {
Object val = item.getE().evaluate((ProcessDataRecord)null).getPayload(); // evaluation type
val = DatastoreUtil.toDatastoreType(val); // datastore type
if (item.getIndexed()) {
e.setProperty(item.getIdentity(), val);
} else {
e.setUnindexedProperty(item.getIdentity(), val);
}
}
DatastoreServiceFactory.getAsyncDatastoreService().put(e);
}
}