Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.solr.rest;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.rest.ManagedResourceStorage.StorageIO;
import org.restlet.Request;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.restlet.routing.Router;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.common.util.Utils.fromJSONString;
/**
* Supports runtime mapping of REST API endpoints to ManagedResource
* implementations; endpoints can be registered at either the /schema
* or /config base paths, depending on which base path is more appropriate
* for the type of managed resource.
*/
public class RestManager {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String SCHEMA_BASE_PATH = "/schema";
public static final String MANAGED_ENDPOINT = "/managed";
// used for validating resourceIds provided during registration
private static final Pattern resourceIdRegex = Pattern.compile("(/config|/schema)(/.*)");
private static final boolean DECODE = true;
/**
* Used internally to keep track of registrations during core initialization
*/
private static class ManagedResourceRegistration {
String resourceId;
Class implClass;
Set observers = new LinkedHashSet<>();
private ManagedResourceRegistration(String resourceId,
Class implClass,
ManagedResourceObserver observer)
{
this.resourceId = resourceId;
this.implClass = implClass;
if (observer != null) {
this.observers.add(observer);
}
}
/** Returns resourceId, class, and number of observers of this registered resource */
public Map getInfo() {
Map info = new HashMap<>();
info.put("resourceId", resourceId);
info.put("class", implClass.getName());
info.put("numObservers", String.valueOf(observers.size()));
return info;
}
}
/**
* Per-core registry of ManagedResources found during core initialization.
*
* Registering of managed resources can happen before the RestManager is
* fully initialized. To avoid timing issues, resources register themselves
* and then the RestManager initializes all ManagedResources before the core
* is activated.
*/
public static class Registry {
private Map registered = new TreeMap<>();
// maybe null until there is a restManager
private RestManager initializedRestManager = null;
// REST API endpoints that need to be protected against dynamic endpoint creation
private final Set reservedEndpoints = new HashSet<>();
private final Pattern reservedEndpointsPattern;
public Registry() {
reservedEndpoints.add(SCHEMA_BASE_PATH + MANAGED_ENDPOINT);
for (String reservedEndpoint : SolrSchemaRestApi.getReservedEndpoints()) {
reservedEndpoints.add(reservedEndpoint);
}
reservedEndpointsPattern = getReservedEndpointsPattern();
}
/**
* Returns the set of non-registrable endpoints.
*/
public Set getReservedEndpoints() {
return Collections.unmodifiableSet(reservedEndpoints);
}
/**
* Returns a Pattern, to be used with Matcher.matches(), that will recognize
* prefixes or full matches against reserved endpoints that need to be protected
* against dynamic endpoint registration. group(1) will contain the match
* regardless of whether it's a full match or a prefix.
*/
private Pattern getReservedEndpointsPattern() {
// Match any of the reserved endpoints exactly, or followed by a slash and more stuff
StringBuilder builder = new StringBuilder();
builder.append("(");
boolean notFirst = false;
for (String reservedEndpoint : reservedEndpoints) {
if (notFirst) {
builder.append("|");
} else {
notFirst = true;
}
builder.append(reservedEndpoint);
}
builder.append(")(?:|/.*)");
return Pattern.compile(builder.toString());
}
/**
* Get a view of the currently registered resources.
*/
public Collection getRegistered() {
return Collections.unmodifiableCollection(registered.values());
}
/**
* Register the need to use a ManagedResource; this method is typically called
* by a Solr component during core initialization to register itself as an
* observer of a specific type of ManagedResource. As many Solr components may
* share the same ManagedResource, this method only serves to associate the
* observer with an endpoint and implementation class. The actual construction
* of the ManagedResource and loading of data from storage occurs later once
* the RestManager is fully initialized.
* @param resourceId - An endpoint in the Rest API to manage the resource; must
* start with /config and /schema.
* @param implClass - Class that implements ManagedResource.
* @param observer - Solr component that needs to know when the data being managed
* by the ManagedResource is loaded, such as a TokenFilter.
*/
public synchronized void registerManagedResource(String resourceId,
Class implClass, ManagedResourceObserver observer) {
if (resourceId == null)
throw new IllegalArgumentException(
"Must provide a non-null resourceId to register a ManagedResource!");
Matcher resourceIdValidator = resourceIdRegex.matcher(resourceId);
if (!resourceIdValidator.matches()) {
String errMsg = String.format(Locale.ROOT,
"Invalid resourceId '%s'; must start with %s.",
resourceId, SCHEMA_BASE_PATH);
throw new SolrException(ErrorCode.SERVER_ERROR, errMsg);
}
// protect reserved REST API endpoints from being used by another
Matcher reservedEndpointsMatcher = reservedEndpointsPattern.matcher(resourceId);
if (reservedEndpointsMatcher.matches()) {
throw new SolrException(ErrorCode.SERVER_ERROR,
reservedEndpointsMatcher.group(1)
+ " is a reserved endpoint used by the Solr REST API!");
}
// IMPORTANT: this code should assume there is no RestManager at this point
// it's ok to re-register the same class for an existing path
ManagedResourceRegistration reg = registered.get(resourceId);
if (reg != null) {
if (!reg.implClass.equals(implClass)) {
String errMsg = String.format(Locale.ROOT,
"REST API path %s already registered to instances of %s",
resourceId, reg.implClass.getName());
throw new SolrException(ErrorCode.SERVER_ERROR, errMsg);
}
if (observer != null) {
reg.observers.add(observer);
log.info("Added observer of type {} to existing ManagedResource {}",
observer.getClass().getName(), resourceId);
}
} else {
registered.put(resourceId,
new ManagedResourceRegistration(resourceId, implClass, observer));
log.info("Registered ManagedResource impl {} for path {}",
implClass.getName(), resourceId);
}
// there may be a RestManager, in which case, we want to add this new ManagedResource immediately
if (initializedRestManager != null && initializedRestManager.getManagedResourceOrNull(resourceId) == null) {
initializedRestManager.addRegisteredResource(registered.get(resourceId));
}
}
}
/**
* Locates the RestManager using ThreadLocal SolrRequestInfo.
*/
public static RestManager getRestManager(SolrRequestInfo solrRequestInfo) {
if (solrRequestInfo == null)
throw new ResourceException(Status.SERVER_ERROR_INTERNAL,
"No SolrRequestInfo in this Thread!");
SolrQueryRequest req = solrRequestInfo.getReq();
RestManager restManager =
(req != null) ? req.getCore().getRestManager() : null;
if (restManager == null)
throw new ResourceException(Status.SERVER_ERROR_INTERNAL,
"No RestManager found!");
return restManager;
}
/**
* The Restlet router needs a lightweight extension of ServerResource to delegate a request
* to. ManagedResource implementations are heavy-weight objects that live for the duration of
* a SolrCore, so this class acts as the proxy between Restlet and a ManagedResource when
* doing request processing.
*
*/
public static class ManagedEndpoint extends BaseSolrResource
implements GETable, PUTable, POSTable, DELETEable
{
/**
* Determines the ManagedResource resourceId from the Restlet request.
*/
public static String resolveResourceId(Request restletReq) {
String resourceId = restletReq.getResourceRef().
getRelativeRef(restletReq.getRootRef().getParentRef()).getPath(DECODE);
// all resources are registered with the leading slash
if (!resourceId.startsWith("/"))
resourceId = "/"+resourceId;
return resourceId;
}
protected ManagedResource managedResource;
protected String childId;
/**
* Initialize objects needed to handle a request to the REST API. Specifically,
* we lookup the RestManager using the ThreadLocal SolrRequestInfo and then
* dynamically locate the ManagedResource associated with the request URI.
*/
@Override
public void doInit() throws ResourceException {
super.doInit();
// get the relative path to the requested resource, which is
// needed to locate ManagedResource impls at runtime
String resourceId = resolveResourceId(getRequest());
// supports a request for a registered resource or its child
RestManager restManager =
RestManager.getRestManager(SolrRequestInfo.getRequestInfo());
managedResource = restManager.getManagedResourceOrNull(resourceId);
if (managedResource == null) {
// see if we have a registered endpoint one-level up ...
int lastSlashAt = resourceId.lastIndexOf('/');
if (lastSlashAt != -1) {
String parentResourceId = resourceId.substring(0,lastSlashAt);
log.info("Resource not found for {}, looking for parent: {}",
resourceId, parentResourceId);
managedResource = restManager.getManagedResourceOrNull(parentResourceId);
if (managedResource != null) {
// verify this resource supports child resources
if (!(managedResource instanceof ManagedResource.ChildResourceSupport)) {
String errMsg = String.format(Locale.ROOT,
"%s does not support child resources!", managedResource.getResourceId());
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, errMsg);
}
childId = resourceId.substring(lastSlashAt+1);
log.info("Found parent resource {} for child: {}",
parentResourceId, childId);
}
}
}
if (managedResource == null) {
if (Method.PUT.equals(getMethod()) || Method.POST.equals(getMethod())) {
// delegate create requests to the RestManager
managedResource = restManager.endpoint;
} else {
throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND,
"No REST managed resource registered for path "+resourceId);
}
}
log.info("Found ManagedResource ["+managedResource+"] for "+resourceId);
}
@Override
public Representation put(Representation entity) {
try {
managedResource.doPut(this, entity, parseJsonFromRequestBody(entity));
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
@Override
public Representation post(Representation entity) {
try {
managedResource.doPost(this, entity, parseJsonFromRequestBody(entity));
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
@Override
public Representation delete() {
// only delegate delete child resources to the ManagedResource
// as deleting the actual resource is best handled by the
// RestManager
if (childId != null) {
try {
managedResource.doDeleteChild(this, childId);
} catch (Exception e) {
getSolrResponse().setException(e);
}
} else {
try {
RestManager restManager =
RestManager.getRestManager(SolrRequestInfo.getRequestInfo());
restManager.deleteManagedResource(managedResource);
} catch (Exception e) {
getSolrResponse().setException(e);
}
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
@Override
public Representation get() {
try {
managedResource.doGet(this, childId);
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
/**
* Parses and validates the JSON passed from the to the ManagedResource.
*/
protected Object parseJsonFromRequestBody(Representation entity) {
if (entity.getMediaType() == null) {
entity.setMediaType(MediaType.APPLICATION_JSON);
}
if (!entity.getMediaType().equals(MediaType.APPLICATION_JSON, true)) {
String errMsg = String.format(Locale.ROOT,
"Invalid content type %s; only %s is supported.",
entity.getMediaType(), MediaType.APPLICATION_JSON.toString());
log.error(errMsg);
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, errMsg);
}
String text = null;
try {
text = entity.getText();
} catch (IOException ioExc) {
String errMsg = "Failed to read entity text due to: "+ioExc;
log.error(errMsg, ioExc);
throw new ResourceException(Status.SERVER_ERROR_INTERNAL, errMsg, ioExc);
}
if (text == null || text.trim().length() == 0) {
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Empty request body!");
}
Object parsedJson = null;
try {
parsedJson = fromJSONString(text);
} catch (Exception ioExc) {
String errMsg = String.format(Locale.ROOT,
"Failed to parse request [%s] into JSON due to: %s",
text, ioExc.toString());
log.error(errMsg, ioExc);
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, errMsg, ioExc);
}
return parsedJson;
}
@Override
protected void addDeprecatedWarning() {
//this is not deprecated
}
} // end ManagedEndpoint class
/**
* The RestManager itself supports some endpoints for creating and listing managed resources.
* Effectively, this resource provides the API endpoint for doing CRUD on the registry.
*/
private static class RestManagerManagedResource extends ManagedResource {
private static final String REST_MANAGER_STORAGE_ID = "/rest/managed";
private final RestManager restManager;
public RestManagerManagedResource(RestManager restManager) throws SolrException {
super(REST_MANAGER_STORAGE_ID, restManager.loader, restManager.storageIO);
this.restManager = restManager;
}
/**
* Overrides the parent impl to handle FileNotFoundException better
*/
@Override
protected synchronized void reloadFromStorage() throws SolrException {
String resourceId = getResourceId();
Object data = null;
try {
data = storage.load(resourceId);
} catch (FileNotFoundException fnf) {
// this is ok - simply means there are no managed components added yet
} catch (IOException ioExc) {
throw new SolrException(ErrorCode.SERVER_ERROR,
"Failed to load stored data for "+resourceId+" due to: "+ioExc, ioExc);
}
Object managedData = processStoredData(data);
if (managedInitArgs == null)
managedInitArgs = new NamedList<>();
if (managedData != null)
onManagedDataLoadedFromStorage(managedInitArgs, managedData);
}
/**
* Loads and initializes any ManagedResources that have been created but
* are not associated with any Solr components.
*/
@SuppressWarnings("unchecked")
@Override
protected void onManagedDataLoadedFromStorage(NamedList managedInitArgs, Object managedData)
throws SolrException {
if (managedData == null) {
// this is ok - just means no managed components have been added yet
return;
}
List