/*
* Copyright 2014 JBoss Inc
*
* 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 io.apiman.manager.api.gateway.rest;
import io.apiman.gateway.api.rest.contract.exceptions.GatewayApiErrorBean;
import io.apiman.gateway.engine.beans.Api;
import io.apiman.gateway.engine.beans.ApiEndpoint;
import io.apiman.gateway.engine.beans.Client;
import io.apiman.gateway.engine.beans.SystemStatus;
import io.apiman.gateway.engine.beans.exceptions.PublishingException;
import io.apiman.gateway.engine.beans.exceptions.RegistrationException;
import io.apiman.manager.api.gateway.GatewayAuthenticationException;
import io.apiman.manager.api.gateway.i18n.Messages;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* A REST client for accessing the Gateway API.
*
* @author eric.wittmann@redhat.com
*/
@SuppressWarnings("javadoc") // class is temporarily delinked from its interfaces
public class GatewayClient /*implements ISystemResource, IApiResource, IClientResource*/ {
private static final String SYSTEM_STATUS = "/system/status"; //$NON-NLS-1$
private static final String APIs = "/apis"; //$NON-NLS-1$
private static final String CLIENTS = "/clients"; //$NON-NLS-1$
private static final ObjectMapper mapper = new ObjectMapper();
private String endpoint;
private CloseableHttpClient httpClient;
/**
* Constructor.
* @param endpoint the endpoint
* @param httpClient the http client
*/
public GatewayClient(String endpoint, CloseableHttpClient httpClient) {
this.endpoint = endpoint;
this.httpClient = httpClient;
if (this.endpoint.endsWith("/")) { //$NON-NLS-1$
this.endpoint = this.endpoint.substring(0, this.endpoint.length() - 1);
}
}
/**
* @see io.apiman.gateway.api.rest.contract.ISystemResource#getStatus()
*/
public SystemStatus getStatus() throws GatewayAuthenticationException {
InputStream is = null;
try {
URI uri = new URI(this.endpoint + SYSTEM_STATUS);
HttpGet get = new HttpGet(uri);
HttpResponse response = httpClient.execute(get);
int actualStatusCode = response.getStatusLine().getStatusCode();
if (actualStatusCode == 401 || actualStatusCode == 403) {
throw new GatewayAuthenticationException();
} else if (actualStatusCode != 200) {
throw new RuntimeException("System status check failed: " + actualStatusCode); //$NON-NLS-1$
}
is = response.getEntity().getContent();
return mapper.reader(SystemStatus.class).readValue(is);
} catch (GatewayAuthenticationException | RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
IOUtils.closeQuietly(is);
}
}
/**
* @see io.apiman.gateway.api.rest.contract.IApiResource#getApiEndpoint(java.lang.String, java.lang.String, java.lang.String)
*/
public ApiEndpoint getApiEndpoint(String organizationId, String apiId, String version)
throws GatewayAuthenticationException {
InputStream is = null;
try {
@SuppressWarnings("nls")
URI uri = new URI(this.endpoint + APIs + "/" + organizationId + "/" + apiId + "/" + version + "/endpoint");
HttpGet get = new HttpGet(uri);
HttpResponse response = httpClient.execute(get);
int actualStatusCode = response.getStatusLine().getStatusCode();
if (actualStatusCode == 401 || actualStatusCode == 403) {
throw new GatewayAuthenticationException();
}
if (actualStatusCode != 200) {
throw new RuntimeException("Failed to get the API endpoint: " + actualStatusCode); //$NON-NLS-1$
}
is = response.getEntity().getContent();
return mapper.reader(ApiEndpoint.class).readValue(is);
} catch (GatewayAuthenticationException | RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
IOUtils.closeQuietly(is);
}
}
/**
* @see io.apiman.gateway.api.rest.contract.IClientResource#register(io.apiman.gateway.engine.beans.Client)
*/
public void register(Client client) throws RegistrationException, GatewayAuthenticationException {
try {
URI uri = new URI(this.endpoint + CLIENTS);
HttpPut put = new HttpPut(uri);
put.setHeader("Content-Type", "application/json; charset=utf-8"); //$NON-NLS-1$ //$NON-NLS-2$
String jsonPayload = mapper.writer().writeValueAsString(client);
HttpEntity entity = new StringEntity(jsonPayload);
put.setEntity(entity);
HttpResponse response = httpClient.execute(put);
int actualStatusCode = response.getStatusLine().getStatusCode();
if (actualStatusCode == 401 || actualStatusCode == 403) {
throw new GatewayAuthenticationException();
}
if (actualStatusCode == 500) {
Header[] headers = response.getHeaders("X-API-Gateway-Error"); //$NON-NLS-1$
if (headers != null && headers.length > 0) {
throw readRegistrationException(response);
}
}
if (actualStatusCode >= 300) {
throw new RuntimeException(Messages.i18n.format("GatewayClient.ClientRegistrationFailed", actualStatusCode)); //$NON-NLS-1$
}
} catch (GatewayAuthenticationException | RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @see io.apiman.gateway.api.rest.contract.IClientResource#unregister(java.lang.String, java.lang.String, java.lang.String)
*/
public void unregister(String organizationId, String clientId, String version)
throws RegistrationException, GatewayAuthenticationException {
try {
@SuppressWarnings("nls")
URI uri = new URI(this.endpoint + CLIENTS + "/" + organizationId + "/" + clientId + "/" + version);
HttpDelete put = new HttpDelete(uri);
HttpResponse response = httpClient.execute(put);
int actualStatusCode = response.getStatusLine().getStatusCode();
if (actualStatusCode == 401 || actualStatusCode == 403) {
throw new GatewayAuthenticationException();
}
if (actualStatusCode == 500) {
Header[] headers = response.getHeaders("X-API-Gateway-Error"); //$NON-NLS-1$
if (headers != null && headers.length > 0) {
RegistrationException re = readRegistrationException(response);
throw re;
}
}
if (actualStatusCode >= 300) {
throw new Exception(Messages.i18n.format("GatewayClient.ClientUnregistrationFailed", actualStatusCode)); //$NON-NLS-1$
}
} catch (RegistrationException|GatewayAuthenticationException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @see io.apiman.gateway.api.rest.contract.IApiResource#publish(io.apiman.gateway.engine.beans.Api)
*/
public void publish(Api api) throws PublishingException, GatewayAuthenticationException {
try {
URI uri = new URI(this.endpoint + APIs);
HttpPut put = new HttpPut(uri);
put.setHeader("Content-Type", "application/json; charset=utf-8"); //$NON-NLS-1$ //$NON-NLS-2$
String jsonPayload = mapper.writer().writeValueAsString(api);
HttpEntity entity = new StringEntity(jsonPayload);
put.setEntity(entity);
HttpResponse response = httpClient.execute(put);
int actualStatusCode = response.getStatusLine().getStatusCode();
if (actualStatusCode == 401 || actualStatusCode == 403) {
throw new GatewayAuthenticationException();
}
if (actualStatusCode == 500) {
Header[] headers = response.getHeaders("X-API-Gateway-Error"); //$NON-NLS-1$
if (headers != null && headers.length > 0) {
PublishingException pe = readPublishingException(response);
throw pe;
}
}
if (actualStatusCode >= 300) {
throw new Exception(Messages.i18n.format("GatewayClient.ApiPublishingFailed", actualStatusCode)); //$NON-NLS-1$
}
} catch (PublishingException|GatewayAuthenticationException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @see io.apiman.gateway.api.rest.contract.IApiResource#retire(java.lang.String, java.lang.String, java.lang.String)
*/
public void retire(String organizationId, String apiId, String version) throws RegistrationException, GatewayAuthenticationException {
try {
@SuppressWarnings("nls")
URI uri = new URI(this.endpoint + APIs + "/" + organizationId + "/" + apiId + "/" + version);
HttpDelete put = new HttpDelete(uri);
HttpResponse response = httpClient.execute(put);
int actualStatusCode = response.getStatusLine().getStatusCode();
if (actualStatusCode == 401 || actualStatusCode == 403) {
throw new GatewayAuthenticationException();
}
if (actualStatusCode == 500) {
Header[] headers = response.getHeaders("X-API-Gateway-Error"); //$NON-NLS-1$
if (headers != null && headers.length > 0) {
PublishingException pe = readPublishingException(response);
throw pe;
}
}
if (actualStatusCode >= 300) {
throw new Exception(Messages.i18n.format("GatewayClient.ApiRetiringFailed", actualStatusCode)); //$NON-NLS-1$
}
} catch (PublishingException|GatewayAuthenticationException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Reads a publishing exception from the response.
* @param response
*/
private PublishingException readPublishingException(HttpResponse response) {
InputStream is = null;
PublishingException exception;
try {
is = response.getEntity().getContent();
GatewayApiErrorBean error = mapper.reader(GatewayApiErrorBean.class).readValue(is);
exception = new PublishingException(error.getMessage());
StackTraceElement[] stack = parseStackTrace(error.getStacktrace());
if (stack != null) {
exception.setStackTrace(stack);
}
} catch (Exception e) {
exception = new PublishingException(e.getMessage(), e);
} finally {
IOUtils.closeQuietly(is);
}
return exception;
}
/**
* Reads a registration exception from the response body.
* @param response
*/
private RegistrationException readRegistrationException(HttpResponse response) {
InputStream is = null;
RegistrationException exception;
try {
is = response.getEntity().getContent();
GatewayApiErrorBean error = mapper.reader(GatewayApiErrorBean.class).readValue(is);
exception = new RegistrationException(error.getMessage());
StackTraceElement[] stack = parseStackTrace(error.getStacktrace());
if (stack != null) {
exception.setStackTrace(stack);
}
} catch (Exception e) {
exception = new RegistrationException(e.getMessage(), e);
} finally {
IOUtils.closeQuietly(is);
}
return exception;
}
/**
* Parses a stack trace from the given string.
* @param stacktrace
*/
protected static StackTraceElement[] parseStackTrace(String stacktrace) {
try (BufferedReader reader = new BufferedReader(new StringReader(stacktrace))) {
List<StackTraceElement> elements = new ArrayList<>();
String line;
// Example lines:
// \tat io.apiman.gateway.engine.es.ESRegistry$1.completed(ESRegistry.java:79)
// \tat org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81)\r\n
while ( (line = reader.readLine()) != null) {
if (line.startsWith("\tat ")) { //$NON-NLS-1$
int openParenIdx = line.indexOf('(');
int closeParenIdx = line.indexOf(')');
String classAndMethod = line.substring(4, openParenIdx);
String fileAndLineNum = line.substring(openParenIdx + 1, closeParenIdx);
String className = classAndMethod.substring(0, classAndMethod.lastIndexOf('.'));
String methodName = classAndMethod.substring(classAndMethod.lastIndexOf('.') + 1);
String [] split = fileAndLineNum.split(":"); //$NON-NLS-1$
if (split.length == 1) {
elements.add(new StackTraceElement(className, methodName, fileAndLineNum, -1));
} else {
String fileName = split[0];
String lineNum = split[1];
elements.add(new StackTraceElement(className, methodName, fileName, new Integer(lineNum)));
}
}
}
return elements.toArray(new StackTraceElement[elements.size()]);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}