com.ibm.g11n.pipeline.client.impl.ServiceClientImpl Maven / Gradle / Ivy
Show all versions of gp-java-client Show documentation
/*
* Copyright IBM Corp. 2015
*
* Licensed 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 com.ibm.g11n.pipeline.client.impl;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.ibm.g11n.pipeline.client.BundleData;
import com.ibm.g11n.pipeline.client.BundleDataChangeSet;
import com.ibm.g11n.pipeline.client.BundleMetrics;
import com.ibm.g11n.pipeline.client.LanguageMetrics;
import com.ibm.g11n.pipeline.client.MTServiceBindingData;
import com.ibm.g11n.pipeline.client.NewBundleData;
import com.ibm.g11n.pipeline.client.NewResourceEntryData;
import com.ibm.g11n.pipeline.client.NewTranslationConfigData;
import com.ibm.g11n.pipeline.client.NewUserData;
import com.ibm.g11n.pipeline.client.ResourceEntryData;
import com.ibm.g11n.pipeline.client.ResourceEntryDataChangeSet;
import com.ibm.g11n.pipeline.client.ReviewStatusMetrics;
import com.ibm.g11n.pipeline.client.ServiceAccount;
import com.ibm.g11n.pipeline.client.ServiceClient;
import com.ibm.g11n.pipeline.client.ServiceException;
import com.ibm.g11n.pipeline.client.ServiceInfo;
import com.ibm.g11n.pipeline.client.ServiceInstanceInfo;
import com.ibm.g11n.pipeline.client.TranslationConfigData;
import com.ibm.g11n.pipeline.client.TranslationStatus;
import com.ibm.g11n.pipeline.client.UserData;
import com.ibm.g11n.pipeline.client.UserDataChangeSet;
import com.ibm.g11n.pipeline.client.impl.BundleDataImpl.RestBundle;
import com.ibm.g11n.pipeline.client.impl.MTServiceBindingDataImpl.RestMTServiceBinding;
import com.ibm.g11n.pipeline.client.impl.ResourceEntryDataImpl.RestResourceEntry;
import com.ibm.g11n.pipeline.client.impl.ServiceInfoImpl.ExternalServiceInfoImpl.RestExternalServiceInfo;
import com.ibm.g11n.pipeline.client.impl.ServiceInstanceInfoImpl.RestServiceInstanceInfo;
import com.ibm.g11n.pipeline.client.impl.ServiceResponse.Status;
import com.ibm.g11n.pipeline.client.impl.TranslationConfigDataImpl.RestTranslationConfigData;
import com.ibm.g11n.pipeline.client.impl.UserDataImpl.RestUser;
/**
* ServiceClient implementation by GSON and JDK's HttpURLConnection.
*
* @author Yoshito Umaoka
*/
public class ServiceClientImpl extends ServiceClient {
public ServiceClientImpl(ServiceAccount account) {
super(account);
}
//
// Service API
//
private static class GetServiceInfoResponse extends ServiceResponse {
Map> supportedTranslation;
Collection externalServices;
}
@Override
public ServiceInfo getServiceInfo() throws ServiceException {
GetServiceInfoResponse resp = invokeApi(
"GET",
"$service/v2/info",
null,
GetServiceInfoResponse.class,
true);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new ServiceInfoImpl(resp.supportedTranslation, resp.externalServices);
}
//
// Instance API
//
private static class GetServiceInstanceInfoResponse extends ServiceResponse {
RestServiceInstanceInfo instance;
}
@Override
public ServiceInstanceInfo getServiceInstanceInfo() throws ServiceException {
GetServiceInstanceInfoResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/instance/info",
null,
GetServiceInstanceInfoResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new ServiceInstanceInfoImpl(resp.instance);
}
//
// Bundle API
//
private static class GetBundleListResponse extends ServiceResponse {
Set bundleIds;
}
@Override
public Set getBundleIds() throws ServiceException {
GetBundleListResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/bundles",
null,
GetBundleListResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return resp.bundleIds;
}
@Override
public void createBundle(String bundleId, NewBundleData newBundleData)
throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
if (newBundleData == null) {
throw new IllegalArgumentException("newBundleData must be specified.");
}
Gson gson = createGson(NewBundleData.class.getName());
String jsonBody = gson.toJson(newBundleData, NewBundleData.class);
ServiceResponse resp = invokeApi(
"PUT",
account.getInstanceId() + "/v2/bundles/" + bundleId,
jsonBody,
ServiceResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
}
private static class GetBundleInfoResponse extends ServiceResponse {
RestBundle bundle;
}
@Override
public BundleData getBundleInfo(String bundleId) throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
GetBundleInfoResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/bundles/" + bundleId,
null,
GetBundleInfoResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new BundleDataImpl(resp.bundle);
}
private static class GetBundleMetricsResponse extends ServiceResponse {
private Map> translationStatusMetricsByLanguage;
private Map reviewStatusMetricsByLanguage;
private Map> partnerStatusMetricsByLanguage;
}
@Override
public BundleMetrics getBundleMetrics(String bundleId) throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
GetBundleMetricsResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/bundles/" + bundleId
+ "?fields=translationStatusMetricsByLanguage,reviewStatusMetricsByLanguage,partnerStatusMetricsByLanguage",
null,
GetBundleMetricsResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new BundleMetricsImpl(
resp.translationStatusMetricsByLanguage,
resp.reviewStatusMetricsByLanguage,
resp.partnerStatusMetricsByLanguage);
}
@Override
public void updateBundle(String bundleId, BundleDataChangeSet changeSet)
throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
if (changeSet == null) {
throw new IllegalArgumentException("changeSet must be specified.");
}
Gson gson = createGson(BundleDataChangeSet.class.getName());
String jsonBody = gson.toJson(changeSet, BundleDataChangeSet.class);
ServiceResponse resp = invokeApi(
"POST",
account.getInstanceId() + "/v2/bundles/" + bundleId,
jsonBody,
ServiceResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
}
@Override
public void deleteBundle(String bundleId) throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
GetBundleInfoResponse resp = invokeApi(
"DELETE",
account.getInstanceId() + "/v2/bundles/" + bundleId,
null,
GetBundleInfoResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
}
private static class GetResourceStringsResponse extends ServiceResponse {
Map resourceStrings;
}
@Override
public Map getResourceStrings(String bundleId,
String language, boolean fallback) throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
if (language == null || language.isEmpty()) {
throw new IllegalArgumentException("language must be specified.");
}
StringBuilder endpoint = new StringBuilder();
endpoint
.append(account.getInstanceId())
.append("/v2/bundles/")
.append(bundleId)
.append("/")
.append(language);
if (fallback) {
endpoint.append("?fallback=true");
}
GetResourceStringsResponse resp = invokeApi(
"GET",
endpoint.toString(),
null,
GetResourceStringsResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return resp.resourceStrings;
}
private static class GetResourceEntriesResponse extends ServiceResponse {
Map resourceEntries;
}
@Override
public Map getResourceEntries(String bundleId,
String language) throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
if (language == null || language.isEmpty()) {
throw new IllegalArgumentException("language must be specified.");
}
GetResourceEntriesResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/bundles/" + bundleId + "/" + language
+ "?fields=resourceEntries",
null,
GetResourceEntriesResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
Map resultEntries = new TreeMap();
if (resp.resourceEntries != null && !resp.resourceEntries.isEmpty()) {
for (Entry entry : resp.resourceEntries.entrySet()) {
resultEntries.put(entry.getKey(),
new ResourceEntryDataImpl(entry.getValue()));
}
}
return resultEntries;
}
private static class GetLanguageMetricsResponse extends ServiceResponse {
private EnumMap translationStatusMetrics;
private ReviewStatusMetrics reviewStatusMetrics;
private Map partnerStatusMetrics;
}
@Override
public LanguageMetrics getLanguageMetrics(String bundleId, String language)
throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
if (language == null || language.isEmpty()) {
throw new IllegalArgumentException("language must be specified.");
}
GetLanguageMetricsResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/bundles/" + bundleId + "/" + language
+ "?fields=translationStatusMetrics,reviewStatusMetrics,partnerStatusMetrics",
null,
GetLanguageMetricsResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new LanguageMetricsImpl(resp.translationStatusMetrics,
resp.reviewStatusMetrics, resp.partnerStatusMetrics);
}
@Override
public void uploadResourceStrings(String bundleId, String language,
Map strings) throws ServiceException {
if (strings == null || strings.isEmpty()) {
throw new IllegalArgumentException("strings must be specified.");
}
Map newResourceEntries = new HashMap<>(strings.size());
for (Entry res : strings.entrySet()) {
String key = res.getKey();
String value = res.getValue();
if (value == null) {
newResourceEntries.put(key, null);
} else {
NewResourceEntryData newEntry = new NewResourceEntryData(value);
newResourceEntries.put(key, newEntry);
}
}
uploadResourceEntries(bundleId, language, newResourceEntries);
}
@Override
public void uploadResourceEntries(String bundleId, String language,
Map newResourceEntries)
throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
if (language == null || language.isEmpty()) {
throw new IllegalArgumentException("language must be specified.");
}
if (newResourceEntries == null || newResourceEntries.isEmpty()) {
throw new IllegalArgumentException("newResourceEntries must be specified.");
}
Gson gson = createGson(Map.class.getName());
String jsonBody = gson.toJson(newResourceEntries, Map.class);
ServiceResponse resp = invokeApi(
"PUT",
account.getInstanceId() + "/v2/bundles/" + bundleId + "/" + language,
jsonBody,
ServiceResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
}
@Override
public void updateResourceStrings(String bundleId, String language,
Map strings, boolean resync)
throws ServiceException {
if ((strings == null || strings.isEmpty()) && !resync) {
throw new IllegalArgumentException("strings must be specified when resync is false.");
}
Map resourceEntries = null;
if (strings != null) {
resourceEntries = new HashMap<>(strings.size());
for (Entry stringRes : strings.entrySet()) {
String key = stringRes.getKey();
String value = stringRes.getValue();
if (value == null) {
resourceEntries.put(key, null);
} else {
ResourceEntryDataChangeSet entry = new ResourceEntryDataChangeSet();
entry.setValue(value);
resourceEntries.put(key, entry);
}
}
}
updateResourceEntries(bundleId, language, resourceEntries, resync);
}
@Override
public void updateResourceEntries(String bundleId, String language,
Map resourceEntries, boolean resync)
throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
if (language == null || language.isEmpty()) {
throw new IllegalArgumentException("language must be specified.");
}
String jsonBody = null;
if (resourceEntries == null || resourceEntries.isEmpty()) {
jsonBody = "{}";
} else {
Gson gson = createGson(Map.class.getName());
jsonBody = gson.toJson(resourceEntries, Map.class);
}
ServiceResponse resp = invokeApi(
"POST",
account.getInstanceId() + "/v2/bundles/" + bundleId + "/" + language,
jsonBody,
ServiceResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
}
private static class GetResourceEntryResponse extends ServiceResponse {
RestResourceEntry resourceEntry;
}
@Override
public ResourceEntryData getResourceEntry(String bundleId, String language,
String resKey) throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
if (language == null || language.isEmpty()) {
throw new IllegalArgumentException("language must be specified.");
}
if (resKey == null || resKey.isEmpty()) {
throw new IllegalArgumentException("resKey must be specified.");
}
GetResourceEntryResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/bundles/" + bundleId + "/" + language
+ "/" + resKey,
null,
GetResourceEntryResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new ResourceEntryDataImpl(resp.resourceEntry);
}
@Override
public void updateResourceEntry(String bundleId, String language,
String resKey, ResourceEntryDataChangeSet changeSet)
throws ServiceException {
if (bundleId == null || bundleId.isEmpty()) {
throw new IllegalArgumentException("bundleId must be specified.");
}
if (language == null || language.isEmpty()) {
throw new IllegalArgumentException("language must be specified.");
}
if (resKey == null || resKey.isEmpty()) {
throw new IllegalArgumentException("resKey must be specified.");
}
if (changeSet == null) {
throw new IllegalArgumentException("changeSet must be specified.");
}
Gson gson = createGson(ResourceEntryDataChangeSet.class.getName());
String jsonBody = gson.toJson(changeSet, ResourceEntryDataChangeSet.class);
ServiceResponse resp = invokeApi(
"POST",
account.getInstanceId() + "/v2/bundles/" + bundleId + "/" + language
+ "/" + resKey,
jsonBody,
ServiceResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
}
//
// User API
//
private static class GetUsersResponse extends ServiceResponse {
Map users;
}
@Override
public Map getUsers() throws ServiceException {
GetUsersResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/users",
null,
GetUsersResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
Map resultUsers = new TreeMap();
for (Entry userEntry : resp.users.entrySet()) {
String id = userEntry.getKey();
RestUser user = userEntry.getValue();
resultUsers.put(id, new UserDataImpl(user));
}
return resultUsers;
}
private static class UserResponse extends ServiceResponse {
@SuppressWarnings("unused")
String id;
RestUser user;
}
@Override
public UserData createUser(NewUserData newUserData)
throws ServiceException {
if (newUserData == null) {
throw new IllegalArgumentException("newUserData must be specified.");
}
Gson gson = createGson(NewUserData.class.getName());
String jsonBody = gson.toJson(newUserData, NewUserData.class);
UserResponse resp = invokeApi(
"POST",
account.getInstanceId() + "/v2/users/new",
jsonBody,
UserResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new UserDataImpl(resp.user);
}
@Override
public UserData getUser(String userId) throws ServiceException {
if (userId == null || userId.isEmpty()) {
throw new IllegalArgumentException("userId must be specified.");
}
UserResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/users/" + userId,
null,
UserResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new UserDataImpl(resp.user);
}
@Override
public UserData updateUser(String userId, UserDataChangeSet changeSet,
boolean resetPassword) throws ServiceException {
if (userId == null || userId.isEmpty()) {
throw new IllegalArgumentException("userId must be specified.");
}
if (changeSet == null && !resetPassword) {
throw new IllegalArgumentException("changeSet must be specified when resetPassword is false");
}
StringBuilder endpoint = new StringBuilder();
endpoint
.append(account.getInstanceId())
.append("/v2/users/")
.append(userId);
if (resetPassword) {
endpoint.append("?resetPassword=true");
}
String jsonBody = null;
if (changeSet == null) {
jsonBody = "{}";
} else {
Gson gson = createGson(UserDataChangeSet.class.getName());
jsonBody = gson.toJson(changeSet, UserDataChangeSet.class);
}
UserResponse resp = invokeApi(
"POST",
endpoint.toString(),
jsonBody,
UserResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new UserDataImpl(resp.user);
}
@Override
public void deleteUser(String userId) throws ServiceException {
if (userId == null || userId.isEmpty()) {
throw new IllegalArgumentException("userId must be specified.");
}
ServiceResponse resp = invokeApi(
"DELETE",
account.getInstanceId() + "/v2/users/" + userId,
null,
ServiceResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
}
//
// Config API
//
private static class MTBindingsResponse extends ServiceResponse {
Map mtServiceBindings;
}
@Override
public Map getAllMTServiceBindings() throws ServiceException {
MTBindingsResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/config/mt",
null,
MTBindingsResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
Map resultBindings = new TreeMap<>();
for (Entry entry : resp.mtServiceBindings.entrySet()) {
resultBindings.put(entry.getKey(), new MTServiceBindingDataImpl(entry.getValue()));
}
return resultBindings;
}
private static class AvailableMTLanguagesResponse extends ServiceResponse {
Map>> availableLanguages;
}
@Override
public Map>> getAvailableMTLanguages()
throws ServiceException {
AvailableMTLanguagesResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/config/mt",
null,
AvailableMTLanguagesResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return resp.availableLanguages;
}
private static class GetMTServiceBindingResponse extends ServiceResponse {
RestMTServiceBinding mtServiceBinding;
}
@Override
public MTServiceBindingData getMTServiceBinding(String mtServiceInstanceId)
throws ServiceException {
GetMTServiceBindingResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/config/mt/" + mtServiceInstanceId,
null,
GetMTServiceBindingResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new MTServiceBindingDataImpl(resp.mtServiceBinding);
}
private static class TranslationConfigsResponse extends ServiceResponse {
Map> translationConfigs;
}
@Override
public Map> getAllTranslationConfigs()
throws ServiceException {
TranslationConfigsResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/config/trans",
null,
TranslationConfigsResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return resp.translationConfigs;
}
private static class ConfiguredMTLanguagesResponse extends ServiceResponse {
Map> mtLanguages;
}
@Override
public Map> getConfiguredMTLanguages()
throws ServiceException {
ConfiguredMTLanguagesResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/config/trans",
null,
ConfiguredMTLanguagesResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return resp.mtLanguages;
}
@Override
public void putTranslationConfig(String sourceLanguage, String targetLanguage,
NewTranslationConfigData configData) throws ServiceException {
if (configData == null) {
throw new IllegalArgumentException("configData must be specified");
}
Gson gson = createGson(NewTranslationConfigData.class.getName());
String jsonBody = gson.toJson(configData, NewTranslationConfigData.class);
ServiceResponse resp = invokeApi(
"PUT",
account.getInstanceId() + "/v2/config/trans/" + sourceLanguage + "/" + targetLanguage,
jsonBody,
ServiceResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
}
private static class TranslationConfigResponse extends ServiceResponse {
RestTranslationConfigData config;
}
@Override
public TranslationConfigData getTranslationConfig(String sourceLanguage,
String targetLanguage) throws ServiceException {
TranslationConfigResponse resp = invokeApi(
"GET",
account.getInstanceId() + "/v2/config/trans/" + sourceLanguage + "/" + targetLanguage,
null,
TranslationConfigResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
return new TranslationConfigDataImpl(resp.config);
}
@Override
public void deleteTranslationConfig(String sourceLanguage,
String targetLanguage) throws ServiceException {
ServiceResponse resp = invokeApi(
"DELETE",
account.getInstanceId() + "/v2/config/trans/" + sourceLanguage + "/" + targetLanguage,
null,
ServiceResponse.class);
if (resp.getStatus() == Status.ERROR) {
throw new ServiceException(resp.getMessage());
}
}
//
// Private method used for calling REST endpoints
//
private T invokeApi(String method, String apiPath, String inJson, Class classOfT)
throws ServiceException {
return invokeApi(method, apiPath, inJson, classOfT, false);
}
private T invokeApi(String method, String apiPath, String inJson, Class classOfT,
boolean anonymous) throws ServiceException {
T responseObj = null;
try (StringWriter out = new StringWriter()
) {
String urlStr = account.getUrl() + "/" + apiPath;
URL targetUrl = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)targetUrl.openConnection();
conn.setRequestMethod(method);
conn.setRequestProperty("Accept", "application/json");
// Date header
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
String dateHeader = sdf.format(new Date());
conn.setRequestProperty("Date", dateHeader);
// Request body in UTF-8
byte[] requestBody = null;
if (inJson != null) {
requestBody = inJson.getBytes("UTF-8");
}
// Authorization header
if (!anonymous) {
StringBuilder authHeader = new StringBuilder();
String uid = account.getUserId();
String secret = account.getPassword();
switch (scheme) {
case BASIC:
authHeader.append("Basic ");
authHeader.append(getBasicCredential(uid, secret));
break;
case HMAC:
authHeader.append("GaaS-HMAC ");
authHeader.append(
getHmacCredential(uid, secret,
method, urlStr, dateHeader, requestBody));
break;
}
conn.setRequestProperty("Authorization", authHeader.toString());
}
// write the JSON body
if (requestBody != null) {
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
conn.getOutputStream().write(requestBody);
}
// receiving response
// int httpStatus = conn.getResponseCode();
String ctype = conn.getContentType();
if (!ctype.equalsIgnoreCase("application/json")) {
throw new ServiceException("Received non-JSON response from " + method + " " + urlStr);
} else {
InputStream is = conn.getErrorStream();
if (is == null) {
is = conn.getInputStream();
}
try (BufferedReader br =
new BufferedReader(new InputStreamReader(is, "UTF-8"))
) {
char[] buf = new char[1024];
int numChars;
while ((numChars = br.read(buf)) != -1) {
out.write(buf, 0, numChars);
}
}
}
out.flush();
String strResponse = out.toString();
StringReader resReader = new StringReader(strResponse);
Gson gson = createGson(classOfT.getName());
responseObj = gson.fromJson(resReader, classOfT);
} catch (Exception e) {
// Error handling
String errMsg = "Error while processing API request " + method + " " + apiPath;
throw new ServiceException(errMsg, e);
}
return responseObj;
}
private static final char SEP = ':';
//
// Basic credential
//
private static String getBasicCredential(String uid, String secret) {
if (uid == null || secret == null) {
throw new IllegalArgumentException("uid and secrent must not be null");
}
String token = uid + ":" + secret;
try {
return DatatypeConverter.printBase64Binary(token.getBytes("ISO-8859-1"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//
// Globalization Pipeline HMAC credential
//
private static final byte LINE_SEP = 0x0A;
private static final String ENC = "ISO-8859-1";
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
private static String getHmacCredential(String uid, String secret, String method, String url,
String rfc1123Date, byte[] body) {
if (uid == null || method == null || url == null || rfc1123Date == null) {
throw new IllegalArgumentException("uid, secret, method, url and rfc1123Date must not be null");
}
StringBuilder credential = new StringBuilder(uid);
credential.append(SEP);
try {
// Actual secret used by GaaS looks like: "zg5SlD+ftXYRIZDblLgEA/ILkkCNqE1y"
// This is actually a base64 encoded random bytes. Although we could
// get original random bytes by decoding base64, but we don't do it because
// it can be any 'String' in future. We simply get byte[] expression of the
// secret 'String' (which is restricted to a subset of US-ASCII).
SecretKeySpec key = new SecretKeySpec(secret.getBytes(ENC), HMAC_SHA1_ALGORITHM);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(key);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(method.getBytes(ENC));
baos.write(LINE_SEP);
baos.write(url.getBytes(ENC));
baos.write(LINE_SEP);
baos.write(rfc1123Date.getBytes(ENC));
baos.write(LINE_SEP);
if (body != null) {
baos.write(body);
}
byte[] msg = baos.toByteArray();
// signing
byte[] hmac = mac.doFinal(msg);
credential.append(DatatypeConverter.printBase64Binary(hmac));
} catch (IOException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}
return credential.toString();
}
//
// Custom JSON deserialization code supporting Java Enum
//
// Custom type adapter for TranslationStatus enum. This adapter implementation
// maps unknown status type to TranslationStatus.UNKNOWN. This fallback mapping
// allow GP REST server to introduce a new status without breaking Java SDK
// client code.
private static class TranslationStatusAdapter extends TypeAdapter {
@Override
public TranslationStatus read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
TranslationStatus status = null;
String statusStr = in.nextString();
try {
status = TranslationStatus.valueOf(statusStr.toUpperCase());
} catch (IllegalArgumentException e) {
// use UNKNOWN as fallback
status = TranslationStatus.UNKNOWN;
}
return status;
}
@Override
public void write(JsonWriter out, TranslationStatus value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.value(value.name());
}
}
private static class EnumMapInstanceCreator, V>
implements InstanceCreator> {
private final Class enumClazz;
public EnumMapInstanceCreator(final Class enumClazz) {
super();
this.enumClazz = enumClazz;
}
@Override
public EnumMap createInstance(final Type type) {
return new EnumMap(enumClazz);
}
}
// TypeAdapterFactory used for serializing map entries with null value.
// updateResourceStrings and some other REST APIs handles properties
// with null value as deletion directive.
static class NullMapValueTypeAdapterFactory implements TypeAdapterFactory {
@Override
public TypeAdapter create(Gson gson, TypeToken typeToken) {
// The special handling is only applicable to Map objects.
Class> rawType = typeToken.getRawType();
if (!Map.class.isAssignableFrom(rawType)) {
return null;
}
final TypeAdapter delegate = gson.getDelegateAdapter(this, typeToken);
return new TypeAdapter() {
public void write(JsonWriter out, T value) throws IOException {
boolean serializeNulls = out.getSerializeNulls();
if (!serializeNulls) {
// force null serialization
out.setSerializeNulls(true);
}
delegate.write(out, value);
if (!serializeNulls) {
// reset
out.setSerializeNulls(false);
}
}
public T read(JsonReader in) throws IOException {
return delegate.read(in);
}
};
}
}
/**
* Creates a new Gson object
*
* @param className A class name used for serialization/deserialization.
* Note: This implementation does not use this argument
* for now. If we need different kinds of type adapters
* depending on class, the implementation might be updated
* to set up appropriate set of type adapters.
* @return A Gson object
*/
private static Gson createGson(String className) {
GsonBuilder builder = new GsonBuilder();
// ISO8601 date format support
builder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
builder.registerTypeAdapter(TranslationStatus.class,
new TranslationStatusAdapter());
builder.registerTypeAdapter(
new TypeToken>() {}.getType(),
new EnumMapInstanceCreator(TranslationStatus.class));
builder.registerTypeAdapterFactory(new NullMapValueTypeAdapterFactory());
return builder.create();
}
}