![JAR search and dependency download from the Maven repository](/logo.png)
com.bigdata.rdf.sail.webapp.MultiTenancyServlet Maven / Gradle / Ivy
/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.bigdata.rdf.sail.webapp;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.openrdf.model.BNode;
import org.openrdf.model.Graph;
import org.openrdf.model.impl.LinkedHashModel;
import org.openrdf.model.impl.ValueFactoryImpl;
import com.bigdata.btree.IndexMetadata;
import com.bigdata.journal.IIndexManager;
import com.bigdata.journal.IJournal;
import com.bigdata.journal.Journal;
import com.bigdata.journal.Tx;
import com.bigdata.rdf.properties.PropertiesFormat;
import com.bigdata.rdf.properties.PropertiesParser;
import com.bigdata.rdf.properties.PropertiesParserFactory;
import com.bigdata.rdf.properties.PropertiesParserRegistry;
import com.bigdata.rdf.sail.BigdataSail;
import com.bigdata.rdf.sail.webapp.client.ConnectOptions;
import com.bigdata.rdf.store.AbstractTripleStore;
import com.bigdata.relation.RelationSchema;
import com.bigdata.service.AbstractFederation;
import com.bigdata.service.AbstractTransactionService;
import com.bigdata.service.IBigdataFederation;
import com.bigdata.util.PropertyUtil;
import com.bigdata.rdf.sail.BigdataSailHelper;
/**
* Mult-tenancy Administration Servlet (management for bigdata namespaces). A
* bigdata namespace corresponds to a partition in the naming of durable
* resources. A {@link Journal} or {@link IBigdataFederation} may have multiple
* KB instances, each in their own namespace. This servlet allows you to manage
* those KB instances using CRUD operations.
*
* @see
* NanoSparqlServer Admin API for Multi-tenant deployments
*
* @author thompsonbry
*
* FIXME GROUP COMMIT: The other operations in this class also should
* use the new REST API pattern, but are not intrinsically sensitive.
*/
public class MultiTenancyServlet extends BigdataRDFServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
static private final transient Logger log = Logger.getLogger(MultiTenancyServlet.class);
/**
* URL query parameter used to override the servlet init parameter
* {@link ConfigParams#DESCRIBE_EACH_NAMED_GRAPH}.
*/
protected static final String DESCRIBE_EACH_NAMED_GRAPH = "describe-each-named-graph";
/**
* URL query parameter used to specify that only the default namespace
* should be described.
*/
protected static final String DESCRIBE_DEFAULT_NAMESPACE = "describe-default-namespace";
/**
* URL query parameter used to specify that full text index
* will be created if not exists.
*/
private static final String FORCE_INDEX_CREATE_PARAMETER = "force-index-create";
/**
* Delegate for the sparql end point expressed by
* .../namespace/NAMESPACE/sparql
.
*/
private RESTServlet m_restServlet;
private static final String namespaceRegex = "[^.]+\\Z";
public static final Set PROPERTIES_BLACK_LIST = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
Journal.Options.BUFFER_MODE,
Journal.Options.FILE,
Journal.Options.INITIAL_EXTENT,
Journal.Options.MAXIMUM_EXTENT,
IndexMetadata.Options.WRITE_RETENTION_QUEUE_CAPACITY,
IndexMetadata.Options.BTREE_BRANCHING_FACTOR,
RelationSchema.CLASS,
AbstractTransactionService.Options.MIN_RELEASE_AGE,
RelationSchema.NAMESPACE,
RelationSchema.CONTAINER)
));
public MultiTenancyServlet() {
}
/**
* Overridden to create and initialize the delegate {@link Servlet}
* instances.
*/
@Override
public void init() throws ServletException {
super.init();
m_restServlet = new RESTServlet();
m_restServlet.init(getServletConfig());
}
/**
* Handle namespace create.
*/
@Override
protected void doPost(final HttpServletRequest req,
final HttpServletResponse resp) throws IOException {
if (req.getRequestURI().endsWith("/namespace")) {
// CREATE NAMESPACE.
doCreateNamespace(req, resp);
return;
} else if (req.getRequestURI().endsWith("/prepareProperties")) {
// Prepare properties.
doPrepareProperties(req, resp);
return;
}
final String namespace = getNamespace(req);
if (req.getRequestURI().endsWith(ConnectOptions.urlEncode(namespace) + "/textIndex")) {
// CREATE NAMESPACE.
doRebuildTextIndex(req, resp, namespace);
return;
}
/*
* Pass through to the SPARQL end point REST API.
*
* Note: This also handles CANCEL QUERY, which is a POST.
*/
m_restServlet.doPost(req, resp);
}
/**
* Delete the KB associated with the effective namespace.
*
* @see
* Missing URL encoding in RemoteRepositoryManager
*/
@Override
protected void doDelete(final HttpServletRequest req,
final HttpServletResponse resp) throws IOException {
if (!isWritable(getServletContext(), req, resp)) {
// Service must be writable.
return;
}
final String namespace = getNamespace(req);
if (req.getRequestURI().endsWith(
"/namespace/" + ConnectOptions.urlEncode(namespace))) {
// Delete that namespace.
doDeleteNamespace(req, resp);
return;
}
// Pass through to the SPARQL end point REST API.
m_restServlet.doDelete(req, resp);
}
@Override
protected void doPut(final HttpServletRequest req,
final HttpServletResponse resp) throws IOException {
if (!isWritable(getServletContext(), req, resp)) {
// Service must be writable.
return;
}
// Pass through to the SPARQL end point REST API.
m_restServlet.doPut(req, resp);
}
/**
* Handles all read-only namespace oriented administration requests.
*/
@Override
protected void doGet(final HttpServletRequest req,
final HttpServletResponse resp) throws IOException {
if (req.getRequestURI().endsWith("/namespace")) {
// Describe all namespaces.
doDescribeNamespaces(req, resp);
return;
} else if (req.getRequestURI().endsWith("/properties")) {
// Show properties.
doShowProperties(req, resp);
return;
}
// Pass through to the SPARQL end point REST API.
m_restServlet.doGet(req, resp);
return;
}
/**
* Prepare a list of properties for a new namespace.
*
*
* Request-URI
* ...
* Content-Type=...
* ...
* PropertySet
*
*
* @param req
* @param resp
* @throws IOException
*/
private void doPrepareProperties(final HttpServletRequest req,
final HttpServletResponse resp) throws IOException {
if (!isWritable(getServletContext(), req, resp)) {
// Service must be writable.
return;
}
final BigdataRDFContext context = getBigdataRDFContext();
final IIndexManager indexManager = context.getIndexManager();
final long timestamp = getTimestamp(req);
/*
* 1. Read the request entity, which must be some kind of Properties
* object. The BigdataSail.Options.NAMESPACE property defaults to "kb".
* A non-default value SHOULD be specified by the client.
*
* 2. Wrap and flatten the base properties for the Journal or
* Federation. This provides defaults for properties which were not
* explicitly configured for this KB instance.
*
* 3. Add the given properties to the flattened defaults to obtain the
* effective properties.
*/
final Properties given, defaults, effectiveProperties;
{
final String contentType = req.getContentType();
if (log.isInfoEnabled())
log.info("Request body: " + contentType);
final PropertiesFormat format = PropertiesFormat.forMIMEType(contentType);
if (format == null) {
buildAndCommitResponse(resp, HTTP_BADREQUEST, MIME_TEXT_PLAIN,
"Content-Type not recognized as Properties: "
+ contentType);
return;
}
if (log.isInfoEnabled())
log.info("Format=" + format);
final PropertiesParserFactory parserFactory = PropertiesParserRegistry
.getInstance().get(format);
if (parserFactory == null) {
buildAndCommitResponse(resp, HTTP_INTERNALERROR, MIME_TEXT_PLAIN,
"Parser factory not found: Content-Type="
+ contentType + ", format=" + format);
return;
}
/*
* There is a request body, so let's try and parse it.
*/
final PropertiesParser parser = parserFactory.getParser();
// The given Properties.
given = parser.parse(req.getInputStream());
//check properties
BigdataSail.checkProperties(given);
// The effective namespace for the new KB.
final String namespace = given.getProperty(
BigdataSail.Options.NAMESPACE,
BigdataSail.Options.DEFAULT_NAMESPACE);
try {
if (!Pattern.matches(namespaceRegex , namespace)) {
throw new IllegalArgumentException("Namespace should not be empty nor include '.' character");
}
} catch (Throwable e) {
launderThrowable(e, resp, "namespace=" + namespace);
return;
}
/*
* Get the default Properties.
*/
if (indexManager instanceof IJournal) {
final IJournal jnl = (IJournal) indexManager;
defaults = new Properties(jnl.getProperties());
} else {
final AbstractFederation> fed = (AbstractFederation>) indexManager;
defaults = fed.getClient().getProperties();
}
/*
* Produce the effective properties.
*/
{
effectiveProperties = PropertyUtil.flatCopy(defaults);
for (Map.Entry
© 2015 - 2025 Weber Informatics LLC | Privacy Policy