use of io.apiman.gateway.engine.io.ISignalWriteStream in project apiman by apiman.
the class ApiRequestExecutorImpl method parsePayload.
/**
* Parse the inbound request's body into a payload object. The object that is
* produced will depend on the type and content-type of the API. Options
* include, but may not be limited to:
* <ul>
* <li>REST+json</li>
* <li>REST+xml</li>
* <li>SOAP+xml</li>
* </ul>
* @param payloadResultHandler
*/
protected void parsePayload(IAsyncResultHandler<Object> payloadResultHandler) {
// Strip out any content-length header from the request. It will very likely
// no longer be accurate.
// $NON-NLS-1$
request.getHeaders().remove("Content-Length");
// Configure the api's max payload buffer size, if it's not already set.
if (api.getMaxPayloadBufferSize() <= 0) {
api.setMaxPayloadBufferSize(maxPayloadBufferSize);
}
// Now "handle" the inbound request stream, which will cause bytes to be streamed
// to the writeStream we provide (which will store the bytes in a buffer for parsing)
final ByteBuffer buffer = new ByteBuffer(2048);
inboundStreamHandler.handle(new ISignalWriteStream() {
private boolean done = false;
@Override
public void abort(Throwable t) {
done = true;
// $NON-NLS-1$
payloadResultHandler.handle(AsyncResultImpl.create(new RuntimeException("Inbound request stream aborted.", t)));
}
@Override
public boolean isFinished() {
return done;
}
@Override
public void write(IApimanBuffer chunk) {
if (done) {
return;
}
if (buffer.length() > api.getMaxPayloadBufferSize()) {
// $NON-NLS-1$
payloadResultHandler.handle(AsyncResultImpl.create(new Exception("Max request payload size exceeded.")));
done = true;
return;
}
buffer.append(chunk);
}
@Override
public void end() {
if (done) {
return;
}
// When end() is called, the stream of bytes is done and we can parse them into
// an appropriate payload object.
done = true;
if (buffer.length() == 0) {
payloadResultHandler.handle(AsyncResultImpl.create(null));
} else {
payloadIO = null;
if ("soap".equalsIgnoreCase(api.getEndpointType())) {
// $NON-NLS-1$
payloadIO = new SoapPayloadIO();
} else if ("rest".equalsIgnoreCase(api.getEndpointType())) {
// $NON-NLS-1$
if ("xml".equalsIgnoreCase(api.getEndpointContentType())) {
// $NON-NLS-1$
payloadIO = new XmlPayloadIO();
} else if ("json".equalsIgnoreCase(api.getEndpointContentType())) {
// $NON-NLS-1$
payloadIO = new JsonPayloadIO();
}
}
if (payloadIO == null) {
payloadIO = new BytesPayloadIO();
}
try {
Object payload = payloadIO.unmarshall(buffer.getBytes());
payloadResultHandler.handle(AsyncResultImpl.create(payload));
} catch (Exception e) {
// $NON-NLS-1$
payloadResultHandler.handle(AsyncResultImpl.create(new Exception("Failed to parse inbound request payload.", e)));
}
}
}
/**
* Because of the way the parsePayload code was written, it's possible
* with async for apiConnection to be +null+ when this is called.
*
* This is because parsePayload invokes inboundStreamHandler before
* policiesLoadedHandler has had a chance to return (and assigned apiConnection).
*
* To work around this we check whether apiConnection is null for #drainHandler
* and #isFull.
*/
@Override
public void drainHandler(IAsyncHandler<Void> drainHandler) {
if (apiConnection != null)
apiConnection.drainHandler(drainHandler);
}
@Override
public boolean isFull() {
if (apiConnection != null) {
return apiConnection.isFull();
} else {
return false;
}
}
});
}
use of io.apiman.gateway.engine.io.ISignalWriteStream in project apiman by apiman.
the class DefaultEngineFactoryTest method testCreateEngine.
/**
* Test method for {@link io.apiman.gateway.engine.impl.AbstractEngineFactory#createEngine()}.
* @throws ExecutionException
* @throws InterruptedException
*/
@Test
public void testCreateEngine() throws InterruptedException, ExecutionException {
DefaultEngineFactory factory = new DefaultEngineFactory() {
@Override
protected IComponentRegistry createComponentRegistry(IPluginRegistry pluginRegistry) {
return new DefaultComponentRegistry() {
@Override
protected void registerBufferFactoryComponent() {
addComponent(IBufferFactoryComponent.class, new ByteBufferFactoryComponent());
}
};
}
@Override
protected IConnectorFactory createConnectorFactory(IPluginRegistry pluginRegistry) {
return new IConnectorFactory() {
@Override
public IApiConnector createConnector(ApiRequest request, Api api, RequiredAuthType requiredAuthType, boolean hasDataPolicy, IConnectorConfig connectorConfig) {
Assert.assertEquals("test", api.getEndpointType());
Assert.assertEquals("test:endpoint", api.getEndpoint());
IApiConnector connector = new IApiConnector() {
/**
* @see io.apiman.gateway.engine.IApiConnector#connect(io.apiman.gateway.engine.beans.ApiRequest, io.apiman.gateway.engine.async.IAsyncResultHandler)
*/
@Override
public IApiConnection connect(ApiRequest request, IAsyncResultHandler<IApiConnectionResponse> handler) throws ConnectorException {
final ApiResponse response = new ApiResponse();
response.setCode(200);
// $NON-NLS-1$
response.setMessage("OK");
mockApiConnectionResponse = new MockApiConnectionResponse() {
@Override
public void write(IApimanBuffer chunk) {
handleBody(chunk);
}
@Override
protected void handleHead(ApiResponse head) {
return;
}
@Override
public ApiResponse getHead() {
return response;
}
@Override
public void end() {
handleEnd();
}
@Override
public void transmit() {
transmitHandler.handle((Void) null);
}
@Override
public void abort(Throwable t) {
}
};
IAsyncResult<IApiConnectionResponse> mockResponseResultHandler = mock(IAsyncResult.class);
given(mockResponseResultHandler.isSuccess()).willReturn(true);
given(mockResponseResultHandler.isError()).willReturn(false);
given(mockResponseResultHandler.getResult()).willReturn(mockApiConnectionResponse);
mockApiConnection = mock(MockApiConnection.class);
given(mockApiConnection.getHead()).willReturn(request);
// Handle head
handler.handle(mockResponseResultHandler);
return mockApiConnection;
}
};
return connector;
}
@Override
public IConnectorConfig createConnectorConfig(ApiRequest request, Api api) {
return new TestConnectorConfigImpl();
}
};
}
@Override
protected IDelegateFactory createLoggerFactory(IPluginRegistry pluginRegistry) {
return null;
}
@Override
protected IApiRequestPathParser createRequestPathParser(IPluginRegistry pluginRegistry) {
return new DefaultRequestPathParser(null);
}
@Override
protected void complete() {
}
};
IEngine engine = factory.createEngine();
Assert.assertNotNull(engine);
// create a api
Api api = new Api();
api.setEndpointType("test");
api.setEndpoint("test:endpoint");
api.setOrganizationId("TestOrg");
api.setApiId("TestApi");
api.setVersion("1.0");
// create a client
Client app = new Client();
app.setClientId("TestApp");
app.setOrganizationId("TestOrg");
app.setVersion("1.0");
app.setApiKey("client-12345");
Contract contract = new Contract();
contract.setPlan("Gold");
contract.setApiId("TestApi");
contract.setApiOrgId("TestOrg");
contract.setApiVersion("1.0");
contract.setPolicies(policyList);
app.addContract(contract);
// simple api/app config
engine.getRegistry().publishApi(api, new IAsyncResultHandler<Void>() {
@Override
public void handle(IAsyncResult<Void> result) {
}
});
engine.getRegistry().registerClient(app, new IAsyncResultHandler<Void>() {
@Override
public void handle(IAsyncResult<Void> result) {
}
});
ApiRequest request = new ApiRequest();
request.setApiKey("client-12345");
request.setApiId("TestApi");
request.setApiOrgId("TestOrg");
request.setApiVersion("1.0");
request.setDestination("/");
request.setUrl("http://localhost:9999/");
request.setType("TEST");
IApiRequestExecutor prExecutor = engine.executor(request, new IAsyncResultHandler<IEngineResult>() {
// At this point, we are either saying *fail* or *response connection is ready*
@Override
public void handle(IAsyncResult<IEngineResult> result) {
IEngineResult er = result.getResult();
// No exception occurred
Assert.assertTrue(result.isSuccess());
// The chain evaluation succeeded
Assert.assertNotNull(er);
Assert.assertTrue(!er.isFailure());
Assert.assertNotNull(er.getApiResponse());
// $NON-NLS-1$
Assert.assertEquals("OK", er.getApiResponse().getMessage());
er.bodyHandler(mockBodyHandler);
er.endHandler(mockEndHandler);
}
});
prExecutor.streamHandler(new IAsyncHandler<ISignalWriteStream>() {
@Override
public void handle(ISignalWriteStream streamWriter) {
streamWriter.write(mockBufferInbound);
streamWriter.end();
}
});
transmitHandler = new IAsyncHandler<Void>() {
@Override
public void handle(Void result) {
// NB: This is cheating slightly for testing purposes, we don't have real async here.
// Only now start writing stuff, so user has had opportunity to set handlers
mockApiConnectionResponse.write(mockBufferOutbound);
mockApiConnectionResponse.end();
}
};
prExecutor.execute();
// Request handler should receive the mock inbound buffer once only
verify(mockApiConnection, times(1)).write(mockBufferInbound);
// Ultimately user should receive the contrived response and end in order.
InOrder order = inOrder(mockBodyHandler, mockEndHandler);
order.verify(mockBodyHandler).handle(mockBufferOutbound);
order.verify(mockEndHandler).handle((Void) null);
}
use of io.apiman.gateway.engine.io.ISignalWriteStream in project apiman by apiman.
the class InMemoryCacheStoreComponent method putBinary.
/**
* @see io.apiman.gateway.engine.components.ICacheStoreComponent#putBinary(java.lang.String, java.lang.Object, long)
*/
@Override
public <T> ISignalWriteStream putBinary(final String cacheKey, final T jsonObject, final long timeToLive) throws IOException {
final IApimanBuffer buffer = bufferFactory.createBuffer();
synchronized (mapMutex) {
expireOnMap.put(cacheKey, System.currentTimeMillis() + (timeToLive * 1000));
objectCache.put(cacheKey, jsonObject);
dataCache.put(cacheKey, buffer);
}
return new ISignalWriteStream() {
private boolean finished = false;
@Override
public void abort(Throwable t) {
finished = true;
}
@Override
public boolean isFinished() {
return finished;
}
@Override
public void write(IApimanBuffer chunk) {
buffer.append(chunk);
}
@Override
public void end() {
finished = true;
synchronized (cacheSizeMutex) {
cacheSize += buffer.length();
if (cacheSize > maxCacheSize) {
synchronized (mapMutex) {
while (cacheSize > maxCacheSize) {
String cacheKey = objectCache.keySet().iterator().next();
objectCache.remove(cacheKey);
expireOnMap.remove(cacheKey);
IApimanBuffer removedBuffer = dataCache.remove(cacheKey);
if (removedBuffer != null) {
cacheSize -= removedBuffer.length();
}
}
}
}
}
}
};
}
use of io.apiman.gateway.engine.io.ISignalWriteStream in project apiman by apiman.
the class CachingPolicy method responseDataHandler.
/**
* @see io.apiman.gateway.engine.policies.AbstractMappedDataPolicy#responseDataHandler(io.apiman.gateway.engine.beans.ApiResponse, io.apiman.gateway.engine.policy.IPolicyContext, java.lang.Object)
*/
@Deprecated
@Override
protected IReadWriteStream<ApiResponse> responseDataHandler(final ApiResponse response, IPolicyContext context, CachingConfig policyConfiguration) {
// Possibly cache the response for future posterity.
// Check the response code against list in config (empty/null list means cache all).
final boolean shouldCache = (context.getAttribute(SHOULD_CACHE_ATTR, Boolean.FALSE) && ofNullable(policyConfiguration.getStatusCodes()).map(statusCodes -> statusCodes.isEmpty() || statusCodes.contains(String.valueOf(response.getCode()))).orElse(true));
if (shouldCache) {
try {
String cacheId = context.getAttribute(CACHE_ID_ATTR, null);
ICacheStoreComponent cache = context.getComponent(ICacheStoreComponent.class);
final ISignalWriteStream writeStream = cache.putBinary(cacheId, response, policyConfiguration.getTtl());
return new AbstractStream<ApiResponse>() {
@Override
public ApiResponse getHead() {
return response;
}
@Override
protected void handleHead(ApiResponse head) {
}
@Override
public void write(IApimanBuffer chunk) {
writeStream.write(chunk);
super.write(chunk);
}
@Override
public void end() {
writeStream.end();
super.end();
}
};
} catch (ComponentNotFoundException | IOException e) {
// TODO log error
return null;
}
} else {
return null;
}
}
use of io.apiman.gateway.engine.io.ISignalWriteStream in project apiman by apiman.
the class CachingResourcesPolicy method responseDataHandler.
/**
* @see io.apiman.gateway.engine.policies.AbstractMappedDataPolicy#responseDataHandler(io.apiman.gateway.engine.beans.ApiResponse, io.apiman.gateway.engine.policy.IPolicyContext, java.lang.Object)
*/
@Override
protected IReadWriteStream<ApiResponse> responseDataHandler(final ApiResponse response, IPolicyContext context, CachingResourcesConfig policyConfiguration) {
if (response == null) {
// if the response is empty because of a policy failure before we end here and return null
return null;
}
List<CachingResourcesSettingsEntry> possibleMatchingCachingEntries = context.getAttribute(CACHE_POSSIBLE_MATCHING_ENTRIES, new ArrayList<CachingResourcesSettingsEntry>());
boolean isAMatch = false;
for (CachingResourcesSettingsEntry entry : possibleMatchingCachingEntries) {
isAMatch = isAMatch || matchesPolicyEntryVsActualValue(entry.getStatusCode(), String.valueOf(response.getCode()));
}
// Possibly cache the response for future posterity.
final boolean shouldCache = context.getAttribute(SHOULD_CACHE_ATTR, Boolean.FALSE) && isAMatch;
if (shouldCache) {
try {
String cacheId = context.getAttribute(CACHE_ID_ATTR, null);
ICacheStoreComponent cache = context.getComponent(ICacheStoreComponent.class);
final ISignalWriteStream writeStream = cache.putBinary(cacheId, response, policyConfiguration.getTtl());
return new AbstractStream<ApiResponse>() {
@Override
public ApiResponse getHead() {
return response;
}
@Override
protected void handleHead(ApiResponse head) {
}
@Override
public void write(IApimanBuffer chunk) {
writeStream.write(chunk);
super.write(chunk);
}
@Override
public void end() {
writeStream.end();
super.end();
}
};
} catch (ComponentNotFoundException | IOException e) {
throw new RuntimeException(e);
}
}
return null;
}
Aggregations