com.linkedin.restli.server.RestLiServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of restli-server Show documentation
Show all versions of restli-server Show documentation
Pegasus is a framework for building robust, scalable service architectures using dynamic discovery and simple asychronous type-checked REST + JSON APIs.
/*
Copyright (c) 2012 LinkedIn Corp.
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 com.linkedin.restli.server;
import com.linkedin.common.callback.Callback;
import com.linkedin.jersey.api.uri.UriBuilder;
import com.linkedin.parseq.Engine;
import com.linkedin.r2.message.RequestContext;
import com.linkedin.r2.message.rest.RestRequest;
import com.linkedin.r2.message.rest.RestRequestBuilder;
import com.linkedin.r2.message.rest.RestResponse;
import com.linkedin.r2.util.URIUtil;
import com.linkedin.restli.common.HttpStatus;
import com.linkedin.restli.common.ProtocolVersion;
import com.linkedin.restli.internal.common.AllProtocolVersions;
import com.linkedin.restli.internal.common.ProtocolVersionUtil;
import com.linkedin.restli.internal.server.RestLiCallback;
import com.linkedin.restli.internal.server.RestLiMethodInvoker;
import com.linkedin.restli.internal.server.RestLiResponseHandler;
import com.linkedin.restli.internal.server.RestLiRouter;
import com.linkedin.restli.internal.server.RoutingResult;
import com.linkedin.restli.internal.server.methods.response.ErrorResponseBuilder;
import com.linkedin.restli.internal.server.model.ResourceMethodDescriptor;
import com.linkedin.restli.internal.server.model.ResourceMethodDescriptor.InterfaceType;
import com.linkedin.restli.internal.server.model.ResourceModel;
import com.linkedin.restli.internal.server.model.RestLiApiBuilder;
import com.linkedin.restli.server.resources.PrototypeResourceFactory;
import com.linkedin.restli.server.resources.ResourceFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author dellamag
* @author Zhenkai Zhu
*/
public class RestLiServer extends BaseRestServer
{
public static final String DEBUG_PATH_SEGMENT = "__debug";
private static final Logger log = LoggerFactory.getLogger(RestLiServer.class);
private final RestLiConfig _config;
private final RestLiRouter _router;
private final ResourceFactory _resourceFactory;
private final RestLiMethodInvoker _methodInvoker;
private final RestLiResponseHandler _responseHandler;
private final RestLiDocumentationRequestHandler _docRequestHandler;
private final ErrorResponseBuilder _errorResponseBuilder;
private final List _invokeAwares;
private final Map _debugHandlers;
private boolean _isDocInitialized = false;
public RestLiServer(final RestLiConfig config)
{
this(config, new PrototypeResourceFactory());
}
public RestLiServer(final RestLiConfig config, final ResourceFactory resourceFactory)
{
this(config, resourceFactory, null, null);
}
public RestLiServer(final RestLiConfig config,
final ResourceFactory resourceFactory,
final Engine engine)
{
this(config, resourceFactory, engine, null);
}
public RestLiServer(final RestLiConfig config,
final ResourceFactory resourceFactory,
final Engine engine,
final List invokeAwares)
{
super(config);
_config = config;
_errorResponseBuilder = new ErrorResponseBuilder(config.getErrorResponseFormat(),
config.getInternalErrorMessage());
_resourceFactory = resourceFactory;
_rootResources = new RestLiApiBuilder(config).build();
_resourceFactory.setRootResources(_rootResources);
_router = new RestLiRouter(_rootResources);
_methodInvoker = new RestLiMethodInvoker(_resourceFactory, engine, _errorResponseBuilder);
_responseHandler = new RestLiResponseHandler.Builder()
.setErrorResponseBuilder(_errorResponseBuilder)
.setPermissiveEncoding(config.getPermissiveEncoding())
.build();
_docRequestHandler = config.getDocumentationRequestHandler();
_invokeAwares = (invokeAwares == null) ? Collections.emptyList() : Collections.unmodifiableList(invokeAwares);
_debugHandlers = new HashMap();
for (RestLiDebugRequestHandler debugHandler : config.getDebugRequestHandlers())
{
_debugHandlers.put(debugHandler.getHandlerId(), debugHandler);
}
// verify that if there are resources using the engine, then the engine is not null
if (engine == null)
{
for (ResourceModel model : _rootResources.values())
{
for (ResourceMethodDescriptor desc : model.getResourceMethodDescriptors())
{
final InterfaceType type = desc.getInterfaceType();
if (type == InterfaceType.PROMISE || type == InterfaceType.TASK)
{
final String fmt =
"ParSeq based method %s.%s, but no engine given. "
+ "Check your RestLiServer construction, spring wiring, "
+ "and container-pegasus-restli-server-cmpt version.";
log.warn(String.format(fmt, model.getResourceClass().getName(), desc.getMethod()
.getName()));
}
}
}
}
}
public Map getRootResources()
{
return Collections.unmodifiableMap(_rootResources);
}
/**
* @see BaseRestServer#doHandleRequest(com.linkedin.r2.message.rest.RestRequest,
* com.linkedin.r2.message.RequestContext, com.linkedin.common.callback.Callback)
*/
@Override
protected void doHandleRequest(final RestRequest request, final RequestContext requestContext,
final Callback callback)
{
if (_docRequestHandler == null || !_docRequestHandler.isDocumentationRequest(request))
{
RestLiDebugRequestHandler debugHandlerForRequest = findDebugRequestHandler(request);
if (debugHandlerForRequest != null)
{
handleDebugRequest(debugHandlerForRequest, request, requestContext, callback);
}
else
{
handleResourceRequest(request,
requestContext,
new RequestExecutionCallbackAdapter(callback),
false);
}
}
else
{
handleDocumentationRequest(request, callback);
}
}
private boolean isSupportedProtocolVersion(ProtocolVersion clientProtocolVersion,
ProtocolVersion lowerBound,
ProtocolVersion upperBound)
{
int lowerCheck = clientProtocolVersion.compareTo(lowerBound);
int upperCheck = clientProtocolVersion.compareTo(upperBound);
return lowerCheck >= 0 && upperCheck <= 0;
}
/**
* Ensures that the Rest.li protocol version used by the client is valid based on the config.
*
* (assume the protocol version used by the client is "v")
*
* If we are using {@link com.linkedin.restli.server.RestLiConfig.RestliProtocolCheck#STRICT} then
* {@link AllProtocolVersions#BASELINE_PROTOCOL_VERSION} <= v <= {@link AllProtocolVersions#LATEST_PROTOCOL_VERSION}
*
* If we are using {@link com.linkedin.restli.server.RestLiConfig.RestliProtocolCheck#RELAXED} then
* {@link AllProtocolVersions#BASELINE_PROTOCOL_VERSION} <= v <= {@link AllProtocolVersions#NEXT_PROTOCOL_VERSION}
*
* @param request the incoming request from the client
* @throws RestLiServiceException if the protocol version used by the client is not valid based on the rules described
* above
*/
private void ensureRequestUsesValidRestliProtocol(final RestRequest request) throws RestLiServiceException
{
ProtocolVersion clientProtocolVersion = ProtocolVersionUtil.extractProtocolVersion(request.getHeaders());
ProtocolVersion lowerBound = AllProtocolVersions.BASELINE_PROTOCOL_VERSION;
ProtocolVersion upperBound = AllProtocolVersions.LATEST_PROTOCOL_VERSION;
if (_config.getRestliProtocolCheck() == RestLiConfig.RestliProtocolCheck.RELAXED)
{
upperBound = AllProtocolVersions.NEXT_PROTOCOL_VERSION;
}
if (!isSupportedProtocolVersion(clientProtocolVersion, lowerBound, upperBound))
{
throw new RestLiServiceException(HttpStatus.S_400_BAD_REQUEST, "Rest.li protocol version " +
clientProtocolVersion + " used by the client is not supported!");
}
}
private void handleDebugRequest(
final RestLiDebugRequestHandler debugHandler,
final RestRequest request,
final RequestContext requestContext,
final Callback callback)
{
debugHandler.handleRequest(
request,
requestContext,
new RestLiDebugRequestHandler.ResourceDebugRequestHandler(){
@Override
public void handleRequest(final RestRequest request, final RequestContext requestContext,
final RequestExecutionCallback callback)
{
//Create a new request at this point from the debug request by removing the path suffix
// starting with "__debug".
String fullPath = request.getURI().getPath();
int debugSegmentIndex = fullPath.indexOf(DEBUG_PATH_SEGMENT);
RestRequestBuilder requestBuilder = new RestRequestBuilder(request);
UriBuilder uriBuilder = UriBuilder.fromUri(request.getURI());
uriBuilder.replacePath(request.getURI().getPath().substring(0, debugSegmentIndex - 1));
requestBuilder.setURI(uriBuilder.build());
handleResourceRequest(requestBuilder.build(), requestContext, callback, true);
}
},
callback);
}
private void handleResourceRequest(final RestRequest request, final RequestContext requestContext,
final RequestExecutionCallback callback,
final boolean isDebugMode)
{
try
{
ensureRequestUsesValidRestliProtocol(request);
}
catch (RestLiServiceException e)
{
final RestLiCallback
© 2015 - 2025 Weber Informatics LLC | Privacy Policy