![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.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
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.Locale;
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.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.InputStream;
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.*;
import static java.lang.String.format;
import static java.lang.String.valueOf;
/**
* 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 final XmlMapper xmlMapper = getConfiguredXmlMapper();
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);
}
@SuppressWarnings("deprecation")
public ContentAPI(final String keyId, final String secretKey, final String endpoint, final String defaultCurrency) throws ContentAPIException {
this.keyId = keyId;
this.secretKey = secretKey;
try {
setUriDefaults(endpoint);
} catch (Exception e) {
throw new ContentAPIException(e);
}
setDefaultCurrency(defaultCurrency);
}
/**
* @deprecated This method will become private in the next release
*/
@Deprecated
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;
}
/**
* @deprecated This method will become private in the next release
*/
@Deprecated
public void setDefaultCurrency(final String defaultCurrency) {
this.defaultCurrency = defaultCurrency;
}
/**
* Determine if the endpoint and credentials you provided are valid
*
* @return A boolean representing validity
* @throws ContentAPIException An exception containing any API errors
*/
public boolean isValid() throws ContentAPIException {
try {
CloseableHttpClient httpClient = getCloseableClient();
HttpGet request = generateGetRequest("/account/info");
CloseableHttpResponse response = httpClient.execute(request);
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
} catch (Exception e) {
throw new ContentAPIException(e);
}
}
/**
* @deprecated You should use getLocales instead
*/
@Deprecated
public LocaleList listLocales() throws ContentAPIException {
return getApiObject("/locales", LocaleList.class);
}
/**
* Returns a list of Locales supported for translation
*
* @return List of Locales
* @throws ContentAPIException An exception containing any API errors
*/
public List getLocales() throws ContentAPIException {
return getApiObjectList("/locales", Locale.class);
}
/**
* @deprecated You should use getServices instead
*/
@Deprecated
public ServiceList listServices() throws ContentAPIException {
return getApiObject("/services", ServiceList.class);
}
/**
* Returns a list of Services provided by Lionbridge
*
* @return List of Services
* @throws ContentAPIException An exception containing any API errors
*/
public List getServices() throws ContentAPIException {
return getApiObjectList("/services", Service.class);
}
/**
* Get information about a specific service
*
* @param serviceId The ID of the service you want
* @return The requested Service
* @throws ContentAPIException An exception containing any API errors
*/
public Service getService(int serviceId) throws ContentAPIException {
return getApiObject(format("/services/%d", serviceId), Service.class);
}
/**
* Use this method to retrieve all your projects
*
* @return A List of Projects
* @throws ContentAPIException An exception containing any API errors
*/
public List listProjects() throws ContentAPIException {
return getApiObjectList("/projects", Project.class);
}
/**
* Returns a list of all of the quotes owned by a user.
*
* @return A list of Quote objects
* @throws ContentAPIException An exception containing any API errors
*/
public List listQuotes() throws ContentAPIException {
return getApiObjectList("/quote", Quote.class);
}
/**
* Retrieve a Project by ID
*
* @param projectId The ID of the Project you want
* @return The requested Project
* @throws ContentAPIException An exception containing any API errors
*/
public Project getProject(final String projectId) throws ContentAPIException {
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.");
}
return getApiObject(format("/projects/%s", projectId.trim()), Project.class);
}
/**
* Retrieve a single Quote by ID
*
* @param quoteId The ID of the desired quote
* @return The desired Quote
* @throws ContentAPIException An exception containing any API errors
*/
public Quote getQuote(final String quoteId) throws ContentAPIException {
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.");
}
return getApiObject(format("/quote/%s", quoteId.trim()), Quote.class);
}
/**
* Returns details about a file
*
* @param assetId The ID of the file you want details on
* @return The requested file information
* @throws ContentAPIException An exception containing any API errors
*/
public LBFile getFileDetails(String assetId) throws ContentAPIException {
if (null == assetId) {
throw new ContentAPIException("Cannot get file details for null asset ID.");
} else if (assetId.trim().isEmpty()) {
throw new ContentAPIException("Cannot get file details for blank asset ID.");
}
return getApiObject(format("/files/%s/details", assetId), LBFile.class);
}
/**
* @deprecated You should use getQuotesForJob(String jobId) instead
*/
@Deprecated
@SuppressWarnings("unused")
public List getQuotesForJob(String jobId, String objectTitle) throws ContentAPIException {
return getQuotesForJob(jobId);
}
/**
* Retrieve a list of Quotes associated with a Job
*
* @param jobId The Job ID you want Quotes for
* @return A List of Quotes
* @throws ContentAPIException An exception containing any API errors
*/
public List getQuotesForJob(String jobId) 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;
}
/**
* Downloads the desired file and stores it at the returned path
*
* @param assetId The ID of the desired file
* @return A string representing the path of the downloaded files
* @throws ContentAPIException An exception containing any API errors
*/
public String getFile(final String assetId) throws ContentAPIException {
/*
* 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.
*/
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 {
CloseableHttpClient httpClient = this.getCloseableClient();
HttpGet request = generateGetRequest(format("/files/%s", assetId.trim()));
CloseableHttpResponse response = httpClient.execute(request);
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
String[] dispositionPieces = fileNameHeader.getValue().split("=");
fileName = dispositionPieces[dispositionPieces.length - 1];
}
if (fileName.isEmpty()) {
fileName = "liondemand_" + assetId;
}
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);
IOUtils.copy(response.getEntity().getContent(), fileStream);
fileStream.close();
} else {
handleErrorResponse(response);
}
} catch (Exception e) {
throw new ContentAPIException(e);
}
if (null != outputFile) {
return ZipUtils.extractFilesFromZip(outputFile, "extract");
}
return null;
}
/**
* Get the translated version of a file
*
* @param assetId The ID of the source file
* @param languageCode The language code of the desired translation
* @return The translated file as a File object
* @throws ContentAPIException An exception containing any API errors
*/
public File getTranslatedFile(final String assetId, final String languageCode) throws ContentAPIException {
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;
try {
CloseableHttpClient httpClient = this.getCloseableClient();
HttpGet request = generateGetRequest(format("/files/%s/%s", assetId.trim(), languageCode));
CloseableHttpResponse response = null;
while (true) {
response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
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
String[] dispositionPieces = fileNameHeader.getValue().split("=");
fileName = dispositionPieces[dispositionPieces.length - 1];
}
if (fileName.isEmpty()) {
fileName = "liondemand_" + assetId;
}
File tempFolder = new File("/tmp/liondemand/getTranslatedFile/" + assetId + "/" + languageCode + "/");
if (!tempFolder.isDirectory()) {
tempFolder.mkdirs();
}
translatedAsset = new File(tempFolder, fileName);
translatedAsset.createNewFile();
// build the file for translation
FileOutputStream fileStream = new FileOutputStream(translatedAsset);
IOUtils.copy(response.getEntity().getContent(), fileStream);
fileStream.close();
return translatedAsset;
} else {
handleErrorResponse(response);
}
} catch (Exception e) {
throw new ContentAPIException(e);
}
return null;
}
/**
* 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 An exception containing any API errors
*/
public LBFile addFile(final String mimeType, final File inputFile, final String languageCode) throws ContentAPIException {
LBFile file = null;
try {
CloseableHttpClient httpClient = getCloseableClient();
StringBuilder pathBuilder = new StringBuilder();
// TODO the replace() is a workaround for rejected images with a space in the file name
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);
}
// build request and header
final HttpPost request = generatePostRequest(pathBuilder.toString());
request.setHeader(HttpHeaders.CONTENT_TYPE, mimeType);
// attach the file in the request body
FileEntity inputEntity = new FileEntity(inputFile);
request.setEntity(inputEntity);
boolean sendFile = true;
while (sendFile) {
// send the request and handle the response
final CloseableHttpResponse response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_CREATED) {
LBFile fileInfo = xmlMapper.readValue(response.getEntity().getContent(), LBFile.class);
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) {
throw e;
} catch (Exception e) {
throw new ContentAPIException(e);
}
return file;
}
/**
* Add a file by URL for translation
*
* @param url The URL of the file you want to add.
* @param languageCode the source language (the current language)
* @return file information returned from the API
* @throws ContentAPIException An exception containing any API errors
*/
public LBFile addFileByReference(URI url, String languageCode) throws ContentAPIException {
LBFile file = null;
try {
CloseableHttpClient httpClient = getCloseableClient();
StringBuilder pathBuilder = new StringBuilder();
String[] pathParts =url.getPath().split("/");
String encodedFileName = pathParts[pathParts.length-1];
if (languageCode == null) {
pathBuilder.append("/files/add_by_reference/detect-language/").append(encodedFileName);
} else {
pathBuilder.append("/files/add_by_reference/").append(languageCode).append("/").append(encodedFileName);
}
StringEntity stringEntity = new StringEntity("" + url.toString() + " ");
HttpPost request = generatePostRequest(pathBuilder.toString());
request.setHeader(HttpHeaders.CONTENT_TYPE, "text/xml");
request.setHeader(HttpHeaders.ACCEPT, "text/xml");
request.setEntity(stringEntity);
boolean sendFile = true;
while (sendFile) {
CloseableHttpResponse response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_ACCEPTED) {
LBFile fileInfo = xmlMapper.readValue(response.getEntity().getContent(), LBFile.class);
file = fileInfo;
sendFile = false;
} else if (statusCode == TOO_MANY_REQUESTS_STATUS_CODE) {
LOGGER.info("API addFile: Got too many requests.");
Thread.sleep(TOO_MANY_REQUESTS_TIMEOUT_MS);
} else {
handleErrorResponse(response);
sendFile = false;
}
}
} catch (ContentAPIException e) {
throw e;
} catch (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 An exception containing any API errors
*/
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 translationOptions 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)
* @param notifications A list of notification endpoints
* @return project information returned from the API
* @throws ContentAPIException An exception containing any API errors
*/
public Project addProject(
final String projectName,
final TranslationOptions translationOptions,
final List fileAssetIds,
final List referenceFileAssetIds,
List notifications
) throws ContentAPIException {
AddProjectTemplate addProjectTemplate = new AddProjectTemplate(projectName, translationOptions, fileAssetIds, referenceFileAssetIds, notifications);
return postApiObject(
"/projects/add",
addProjectTemplate.toXmlString(),
Project.class
);
}
/**
* 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 An exception containing any API errors
*/
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 An exception containing any API errors
*/
public Quote addQuote(TranslationOptions translationOptions, List projects, List notifications) throws ContentAPIException {
if (translationOptions == null) {
throw new ContentAPIException("Must specify options to generate a quote");
}
GenerateQuoteTemplate quoteTemplate = new GenerateQuoteTemplate(translationOptions, projects, notifications);
return postApiObject(
"/quote/generate",
quoteTemplate.toXmlString(),
Quote.class
);
}
/**
* 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.
* @param notificationList A list of endpoints to notify when the quote is paid.
* @return A QuoteAuthorization
* @throws ContentAPIException An exception containing any API errors
*/
public QuoteAuthorization authorizeQuote(String quoteId, String poNumber, List notificationList) throws ContentAPIException {
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.");
}
Quote quoteInfo = getQuote(quoteId);
quoteInfo.setPurchaseOrderNumber(poNumber);
List notificationSubscriptions = new ArrayList<>();
if (notificationList != null) {
for (String endpoint : notificationList) {
NotificationSubscription notificationSubscription = new NotificationSubscription();
notificationSubscription.setEndpoint(endpoint);
notificationSubscription.setEventName("quote-paid");
notificationSubscriptions.add(notificationSubscription);
}
quoteInfo.setNotificationSubscriptions(notificationSubscriptions);
}
return postApiObject(
format("/quote/%s/authorize", quoteId.trim()),
quoteInfo.toXmlForAuthorize(),
QuoteAuthorization.class
);
}
/**
* 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 An exception containing any API errors
*/
public QuoteAuthorization authorizeQuote(final String quoteId, final String poNumber) throws ContentAPIException {
return authorizeQuote(quoteId, poNumber, null);
}
/**
* Reject a Quote
* @param quoteId The ID of the quote to reject
* @return A RejectQuote representing the status
*/
public RejectQuote rejectQuote(String quoteId) throws ContentAPIException {
try {
CloseableHttpClient httpClient = getCloseableClient();
HttpPost request = generatePostRequest(format("/quote/%s/reject", quoteId));
CloseableHttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
InputStream content = response.getEntity().getContent();
return xmlMapper.readValue(content, RejectQuote.class);
} else {
handleErrorResponse(response);
}
} catch (ContentAPIException contentApiException) {
throw contentApiException;
} catch (Exception e) {
throw new ContentAPIException(e);
}
return null;
}
/**
* Reject the translation of a file for the given language code
*
* @param assetId The ID of the source file
* @param languageCode The language code for the translation you want to reject
* @return A string representing the result of the operation
* @throws ContentAPIException An exception containing any API errors
*/
public RejectTranslationOutput rejectTranslatedFile(final String assetId, final String languageCode) throws ContentAPIException {
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.");
}
RejectFile rejectBody = new RejectFile(5000, "Not good enough");
return postApiObject(
format("/files/%s/%s/reject", assetId.trim(), languageCode),
rejectBody.toXmlString(),
RejectTranslationOutput.class
);
}
private HttpGet generateGetRequest(final String path) throws URISyntaxException, NoSuchAlgorithmException {
URI uri = getURI(path);
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 {
URI uri = getURI(path);
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;
HttpEntity responseEntity = response.getEntity();
try {
errorManager = 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) {
String errorBuilder = format(
"%s: %s",
valueOf(response.getStatusLine().getStatusCode()),
response.getStatusLine().getReasonPhrase()
);
ContentAPIException contentAPIException = new ContentAPIException();
contentAPIException.setErrors(errorManager.getErrors());
throw contentAPIException;
} else {
throw errorManager.generateException();
}
}
private void setAuthHeaders(HttpRequest request, final URI uri, final String method) throws NoSuchAlgorithmException {
String timestamp = getTimestamp();
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() {
StringBuilder timestamp = new StringBuilder();
Date now = new Date();
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 = 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 {
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();
}
private XmlMapper getConfiguredXmlMapper() {
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return xmlMapper;
}
private T getApiObject(String path, Class valueType) throws ContentAPIException {
try {
CloseableHttpClient httpClient = getCloseableClient();
HttpGet request = generateGetRequest(path);
CloseableHttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
return xmlMapper.readValue(response.getEntity().getContent(), valueType);
} else {
handleErrorResponse(response);
}
} catch (ContentAPIException contentApiException) {
throw contentApiException;
} catch (Exception e) {
throw new ContentAPIException(e);
}
return null;
}
private T postApiObject(String path, String entity, Class valueType) throws ContentAPIException {
try {
StringEntity stringEntity = new StringEntity(entity);
CloseableHttpClient httpClient = getCloseableClient();
HttpPost request = generatePostRequest(path);
request.setEntity(stringEntity);
CloseableHttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED ||
response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
return xmlMapper.readValue(response.getEntity().getContent(), valueType);
} else {
handleErrorResponse(response);
}
} catch (ContentAPIException contentApiException) {
throw contentApiException;
} catch (Exception e) {
throw new ContentAPIException(e);
}
return null;
}
private List getApiObjectList(String path, Class klazz) throws ContentAPIException {
List objectList = new ArrayList<>();
try {
CloseableHttpClient httpClient = this.getCloseableClient();
HttpGet request = generateGetRequest(path);
CloseableHttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
JavaType type = xmlMapper.getTypeFactory().constructCollectionType(List.class, klazz);
objectList = xmlMapper.readValue(response.getEntity().getContent(), type);
} else {
handleErrorResponse(response);
}
} catch (ContentAPIException contentApiException) {
throw contentApiException;
} catch (Exception e) {
throw new ContentAPIException(e);
}
return objectList;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy