package cloudone.client.internal;
import cloudone.ServiceFullName;
import cloudone.internal.ApplicationFullName;
import cloudone.internal.nimbostratus.CumulonimbusClient;
import org.glassfish.jersey.client.JerseyClient;
import org.glassfish.jersey.client.JerseyInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Martin Mares (martin.mares at oracle.com)
*/
public abstract class InvokerBase {
private static final Logger LOGGER = LoggerFactory.getLogger(InvokerBase.class);
protected final JerseyClient client;
protected final UriBuilder targetUri;
protected final ServiceFullName[] serviceFullNames;
protected final List<MediaType> acceptMediaTypes = new ArrayList<>();
public InvokerBase(JerseyClient client, UriBuilder targetUri, ServiceFullName[] serviceFullNames) {
this.client = client;
this.targetUri = targetUri;
this.serviceFullNames = serviceFullNames;
}
protected List<ApplicationFullName> findTargetApplications(String method) {
String path = targetUri.build().getPath();
List<ApplicationFullName> apps = CumulonimbusClient.getInstance().getApplicationsForPath(path, method);
List<ApplicationFullName> result;
if (serviceFullNames == null || serviceFullNames.length == 0) {
result = apps;
} else {
result = new ArrayList<>();
for (ApplicationFullName app : apps) {
for (ServiceFullName serviceFullName : serviceFullNames) {
if (serviceFullName.accepts(app.getServiceName())) {
result.add(app);
break;
}
}
}
}
return result;
}
/**
* Add the accepted response media types.
*
* @param responseMediaTypes accepted response media types.
* @return the updated invoker.
*/
public InvokerBase accept(String... responseMediaTypes) {
if (responseMediaTypes != null) {
MediaType[] types = new MediaType[responseMediaTypes.length];
for (int i = 0; i < responseMediaTypes.length; i++) {
types[i] = MediaType.valueOf(responseMediaTypes[i]);
}
accept(types);
}
return this;
}
/**
* Add the accepted response media types.
*
* @param mediaTypes accepted response media types.
* @return the updated invoker.
*/
public InvokerBase accept(MediaType... mediaTypes) {
if (mediaTypes != null) {
acceptMediaTypes.addAll(Arrays.asList(mediaTypes));
}
return this;
}
protected abstract boolean isResponseCodeAcceptable(int code);
protected Response method(ApplicationFullName targetApplication, String name, Entity<?> entity) {
int port = LoadBalancer.getInstance().getPort(targetApplication);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("method(" + targetApplication + ", " + name + "): port: " + port);
}
long startAt = -1;
if (port > 0) {
UriBuilder uriBuilder = targetUri
.clone()
.scheme("http")
.host("localhost")
.port(port);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("method(" + targetApplication + ", " + name + "): URI: " + uriBuilder.build());
}
try {
JerseyInvocation.Builder request = client.target(uriBuilder.build()).request();
for (MediaType accept : acceptMediaTypes) {
request = request.accept(accept);
}
JerseyInvocation invocation;
if (entity == null) {
invocation = request.build(name);
} else {
invocation = request.build(name, entity);
}
startAt = System.currentTimeMillis();
Response response = invocation.invoke();
if (isResponseCodeAcceptable(response.getStatus())) {
return response;
}
} catch (WebApplicationException wExc) {
if (isResponseCodeAcceptable(wExc.getResponse().getStatus())) {
throw wExc;
}
} catch (Exception e) {
//TODO: Provide support for balancing to another endpoint (another endpoint of the same cluster).
LOGGER.warn("Cannot reach application " + targetApplication + " on port " + port, e);
} finally {
if (startAt > 0) {
LoadBalancer.getInstance().updateStats(targetApplication, port, System.currentTimeMillis() - startAt);
}
}
}
return null;
}
}