![JAR search and dependency download from the Maven repository](/logo.png)
com.lionbridge.content.sdk.ContentAPI Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of liox-content-sdk-java Show documentation
Show all versions of liox-content-sdk-java Show documentation
Client for Lionbridge Ondemand API
package com.lionbridge.content.sdk;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.lionbridge.content.sdk.models.*;
import com.lionbridge.content.sdk.models.templates.AddProjectTemplate;
import com.lionbridge.content.sdk.models.templates.GenerateQuoteTemplate;
import com.lionbridge.content.sdk.utilities.ZipUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BufferedHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import static java.lang.String.*;
import static java.lang.String.format;
/**
* This is the main class for interacting with the Lionbridge Content API.
* To use the Content API:
*
* - instantiate an instance using your access key and secret
* - use the ContentAPI instance to send/received information
* - display the results of your operation
*
* Here is a quick example that lists the services available:
* {@code
* try {
* ContentAPI contentApi = new ContentAPI(
* "LIOX_API_ACCESS_KEY_ID",
* "LIOX_API_SECRET_KEY",
* "LIOX_API_ENDPOINT",
* "LIOX_API_DEFAULT_CURRENCY"
* );
*
* ServiceList serviceList = contentApi.listServices();
* serviceList.getServices().stream().map(service -> service.getName()).forEach(System.out::println);
*
* } catch (ContentAPIException e) {
* // Deal with exception...
* }
* }
*/
public class ContentAPI {
private static final String VERSION = "2016-03-15";
private static final int TOO_MANY_REQUESTS_STATUS_CODE = 429;
private static final long TOO_MANY_REQUESTS_TIMEOUT_MS = 60000;
private final Logger LOGGER = LoggerFactory.getLogger(ContentAPI.class);
private String defaultCurrency;
private String host;
private String keyId;
private String rootPath;
private String scheme;
private String secretKey;
private int port;
public ContentAPI(final String keyId, final String secretKey, final String endpoint) throws ContentAPIException {
this(keyId, secretKey, endpoint, null);
}
public ContentAPI(final String keyId, final String secretKey, final String endpoint, final String defaultCurrency) throws ContentAPIException {
this.keyId = keyId;
this.secretKey = secretKey;
try {
this.setUriDefaults(endpoint);
} catch(Exception e) {
throw new ContentAPIException(e);
}
this.setDefaultCurrency(defaultCurrency);
}
/**
* Determine if the endpoint and credentials you provided are valid
* @return A boolean representing validity
* @throws ContentAPIException
*/
public boolean isValid() throws ContentAPIException {
try (CloseableHttpClient httpClient = getCloseableClient()) {
HttpGet request = generateGetRequest("/account/info");
CloseableHttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
return true;
} else {
return false;
}
} catch(Exception e) {
throw new ContentAPIException(e);
}
}
/**
* Returns a list of Locales supported for translation
* @return List of Locales
* @throws ContentAPIException for any caught exception
*/
public LocaleList listLocales() throws ContentAPIException {
LocaleList localeList = new LocaleList();
try (CloseableHttpClient httpClient = getCloseableClient()) {
HttpGet request = generateGetRequest("/locales");
CloseableHttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
localeList = new XmlMapper().readValue(
response.getEntity().getContent(),
LocaleList.class
);
} else {
handleErrorResponse(response);
}
} catch(Exception e) {
throw new ContentAPIException(e);
}
return localeList;
}
/**
* Returns a list of Services provided by Lionbridge
* @return List of Services
* @throws ContentAPIException for any caught exception
*/
public ServiceList listServices() throws ContentAPIException {
ServiceList serviceList = new ServiceList();
try (final CloseableHttpClient httpClient = getCloseableClient()) {
HttpGet request = generateGetRequest("/services");
CloseableHttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
serviceList = new XmlMapper().readValue(
response.getEntity().getContent(),
ServiceList.class
);
} else {
handleErrorResponse(response);
}
} catch(Exception e) {
throw new ContentAPIException(e);
}
return serviceList;
}
/**
* Get information about a specific service
* @param serviceId The ID of the service you want
* @return The requested Service
* @throws ContentAPIException for any caught exception
*/
public Service getService(int serviceId) throws ContentAPIException {
Service service = new Service();
try (final CloseableHttpClient httpClient = getCloseableClient()) {
HttpGet request = generateGetRequest(format("/services/%d", serviceId));
CloseableHttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
service = new XmlMapper().readValue(
response.getEntity().getContent(),
Service.class
);
} else {
handleErrorResponse(response);
}
} catch(Exception e) {
throw new ContentAPIException(e);
}
return service;
}
/**
* Set the class defaults based on an input URI path
* @param uriPath the input path to use for the URI
* @throws URISyntaxException
*/
public void setUriDefaults(final String uriPath) throws URISyntaxException {
URI uri = new URI(uriPath);
scheme = uri.getScheme();
host = uri.getHost();
port = uri.getPort();
rootPath = uri.getPath();
}
/**
* Returns the default currency
* @return The default currency as a String
*/
public String getDefaultCurrency() {
return defaultCurrency;
}
public void setDefaultCurrency(final String defaultCurrency) {
this.defaultCurrency = defaultCurrency;
}
/**
* Add a file for translation
* @param mimeType type of the file -
* This is an enum based on the MIME text, i.e. "text/plain"
* @param inputFile the file selected for translation
* @param languageCode the source language (the current language)
* @return file information returned from the API
* @throws ContentAPIException on any encountered exception
*/
public LBFile addFile(final String mimeType, final File inputFile, final String languageCode) throws ContentAPIException {
LBFile file = null;
try (final CloseableHttpClient httpClient = getCloseableClient()) {
StringBuilder pathBuilder = new StringBuilder();
// TODO the replace() is a workaround for rejected images with a space in the file name
final String encodedFileName = URLEncoder.encode(inputFile.getName(), "UTF-8").replace('+', '-');
if (languageCode == null) {
//sample URI: /files/add/detect-language/file-name-here.xml
pathBuilder.append("/files/add/detect-language/").append(encodedFileName);
} else {
//sample URI: /files/add/en-us/file-name-here.xml
pathBuilder.append("/files/add/").append(languageCode).append("/").append(encodedFileName);
}
LOGGER.debug("addFile: pathBuilder=" + pathBuilder.toString() +
", mime=" + mimeType +
", rootPath=" + this.rootPath);
// build request and header
final HttpPost request = generatePostRequest(pathBuilder.toString());
LOGGER.trace("Request URL={}", request.getURI().toString());
request.setHeader(HttpHeaders.CONTENT_TYPE, mimeType);
LOGGER.debug("addFile Request Content Header={}", request.getFirstHeader(HttpHeaders.CONTENT_TYPE));
LOGGER.trace("addFile Request Accept Header={}", request.getFirstHeader(HttpHeaders.ACCEPT));
// attach the file in the request body
FileEntity inputEntity = new FileEntity(inputFile);
request.setEntity(inputEntity);
LOGGER.debug("API addFile Http request={}", request.toString());
boolean sendFile = true;
while (sendFile) {
// send the request and handle the response
final CloseableHttpResponse response = httpClient.execute(request);
LOGGER.debug("API addFile Response={}", response.toString());
int statusCode = response.getStatusLine().getStatusCode();
LOGGER.trace("API addFile Response phrase={}", response.getStatusLine().getReasonPhrase());
if (statusCode == HttpStatus.SC_CREATED) {
LBFile fileInfo = new XmlMapper().readValue(
response.getEntity().getContent(),
LBFile.class
);
LOGGER.debug("API handleAddFileResponse assetId={}", fileInfo.getAssetId());
file = fileInfo;
sendFile = false;
} else if (statusCode == TOO_MANY_REQUESTS_STATUS_CODE) {
// wait one minute and try it again
LOGGER.info("API addFile: Got too many requests.");
Thread.sleep(TOO_MANY_REQUESTS_TIMEOUT_MS);
} else {
handleErrorResponse(response);
sendFile = false;
}
}
} catch (ContentAPIException e) {
LOGGER.error("AddFile EXCEPTION", e);
throw e;
} catch (Exception e) {
LOGGER.error("AddFile EXCEPTION", e);
throw new ContentAPIException(e);
}
return file;
}
/**
* Add a translation Project
* @param projectName name of the project to create
* @param translationOptions selected translation options
* @param fileAssetIds file IDs to be translated
* (must already have been added using addFile)
* @return project information returned from the API
* @throws ContentAPIException on any encountered exception
*/
public Project addProject(
final String projectName,
final TranslationOptions translationOptions,
final List fileAssetIds
) throws ContentAPIException {
return addProject(projectName, translationOptions, fileAssetIds, null, null);
}
/**
* Add a translation Project
* @param projectName name of the project to create
* @param options selected translation options
* @param fileAssetIds file IDs to be translated
* (must already have been added using addFile)
* @param referenceFileAssetIds
* (must already have been added using addFile)
* @return project information returned from the API
* @throws ContentAPIException on any encountered exception
*/
public Project addProject(
final String projectName,
final TranslationOptions options,
final List fileAssetIds,
final List referenceFileAssetIds,
List notifications
) throws ContentAPIException {
Project result = null;
try (final CloseableHttpClient httpClient = getCloseableClient()) {
if (options == null) {
throw new Exception("Must specify options");
}
final HttpPost request = generatePostRequest("/projects/add");
AddProjectTemplate addProjectTemplate = new AddProjectTemplate(projectName, options, fileAssetIds, referenceFileAssetIds, notifications);
StringEntity entity = new StringEntity(addProjectTemplate.toXmlString());
request.setEntity(entity);
final CloseableHttpResponse response = httpClient.execute(request);
final int statusCode = response.getStatusLine().getStatusCode();
LOGGER.trace("API addProject statusCode = {}", statusCode);
if (statusCode == HttpStatus.SC_CREATED) {
result = new XmlMapper().readValue(
response.getEntity().getContent(),
Project.class
);
} else {
this.handleErrorResponse(response);
}
} catch (ContentAPIException contentApiException) {
LOGGER.error("AddProject EXCEPTION", contentApiException);
throw contentApiException;
} catch (Exception e) {
LOGGER.error("AddProject EXCEPTION", e);
throw new ContentAPIException(e);
}
return result;
}
/**
* Create a quote from a list of projects
* @param translationOptions The translation options to use for this Quote
* @param projects The projects associate with this Quote (must already have been added using addProject)
* @return The created Quote
* @throws ContentAPIException
*/
public Quote addQuote(final TranslationOptions translationOptions, final List projects) throws ContentAPIException {
return addQuote(translationOptions, projects, null);
}
/**
*
* @param translationOptions The translation options to use for this Quote
* @param projects The projects associate with this Quote (must already have been added using addProject)
* @param notifications A list of email addresses and/or URLs to send Quote Ready notifications to
* @return The created Quote
* @throws ContentAPIException
*/
public Quote addQuote(TranslationOptions translationOptions, List projects, List notifications) throws ContentAPIException {
LOGGER.debug("\nAPI addQuote: Project 1 = {}", projects.get(0).getProjectId());
if (translationOptions == null) {
throw new ContentAPIException("Must specify options to generate a quote");
}
Quote result = null;
try (CloseableHttpClient httpClient = getCloseableClient()) {
HttpPost request = generatePostRequest("/quote/generate");
GenerateQuoteTemplate quoteTemplate = new GenerateQuoteTemplate(translationOptions, projects, notifications);
StringEntity quoteEntity = new StringEntity(quoteTemplate.toXmlString());
request.setEntity(quoteEntity);
CloseableHttpResponse response = httpClient.execute(request);
LOGGER.debug("API addQuote Response={}", response.toString());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
result = new XmlMapper().readValue(
response.getEntity().getContent(),
Quote.class
);
LOGGER.debug(
"API addQuote Result = {id: {}, status: {}}",
result.getQuoteId(),
result.getStatus()
);
} else {
handleErrorResponse(response);
}
} catch (ContentAPIException contentApiException) {
throw contentApiException;
} catch (Exception e) {
throw new ContentAPIException(e);
}
return result;
}
public List listProjects() throws ContentAPIException {
List projects = new ArrayList<>();
try {
final CloseableHttpClient httpClient = this.getCloseableClient();
final HttpGet request = generateGetRequest("/projects");
final CloseableHttpResponse response = httpClient.execute(request);
LOGGER.debug("\n\nAPI listProjects Response={}", response.toString());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
final XmlMapper mapper = new XmlMapper();
projects = mapper.readValue(response.getEntity().getContent(), new TypeReference>() {});
} else {
this.handleErrorResponse(response);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.debug("API listProjects NoSuchAlgorithmException", e);
throw new ContentAPIException(e);
} catch (URISyntaxException e) {
LOGGER.debug("API listProjects URISyntaxException", e);
throw new ContentAPIException(e);
} catch (ClientProtocolException e) {
LOGGER.debug("API listProjects ClientProtocolException", e);
throw new ContentAPIException(e);
} catch (IOException e) {
LOGGER.debug("API listProjects IOException", e);
throw new ContentAPIException(e);
} catch (Exception e) {
LOGGER.debug("API listProjects EXCEPTION", e);
throw new ContentAPIException(e);
}
return projects;
}
public List getQuotesForJob(final String jobId, final String objectTitle) throws ContentAPIException {
if(null == jobId) {
throw new ContentAPIException("Cannot get quote info for null job ID.");
} else if(jobId.trim().isEmpty()) {
throw new ContentAPIException("Cannot get quote info for blank job ID.");
}
List quotes = null;
// parse Job ID for name and creation date
// /var/lionbridge-ondemand-connector/20160425/xmltest3_translationjob
String jobName = "";
String jobCreationDate = "";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String[] jobIdParts = jobId.split("/");
if(0 < jobIdParts.length) {
// job name had better be last
jobName = jobIdParts[jobIdParts.length - 1];
// now find the creation date. It should be just before the name.
if(1 < jobIdParts.length) {
int partNumber = jobIdParts.length - 2;
while(0 <= partNumber) {
String jobPart = jobIdParts[partNumber];
try {
dateFormat.parse(jobPart);
jobCreationDate = jobPart;
break;
} catch (ParseException e1) {
// Hopefully this just means this piece isn't the date
}
partNumber--;
}
}
}
// list quotes, but don't bother if we didn't get a job name and creation date
if(!jobName.isEmpty() && !jobCreationDate.isEmpty()) {
final List allQuotes = this.listQuotes();
// loop through quotes to see if we can find the one matching this job
for(Quote quote : allQuotes) {
if(null != quote.getProjects()) {
for(Project project : quote.getProjects()) {
if(project.getName().toUpperCase().startsWith(jobName.toUpperCase())) {
if(null == quotes) {
quotes = new ArrayList();
}
quotes.add(quote);
}
}
}
}
}
return quotes;
}
public Project getProject(final String projectId) throws ContentAPIException {
LOGGER.debug("\nAPI getProject: projectId={}", projectId);
if (null == projectId) {
throw new ContentAPIException("Cannot get project info for null ID.");
} else if(projectId.trim().isEmpty()) {
throw new ContentAPIException("Cannot get project info for blank ID.");
}
Project projectInfo = null;
try {
CloseableHttpClient httpClient = this.getCloseableClient();
HttpGet request = generateGetRequest(format("/projects/%s", projectId.trim()));
CloseableHttpResponse response = httpClient.execute(request);
LOGGER.debug("API getProject Response={}" + response.toString());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
projectInfo = new XmlMapper().readValue(
response.getEntity().getContent(),
Project.class
);
LOGGER.debug("API getProject Result (MAPPED) = {}", projectInfo.toXmlStringSimple());
} else {
handleErrorResponse(response);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.debug("API getProject NoSuchAlgorithmException", e);
throw new ContentAPIException(e);
} catch (URISyntaxException e) {
LOGGER.debug("API getProject URISyntaxException", e);
throw new ContentAPIException(e);
} catch (ClientProtocolException e) {
LOGGER.debug("API getProject ClientProtocolException", e);
throw new ContentAPIException(e);
} catch (IOException e) {
LOGGER.debug("API getProject IOException", e);
throw new ContentAPIException(e);
} catch (Exception e) {
LOGGER.debug("API getProject EXCEPTION", e);
throw new ContentAPIException(e);
}
return projectInfo;
}
/**
* Returns a list of all of the quotes owned by a user.
* @return A list of Quote object
* @throws ContentAPIException
*/
public List listQuotes() throws ContentAPIException {
List quotes = new ArrayList<>();
try {
final CloseableHttpClient httpClient = this.getCloseableClient();
final HttpGet request = generateGetRequest("/quote");
final CloseableHttpResponse response = httpClient.execute(request);
LOGGER.debug("\n\nAPI listQuotes Response={}", response.toString());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
final XmlMapper mapper = new XmlMapper();
quotes = mapper.readValue(response.getEntity().getContent(), new TypeReference>() {});
} else {
this.handleErrorResponse(response);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.debug("API listQuotes NoSuchAlgorithmException", e);
throw new ContentAPIException(e);
} catch (URISyntaxException e) {
LOGGER.debug("API listQuotes URISyntaxException", e);
throw new ContentAPIException(e);
} catch (ClientProtocolException e) {
LOGGER.debug("API listQuotes ClientProtocolException", e);
throw new ContentAPIException(e);
} catch (IOException e) {
LOGGER.debug("API listQuotes IOException", e);
throw new ContentAPIException(e);
} catch (Exception e) {
LOGGER.debug("API listQuotes EXCEPTION", e);
throw new ContentAPIException(e);
}
return quotes;
}
/**
* Retrieve a single Quote
* @param quoteId The ID of the desired quote
* @return The desired Quote
* @throws ContentAPIException
*/
public Quote getQuote(final String quoteId) throws ContentAPIException {
LOGGER.debug("\nAPI getQuote: quoteId={}", quoteId);
if(null == quoteId) {
throw new ContentAPIException("Cannot get quote info for null ID.");
} else if(quoteId.trim().isEmpty()) {
throw new ContentAPIException("Cannot get quote info for blank ID.");
}
Quote quoteInfo = null;
try {
final CloseableHttpClient httpClient = this.getCloseableClient();
final HttpGet request = generateGetRequest(format("/quote/%s", quoteId.trim()));
final CloseableHttpResponse response = httpClient.execute(request);
LOGGER.debug("API getQuote Response={}", response.toString());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
final XmlMapper mapper = new XmlMapper();
quoteInfo = mapper.readValue(response.getEntity().getContent(), Quote.class);
LOGGER.debug("API getQuote Result = {}", quoteInfo.toXmlStringSimple());
} else {
this.handleErrorResponse(response);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.debug("API getQuote NoSuchAlgorithmException", e);
throw new ContentAPIException(e);
} catch (URISyntaxException e) {
LOGGER.debug("API getQuote URISyntaxException", e);
throw new ContentAPIException(e);
} catch (ClientProtocolException e) {
LOGGER.debug("API getQuote ClientProtocolException", e);
throw new ContentAPIException(e);
} catch (IOException e) {
LOGGER.debug("API getQuote IOException", e);
throw new ContentAPIException(e);
} catch (Exception e) {
LOGGER.debug("API getQuote EXCEPTION", e);
throw new ContentAPIException(e);
}
return quoteInfo;
}
/**
* Authorizes a quote. Only quotes with a status of “Pending” can be authorized.
* @param quoteId The ID of the desired quote
* @param poNumber A PurchaseOrderNumber that matches a purchase order we have on file.
* @return A QuoteAuthorization
* @throws ContentAPIException
*/
public QuoteAuthorization authorizeQuote(final String quoteId, final String poNumber) throws ContentAPIException {
LOGGER.debug("\nAPI authorizeQuote: quoteId={}, poNumber={}", quoteId, poNumber);
if(null == quoteId) {
throw new ContentAPIException("Cannot authorize quote for null ID.");
} else if(quoteId.trim().isEmpty()) {
throw new ContentAPIException("Cannot authorize quote for blank ID.");
} else if(null == poNumber) {
throw new ContentAPIException("Cannot authorize quote for null PO number.");
} else if(poNumber.trim().isEmpty()) {
throw new ContentAPIException("Cannot authorize quote for blank PO number.");
}
QuoteAuthorization quoteAuth = null;
try {
final CloseableHttpClient httpClient = this.getCloseableClient();
final HttpPost request = generatePostRequest(format("/quote/%s/authorize", quoteId.trim()));
// get and update the quote info
Quote quoteInfo = this.getQuote(quoteId);
quoteInfo.setPurchaseOrderNumber(poNumber);
StringEntity entity = new StringEntity(quoteInfo.toXmlForAuthorize());
request.setEntity(entity);
LOGGER.debug("\nAPI authorizeQuote: request body={}", quoteInfo.toXmlForAuthorize());
final CloseableHttpResponse response = httpClient.execute(request);
LOGGER.debug("API authorizeQuote Response={}", response.toString());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED) {
final XmlMapper mapper = new XmlMapper();
quoteAuth = mapper.readValue(response.getEntity().getContent(), QuoteAuthorization.class);
LOGGER.debug("API authorizeQuote Result = {}", quoteAuth.toXmlStringSimple());
} else {
throw new ContentAPIException(response.getStatusLine().toString());
//this.handleErrorResponse(response);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.debug("API authorizeQuote NoSuchAlgorithmException", e);
throw new ContentAPIException(e);
} catch (URISyntaxException e) {
LOGGER.debug("API authorizeQuote URISyntaxException", e);
throw new ContentAPIException(e);
} catch (ClientProtocolException e) {
LOGGER.debug("API authorizeQuote ClientProtocolException", e);
throw new ContentAPIException(e);
} catch (IOException e) {
LOGGER.debug("API authorizeQuote IOException", e);
throw new ContentAPIException(e);
} catch (Exception e) {
LOGGER.debug("API authorizeQuote EXCEPTION", e);
throw new ContentAPIException(e);
}
return quoteAuth;
}
/**
* Returns details about a file
* @param assetId The ID of the file you want details on
* @return The requested file information
* @throws ContentAPIException
*/
public LBFile getFileDetails(String assetId) throws ContentAPIException {
LBFile lbFile = new LBFile();
try (final CloseableHttpClient httpClient = getCloseableClient()) {
HttpGet request = generateGetRequest(format("/files/%s/details", assetId));
CloseableHttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
lbFile = new XmlMapper().readValue(
response.getEntity().getContent(),
LBFile.class
);
} else {
handleErrorResponse(response);
}
} catch(Exception e) {
throw new ContentAPIException(e);
}
return lbFile;
}
/**
* TODO This method was built on the assumption that a zip file was sent to
* the Lionbridge API. That is no longer the case. However, this method is
* not currently being used by the connector. If it ever is used, the method
* will need to be rebuilt to be more like getTranslatedFile.
* @param assetId
* @return
* @throws ContentAPIException
*/
public String getFile(final String assetId) throws ContentAPIException {
LOGGER.debug("\nAPI getFile: assetId={}", assetId);
if(null == assetId) {
throw new ContentAPIException("Cannot get file info for null ID.");
} else if(assetId.trim().isEmpty()) {
throw new ContentAPIException("Cannot get file info for blank ID.");
}
File outputFile = null;
try {
final CloseableHttpClient httpClient = this.getCloseableClient();
final HttpGet request = generateGetRequest(format("/files/%s", assetId.trim()));
final CloseableHttpResponse response = httpClient.execute(request);
LOGGER.debug("API getFile Response={}", response.toString());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String fileName = "";
BufferedHeader fileNameHeader = (BufferedHeader) response.getFirstHeader("Content-Disposition");
if(null != fileNameHeader) {
// Content-Disposition header should look like:
// Content-Disposition: attachment; filename=add_file_only_20160405121236_20160405121236_564669.zip
final String[] dispositionPieces = fileNameHeader.getValue().split("=");
fileName = dispositionPieces[dispositionPieces.length - 1];
LOGGER.debug("Response file name (from firstHeader): {}", fileName);
}
if(fileName.isEmpty()) {
fileName = "liondemand_" + assetId;
LOGGER.debug("Failed to decipher file name. Using default name {}.", fileName);
}
File tempFolder = new File("/tmp/liondemand/getFile/" + assetId + "/");
if(!tempFolder.isDirectory()) {
tempFolder.mkdirs();
}
outputFile = new File(tempFolder, fileName);
outputFile.createNewFile();
// build the file for return
FileOutputStream fileStream = new FileOutputStream(outputFile);
final int bytesCopied = IOUtils.copy(response.getEntity().getContent(), fileStream);
LOGGER.debug("Copied {} bytes to output stream.", bytesCopied);
fileStream.close();
} else {
this.handleErrorResponse(response);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.debug("API getFile NoSuchAlgorithmException", e);
throw new ContentAPIException(e);
} catch (URISyntaxException e) {
LOGGER.debug("API getFile URISyntaxException", e);
throw new ContentAPIException(e);
} catch (ClientProtocolException e) {
LOGGER.debug("API getFile ClientProtocolException", e);
throw new ContentAPIException(e);
} catch (IOException e) {
LOGGER.debug("API getFile IOException", e);
throw new ContentAPIException(e);
} catch (Exception e) {
LOGGER.debug("API getFile EXCEPTION", e);
throw new ContentAPIException(e);
}
if(null != outputFile) {
final String extractPath = ZipUtils.extractFilesFromZip(outputFile, "extract");
return extractPath;
}
return null;
}
public File getTranslatedFile(final String assetId, final String languageCode) throws ContentAPIException {
LOGGER.debug("\nAPI getTranslatedFile: assetId={}, language={}", assetId, languageCode);
if(null == assetId) {
throw new ContentAPIException("Cannot get translated file info for null ID.");
} else if(assetId.trim().isEmpty()) {
throw new ContentAPIException("Cannot get translated file info for blank ID.");
}
File translatedAsset = null;
try {
final CloseableHttpClient httpClient = this.getCloseableClient();
final HttpGet request = generateGetRequest(format("/files/%s/%s", assetId.trim(), languageCode));
CloseableHttpResponse response = null;
while (true) {
response = httpClient.execute(request);
LOGGER.debug("API getTranslatedFile Response={}", response.toString());
int statusCode = response.getStatusLine().getStatusCode();
LOGGER.trace("API getTranslatedFile Response phrase={}", response.getStatusLine().getReasonPhrase());
if (statusCode == TOO_MANY_REQUESTS_STATUS_CODE) {
// wait one minute and try it again
LOGGER.info("API getTranslatedFile: Got too many requests.");
Thread.sleep(TOO_MANY_REQUESTS_TIMEOUT_MS);
} else {
break;
}
}
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String fileName = "";
BufferedHeader fileNameHeader = (BufferedHeader) response.getFirstHeader("Content-Disposition");
if(null != fileNameHeader) {
// Content-Disposition header should look like:
// Content-Disposition: attachment; filename=add_file_only_20160405121236_20160405121236_564669.zip
final String[] dispositionPieces = fileNameHeader.getValue().split("=");
fileName = dispositionPieces[dispositionPieces.length - 1];
LOGGER.debug("Response file name (from firstHeader): {}", fileName);
}
if(fileName.isEmpty()) {
fileName = "liondemand_" + assetId;
LOGGER.debug("Failed to decipher file name. Using default name {}.", fileName);
}
File tempFolder = new File("/tmp/liondemand/getTranslatedFile/" + assetId + "/" + languageCode + "/");
if(!tempFolder.isDirectory()) {
tempFolder.mkdirs();
}
translatedAsset = new File(tempFolder, fileName);
final boolean fileCreated = translatedAsset.createNewFile();
LOGGER.debug("File {} created? {}", translatedAsset.getPath(), fileCreated);
// build the file for translation
FileOutputStream fileStream = new FileOutputStream(translatedAsset);
final int bytesCopied = IOUtils.copy(response.getEntity().getContent(), fileStream);
LOGGER.debug("Copied {} bytes to output stream.", bytesCopied);
fileStream.close();
return translatedAsset;
} else {
this.handleErrorResponse(response);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.debug("API getTranslatedFile NoSuchAlgorithmException", e);
throw new ContentAPIException(e);
} catch (URISyntaxException e) {
LOGGER.debug("API getTranslatedFile URISyntaxException", e);
throw new ContentAPIException(e);
} catch (ClientProtocolException e) {
LOGGER.debug("API getTranslatedFile ClientProtocolException", e);
throw new ContentAPIException(e);
} catch (IOException e) {
LOGGER.debug("API getTranslatedFile IOException", e);
throw new ContentAPIException(e);
} catch (Exception e) {
LOGGER.debug("API getTranslatedFile EXCEPTION", e);
throw new ContentAPIException(e);
}
return null;
}
public RejectTranslationOutput rejectTranslatedFile(final String assetId, final String languageCode) throws ContentAPIException {
LOGGER.debug("\n\nAPI rejectTranslatedFile: assetId={}, language={}", assetId, languageCode);
if(null == assetId) {
throw new ContentAPIException("Cannot reject translated file for null ID.");
} else if(assetId.trim().isEmpty()) {
throw new ContentAPIException("Cannot reject translated file for blank ID.");
}
RejectTranslationOutput rejectOutput = null;
try {
final CloseableHttpClient httpClient = this.getCloseableClient();
final HttpPost request = generatePostRequest(format("/files/%s/%s/reject", assetId.trim(), languageCode));
final RejectFile rejectBody = new RejectFile(5000, "Not good enough");
StringEntity entity = new StringEntity(rejectBody.toXmlString());
request.setEntity(entity);
LOGGER.debug("\nAPI rejectTranslatedFile: reject body={}", rejectBody.toXmlString());
final CloseableHttpResponse response = httpClient.execute(request);
LOGGER.debug("API rejectTranslatedFile Response={}", response.toString());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED) {
final XmlMapper mapper = new XmlMapper();
rejectOutput = mapper.readValue(response.getEntity().getContent(), RejectTranslationOutput.class);
LOGGER.debug("API rejectTranslatedFile Result = {}", rejectOutput.toXmlStringSimple());
} else {
this.handleErrorResponse(response);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.debug("API rejectTranslatedFile NoSuchAlgorithmException", e);
throw new ContentAPIException(e);
} catch (URISyntaxException e) {
LOGGER.debug("API rejectTranslatedFile URISyntaxException", e);
throw new ContentAPIException(e);
} catch (ClientProtocolException e) {
LOGGER.debug("API rejectTranslatedFile ClientProtocolException", e);
throw new ContentAPIException(e);
} catch (IOException e) {
LOGGER.debug("API rejectTranslatedFile IOException", e);
throw new ContentAPIException(e);
} catch (Exception e) {
LOGGER.debug("API rejectTranslatedFile EXCEPTION", e);
throw new ContentAPIException(e);
}
return rejectOutput;
}
private HttpGet generateGetRequest(final String path) throws URISyntaxException, NoSuchAlgorithmException {
final URI uri = this.getURI(path);
final HttpGet request = new HttpGet(uri);
request.setHeader(HttpHeaders.CONTENT_TYPE, "text/xml");
request.setHeader(HttpHeaders.ACCEPT, "text/xml");
setAuthHeaders(request, uri, "GET");
return request;
}
private HttpPost generatePostRequest(final String path) throws URISyntaxException, NoSuchAlgorithmException {
final URI uri = getURI(path);
final HttpPost request = new HttpPost(uri);
request.setHeader(HttpHeaders.CONTENT_TYPE, "text/xml");
request.setHeader(HttpHeaders.ACCEPT, "text/xml");
setAuthHeaders(request, uri, "POST");
return request;
}
private void handleErrorResponse(final CloseableHttpResponse response) throws Exception {
ErrorManager errorManager = null;
final HttpEntity responseEntity = response.getEntity();
try {
errorManager = new XmlMapper().readValue(
responseEntity.getContent(),
ErrorManager.class
);
errorManager.setErrorCode(
valueOf(response.getStatusLine().getStatusCode())
);
} catch(UnrecognizedPropertyException e) {
// Gets thrown in case of errors such as 404, when HTML response is given.
}
if (errorManager == null) {
StringBuilder errorBuilder = new StringBuilder();
errorBuilder.append(valueOf(response.getStatusLine().getStatusCode()))
.append(": ")
.append(response.getStatusLine().getReasonPhrase());
throw new ContentAPIException(errorBuilder.toString());
} else {
throw errorManager.generateException();
}
}
private void setAuthHeaders(HttpRequest request, final URI uri, final String method) throws NoSuchAlgorithmException {
final String timestamp = getTimestamp();
final String authorizationHeader = getAuthHeader(method, uri.getPath(), timestamp, VERSION);
request.addHeader("x-lod-timestamp", timestamp);
request.addHeader("x-lod-version", VERSION);
request.setHeader(HttpHeaders.AUTHORIZATION, authorizationHeader);
}
private String getTimestamp() {
final StringBuilder timestamp = new StringBuilder();
final Date now = new Date();
final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
timestamp.append(dateFormat.format(now));
timestamp.append("0000");
return timestamp.toString();
}
private String getAuthHeader(final String method, final String resource, final String timestamp, final String version) throws NoSuchAlgorithmException {
String signature = this.getSignature(method, resource, timestamp, version);
return getHeaderFromSignature(signature);
}
private String getHeaderFromSignature(final String signature) {
return "LOD1-BASE64-SHA256 " +
"KeyID=" +
this.keyId +
",Signature=" +
signature +
",SignedHeaders=x-lod-timestamp;x-lod-version;accept";
}
private String getSignature(final String method, final String resource, final String timestamp, final String version) throws NoSuchAlgorithmException {
String toEncode = method + ":" + resource + ":" + secretKey + ":" + timestamp + ":" + version + ":text/xml";
return encodeSignature(toEncode);
}
private String encodeSignature(final String input) throws NoSuchAlgorithmException {
final MessageDigest mDigest = MessageDigest.getInstance("SHA-256");
byte[] result = mDigest.digest(input.getBytes(StandardCharsets.US_ASCII));
return Base64.encodeBase64String(result);
}
private CloseableHttpClient getCloseableClient() {
return HttpClients.custom().build();
}
private URI getURI(final String path) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder();
uriBuilder.setScheme(scheme);
uriBuilder.setHost(host);
uriBuilder.setPath(rootPath + path);
if (-1 != port) {
uriBuilder.setPort(port);
}
return uriBuilder.build();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy