package com.google.appengine.api.labs.modules;
import com.google.appengine.api.labs.modules.ModulesServicePb.GetDefaultVersionRequest;
import com.google.appengine.api.labs.modules.ModulesServicePb.GetDefaultVersionResponse;
import com.google.appengine.api.labs.modules.ModulesServicePb.GetHostnameRequest;
import com.google.appengine.api.labs.modules.ModulesServicePb.GetHostnameResponse;
import com.google.appengine.api.labs.modules.ModulesServicePb.GetModulesRequest;
import com.google.appengine.api.labs.modules.ModulesServicePb.GetModulesResponse;
import com.google.appengine.api.labs.modules.ModulesServicePb.GetNumInstancesRequest;
import com.google.appengine.api.labs.modules.ModulesServicePb.GetNumInstancesResponse;
import com.google.appengine.api.labs.modules.ModulesServicePb.GetVersionsRequest;
import com.google.appengine.api.labs.modules.ModulesServicePb.GetVersionsResponse;
import com.google.appengine.api.labs.modules.ModulesServicePb.ModulesServiceError.ErrorCode;
import com.google.appengine.api.labs.modules.ModulesServicePb.SetNumInstancesRequest;
import com.google.appengine.api.labs.modules.ModulesServicePb.StartModuleRequest;
import com.google.appengine.api.labs.modules.ModulesServicePb.StartModuleResponse;
import com.google.appengine.api.labs.modules.ModulesServicePb.StopModuleRequest;
import com.google.appengine.api.labs.modules.ModulesServicePb.StopModuleResponse;
import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.api.ApiProxy.Environment;
import com.google.common.base.Pair;
import com.google.common.base.Splitter;
import com.google.common.collect.Sets;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import java.util.List;
import java.util.Map;
import java.util.Set;
class ModulesServiceImpl implements ModulesService {
protected static final String PACKAGE = "modules";
/**
* Environment attribute key where the instance id is stored.
*
* @see ModulesService#getCurrentInstanceId()
*/
private static final String INSTANCE_ID_ENV_ATTRIBUTE = "com.google.appengine.instance.id";
private Pair<String, String> getCurrentModuleAndVersion() {
Environment env = ApiProxy.getCurrentEnvironment();
String majorVersion = Splitter.on('.').split(env.getVersionId()).iterator().next();
if (majorVersion.contains(":")) {
List<String> result = Splitter.on(':').splitToList(majorVersion);
return Pair.of(result.get(0), result.get(1));
} else {
return Pair.of("default", majorVersion);
}
}
@Override
public String getCurrentModule() {
return getCurrentModuleAndVersion().first;
}
@Override
public String getCurrentVersion() {
return getCurrentModuleAndVersion().second;
}
private static Map<String, Object> getThreadLocalAttributes() {
return ApiProxy.getCurrentEnvironment().getAttributes();
}
@Override
public String getCurrentInstanceId() {
Map<String, Object> env = getThreadLocalAttributes();
if (!env.containsKey(INSTANCE_ID_ENV_ATTRIBUTE)) {
throw new ModulesException("No valid instance id for this instance.");
}
String instanceId = (String) getThreadLocalAttributes().get(INSTANCE_ID_ENV_ATTRIBUTE);
if (instanceId == null) {
throw new ModulesException("No valid instance id for this instance.");
}
return instanceId;
}
private static void makeSyncCall(String method, Message.Builder request, Message.Builder response)
throws ModulesException {
byte[] responseBytes;
try {
responseBytes = ApiProxy.makeSyncCall(PACKAGE, method, request.build().toByteArray());
response.mergeFrom(responseBytes);
} catch (ApiProxy.ApplicationException e) {
switch(ErrorCode.valueOf(e.getApplicationError())) {
case INVALID_MODULE:
throw new InvalidModuleException("Given module is not known.");
case INVALID_VERSION:
throw new InvalidVersionException("Given module version is not known.");
case INVALID_INSTANCES:
throw new InvalidInstanceException("Given instances value is invalid.");
case UNEXPECTED_STATE:
if (method.equals("StartModule")) {
throw new ModuleAlreadyStartedException("Given module version is already started.");
} else if (method.equals("StopModule")) {
throw new ModuleAlreadyStoppedException("Given module version is already stopped.");
}
default:
throw new ModulesException("Unknown error occurred.");
}
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Internal logic error: Response PB could not be parsed.", e);
}
}
@Override
public Set<String> getModules() {
GetModulesResponse.Builder response = GetModulesResponse.newBuilder();
makeSyncCall("GetModules", GetModulesRequest.newBuilder(), response);
return Sets.newHashSet(response.getModuleList());
}
@Override
public Set<String> getVersions(String module) {
GetVersionsRequest.Builder request = GetVersionsRequest.newBuilder();
request.setModule(module);
GetVersionsResponse.Builder response = GetVersionsResponse.newBuilder();
makeSyncCall("GetVersions", request, response);
return Sets.newHashSet(response.getVersionList());
}
@Override
public String getDefaultVersion(String module) {
GetDefaultVersionRequest.Builder request = GetDefaultVersionRequest.newBuilder();
request.setModule(module);
GetDefaultVersionResponse.Builder response = GetDefaultVersionResponse.newBuilder();
makeSyncCall("GetDefaultVersion", request, response);
return response.getVersion();
}
@Override
public long getNumInstances(String module, String version) {
GetNumInstancesRequest.Builder request = GetNumInstancesRequest.newBuilder();
request.setModule(module);
request.setVersion(version);
GetNumInstancesResponse.Builder response = GetNumInstancesResponse.newBuilder();
makeSyncCall("GetNumInstances", request, response);
return response.getInstances();
}
@Override
public void setNumInstances(String module, String version, long instances) {
SetNumInstancesRequest.Builder request = SetNumInstancesRequest.newBuilder();
request.setModule(module);
request.setVersion(version);
request.setInstances(instances);
makeSyncCall("SetNumInstances", request, SetNumInstancesRequest.newBuilder());
}
@Override
public void startModule(String module, String version) {
StartModuleRequest.Builder request = StartModuleRequest.newBuilder();
request.setModule(module);
request.setVersion(version);
makeSyncCall("StartModule", request, StartModuleResponse.newBuilder());
}
@Override
public void stopModule(String module, String version) {
StopModuleRequest.Builder request = StopModuleRequest.newBuilder();
request.setModule(module);
request.setVersion(version);
makeSyncCall("StopModule", request, StopModuleResponse.newBuilder());
}
private String getHostname(GetHostnameRequest.Builder request) {
GetHostnameResponse.Builder response = GetHostnameResponse.newBuilder();
makeSyncCall("GetHostname", request, response);
return response.getHostname();
}
@Override
public String getModuleHostname(String module, String version) {
GetHostnameRequest.Builder request = GetHostnameRequest.newBuilder();
request.setModule(module);
request.setVersion(version);
return getHostname(request);
}
@Override
public String getModuleHostname(String module, String version, int instance) {
GetHostnameRequest.Builder request = GetHostnameRequest.newBuilder();
request.setModule(module);
request.setVersion(version);
request.setInstance(Integer.toString(instance));
return getHostname(request);
}
ModulesServiceImpl() { }
}