/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you 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 org.elasticsearch.thrift;
import org.apache.thrift.TException;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.stream.CachedStreamOutput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.*;
import org.elasticsearch.rest.RestResponse;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
/**
*/
public class ThriftRestImpl extends AbstractComponent implements Rest.Iface {
private final RestController restController;
@Inject
public ThriftRestImpl(Settings settings, RestController restController) {
super(settings);
this.restController = restController;
}
@Override
public org.elasticsearch.thrift.RestResponse execute(RestRequest request) throws TException {
if (logger.isTraceEnabled()) {
logger.trace("thrift message {}", request);
}
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<org.elasticsearch.thrift.RestResponse> ref = new AtomicReference<org.elasticsearch.thrift.RestResponse>();
restController.dispatchRequest(new ThriftRestRequest(request), new RestChannel() {
@Override
public void sendResponse(RestResponse response) {
try {
ref.set(convert(response));
} catch (IOException e) {
// ignore, should not happen...
}
latch.countDown();
}
});
try {
latch.await();
return ref.get();
} catch (Exception e) {
throw new TException("failed to generate response", e);
}
}
private org.elasticsearch.thrift.RestResponse convert(RestResponse response) throws IOException {
org.elasticsearch.thrift.RestResponse tResponse = new org.elasticsearch.thrift.RestResponse(getStatus(response.status()));
if (response.contentLength() > 0) {
if (response.contentThreadSafe()) {
tResponse.setBody(ByteBuffer.wrap(response.content(), 0, response.contentLength()));
} else {
// argh!, we need to copy it over since we are not on the same thread...
byte[] body = new byte[response.contentLength()];
System.arraycopy(response.content(), 0, body, 0, response.contentLength());
tResponse.setBody(ByteBuffer.wrap(body));
if (response instanceof XContentRestResponse) {
XContentBuilder builder = ((XContentRestResponse) response).builder();
if (builder.payload() instanceof CachedStreamOutput.Entry) {
CachedStreamOutput.pushEntry((CachedStreamOutput.Entry) builder.payload());
}
}
}
}
return tResponse;
}
private Status getStatus(RestStatus status) {
switch (status) {
case CONTINUE:
return Status.CONT;
case SWITCHING_PROTOCOLS:
return Status.SWITCHING_PROTOCOLS;
case OK:
return Status.OK;
case CREATED:
return Status.CREATED;
case ACCEPTED:
return Status.ACCEPTED;
case NON_AUTHORITATIVE_INFORMATION:
return Status.NON_AUTHORITATIVE_INFORMATION;
case NO_CONTENT:
return Status.NO_CONTENT;
case RESET_CONTENT:
return Status.RESET_CONTENT;
case PARTIAL_CONTENT:
return Status.PARTIAL_CONTENT;
case MULTI_STATUS:
// no status for this??
return Status.INTERNAL_SERVER_ERROR;
case MULTIPLE_CHOICES:
return Status.MULTIPLE_CHOICES;
case MOVED_PERMANENTLY:
return Status.MOVED_PERMANENTLY;
case FOUND:
return Status.FOUND;
case SEE_OTHER:
return Status.SEE_OTHER;
case NOT_MODIFIED:
return Status.NOT_MODIFIED;
case USE_PROXY:
return Status.USE_PROXY;
case TEMPORARY_REDIRECT:
return Status.TEMPORARY_REDIRECT;
case BAD_REQUEST:
return Status.BAD_REQUEST;
case UNAUTHORIZED:
return Status.UNAUTHORIZED;
case PAYMENT_REQUIRED:
return Status.PAYMENT_REQUIRED;
case FORBIDDEN:
return Status.FORBIDDEN;
case NOT_FOUND:
return Status.NOT_FOUND;
case METHOD_NOT_ALLOWED:
return Status.METHOD_NOT_ALLOWED;
case NOT_ACCEPTABLE:
return Status.NOT_ACCEPTABLE;
case PROXY_AUTHENTICATION:
return Status.INTERNAL_SERVER_ERROR;
case REQUEST_TIMEOUT:
return Status.REQUEST_TIMEOUT;
case CONFLICT:
return Status.CONFLICT;
case GONE:
return Status.GONE;
case LENGTH_REQUIRED:
return Status.LENGTH_REQUIRED;
case PRECONDITION_FAILED:
return Status.PRECONDITION_FAILED;
case REQUEST_ENTITY_TOO_LARGE:
return Status.REQUEST_ENTITY_TOO_LARGE;
case REQUEST_URI_TOO_LONG:
return Status.REQUEST_URI_TOO_LONG;
case UNSUPPORTED_MEDIA_TYPE:
return Status.UNSUPPORTED_MEDIA_TYPE;
case REQUESTED_RANGE_NOT_SATISFIED:
return Status.INTERNAL_SERVER_ERROR;
case EXPECTATION_FAILED:
return Status.EXPECTATION_FAILED;
case UNPROCESSABLE_ENTITY:
return Status.BAD_REQUEST;
case LOCKED:
return Status.BAD_REQUEST;
case FAILED_DEPENDENCY:
return Status.BAD_REQUEST;
case INTERNAL_SERVER_ERROR:
return Status.INTERNAL_SERVER_ERROR;
case NOT_IMPLEMENTED:
return Status.NOT_IMPLEMENTED;
case BAD_GATEWAY:
return Status.BAD_GATEWAY;
case SERVICE_UNAVAILABLE:
return Status.SERVICE_UNAVAILABLE;
case GATEWAY_TIMEOUT:
return Status.GATEWAY_TIMEOUT;
case HTTP_VERSION_NOT_SUPPORTED:
return Status.INTERNAL_SERVER_ERROR;
default:
return Status.INTERNAL_SERVER_ERROR;
}
}
}