/* * Copyright Aduna (http://www.aduna-software.com/) (c) 2007. * * Licensed under the Aduna BSD-style license. */ package org.openrdf.http.server.repository.statements; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; import static org.openrdf.http.protocol.Protocol.BASEURI_PARAM_NAME; import static org.openrdf.http.protocol.Protocol.CONTEXT_PARAM_NAME; import static org.openrdf.http.protocol.Protocol.INCLUDE_INFERRED_PARAM_NAME; import static org.openrdf.http.protocol.Protocol.OBJECT_PARAM_NAME; import static org.openrdf.http.protocol.Protocol.PREDICATE_PARAM_NAME; import static org.openrdf.http.protocol.Protocol.SUBJECT_PARAM_NAME; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContextException; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import info.aduna.webapp.util.HttpServerUtil; import info.aduna.webapp.views.EmptySuccessView; import org.openrdf.http.protocol.Protocol; import org.openrdf.http.protocol.error.ErrorInfo; import org.openrdf.http.protocol.error.ErrorType; import org.openrdf.http.protocol.transaction.TransactionReader; import org.openrdf.http.protocol.transaction.operations.TransactionOperation; import org.openrdf.http.server.ClientHTTPException; import org.openrdf.http.server.ProtocolUtil; import org.openrdf.http.server.ServerHTTPException; import org.openrdf.http.server.repository.RepositoryInterceptor; import org.openrdf.model.Resource; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.model.ValueFactory; import org.openrdf.repository.Repository; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.rio.RDFFormat; import org.openrdf.rio.RDFParseException; import org.openrdf.rio.RDFWriterFactory; import org.openrdf.rio.RDFWriterRegistry; import org.openrdf.rio.Rio; import org.openrdf.rio.UnsupportedRDFormatException; /** * Handles requests for manipulating the statements in a repository. * * @author Herko ter Horst * @author Arjohn Kampman */ public class StatementsController extends AbstractController { private Logger logger = LoggerFactory.getLogger(this.getClass()); public StatementsController() throws ApplicationContextException { setSupportedMethods(new String[] { METHOD_GET, METHOD_POST, "PUT", "DELETE" }); } @Override protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView result; Repository repository = RepositoryInterceptor.getRepository(request); RepositoryConnection repositoryCon = RepositoryInterceptor.getRepositoryConnection(request); String reqMethod = request.getMethod(); if (METHOD_GET.equals(reqMethod)) { logger.info("GET statements"); result = getExportStatementsResult(repository, repositoryCon, request, response); } else if (METHOD_POST.equals(reqMethod)) { String mimeType = HttpServerUtil.getMIMEType(request.getContentType()); if (Protocol.TXN_MIME_TYPE.equals(mimeType)) { logger.info("POST transaction to repository"); result = getTransactionResultResult(repository, repositoryCon, request, response); } else { logger.info("POST data to repository"); result = getAddDataResult(repository, repositoryCon, request, response, false); } } else if ("PUT".equals(reqMethod)) { logger.info("PUT data in repository"); result = getAddDataResult(repository, repositoryCon, request, response, true); } else if ("DELETE".equals(reqMethod)) { logger.info("DELETE data from repository"); result = getDeleteDataResult(repository, repositoryCon, request, response); } else { throw new ClientHTTPException(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Method not allowed: " + reqMethod); } return result; } /** * Get all statements and export them as RDF. * * @return a model and view for exporting the statements. */ private ModelAndView getExportStatementsResult(Repository repository, RepositoryConnection repositoryCon, HttpServletRequest request, HttpServletResponse response) throws ClientHTTPException { ProtocolUtil.logRequestParameters(request); ValueFactory vf = repository.getValueFactory(); Resource subj = ProtocolUtil.parseResourceParam(request, SUBJECT_PARAM_NAME, vf); URI pred = ProtocolUtil.parseURIParam(request, PREDICATE_PARAM_NAME, vf); Value obj = ProtocolUtil.parseValueParam(request, OBJECT_PARAM_NAME, vf); Resource[] contexts = ProtocolUtil.parseContextParam(request, CONTEXT_PARAM_NAME, vf); boolean useInferencing = ProtocolUtil.parseBooleanParam(request, INCLUDE_INFERRED_PARAM_NAME, true); RDFWriterFactory rdfWriterFactory = ProtocolUtil.getAcceptableService(request, response, RDFWriterRegistry.getInstance()); Map<String, Object> model = new HashMap<String, Object>(); model.put(ExportStatementsView.SUBJECT_KEY, subj); model.put(ExportStatementsView.PREDICATE_KEY, pred); model.put(ExportStatementsView.OBJECT_KEY, obj); model.put(ExportStatementsView.CONTEXTS_KEY, contexts); model.put(ExportStatementsView.USE_INFERENCING_KEY, Boolean.valueOf(useInferencing)); model.put(ExportStatementsView.FACTORY_KEY, rdfWriterFactory); return new ModelAndView(ExportStatementsView.getInstance(), model); } /** * Process several actions as a transaction. */ private ModelAndView getTransactionResultResult(Repository repository, RepositoryConnection repositoryCon, HttpServletRequest request, HttpServletResponse response) throws IOException, ClientHTTPException, ServerHTTPException { InputStream in = request.getInputStream(); try { logger.debug("Processing transaction..."); TransactionReader reader = new TransactionReader(); Iterable<? extends TransactionOperation> txn = reader.parse(in); boolean wasAutoCommit = repositoryCon.isAutoCommit(); repositoryCon.setAutoCommit(false); for (TransactionOperation op : txn) { op.execute(repositoryCon); } repositoryCon.setAutoCommit(wasAutoCommit); logger.debug("Transaction processed "); return new ModelAndView(EmptySuccessView.getInstance()); } catch (SAXParseException e) { ErrorInfo errInfo = new ErrorInfo(ErrorType.MALFORMED_DATA, e.getMessage()); throw new ClientHTTPException(SC_BAD_REQUEST, errInfo.toString()); } catch (SAXException e) { throw new ServerHTTPException("Failed to parse transaction data: " + e.getMessage(), e); } catch (IOException e) { throw new ServerHTTPException("Failed to read data: " + e.getMessage(), e); } catch (RepositoryException e) { throw new ServerHTTPException("Repository update error: " + e.getMessage(), e); } } /** * Upload data to the repository. */ private ModelAndView getAddDataResult(Repository repository, RepositoryConnection repositoryCon, HttpServletRequest request, HttpServletResponse response, boolean replaceCurrent) throws IOException, ClientHTTPException, ServerHTTPException { ProtocolUtil.logRequestParameters(request); String mimeType = HttpServerUtil.getMIMEType(request.getContentType()); RDFFormat rdfFormat = Rio.getParserFormatForMIMEType(mimeType); if (rdfFormat == null) { throw new ClientHTTPException(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported MIME type: " + mimeType); } ValueFactory vf = repository.getValueFactory(); Resource[] contexts = ProtocolUtil.parseContextParam(request, CONTEXT_PARAM_NAME, vf); URI baseURI = ProtocolUtil.parseURIParam(request, BASEURI_PARAM_NAME, vf); if (baseURI == null) { baseURI = vf.createURI("foo:bar"); logger.info("no base URI specified, using dummy '{}'", baseURI); } InputStream in = request.getInputStream(); try { boolean wasAutoCommit = repositoryCon.isAutoCommit(); repositoryCon.setAutoCommit(false); if (replaceCurrent) { repositoryCon.clear(contexts); } repositoryCon.add(in, baseURI.toString(), rdfFormat, contexts); repositoryCon.setAutoCommit(wasAutoCommit); return new ModelAndView(EmptySuccessView.getInstance()); } catch (UnsupportedRDFormatException e) { throw new ClientHTTPException(SC_UNSUPPORTED_MEDIA_TYPE, "No RDF parser available for format " + rdfFormat.getName()); } catch (RDFParseException e) { ErrorInfo errInfo = new ErrorInfo(ErrorType.MALFORMED_DATA, e.getMessage()); throw new ClientHTTPException(SC_BAD_REQUEST, errInfo.toString()); } catch (IOException e) { throw new ServerHTTPException("Failed to read data: " + e.getMessage(), e); } catch (RepositoryException e) { throw new ServerHTTPException("Repository update error: " + e.getMessage(), e); } } /** * Delete data from the repository. */ private ModelAndView getDeleteDataResult(Repository repository, RepositoryConnection repositoryCon, HttpServletRequest request, HttpServletResponse response) throws ClientHTTPException, ServerHTTPException { ProtocolUtil.logRequestParameters(request); ValueFactory vf = repository.getValueFactory(); Resource subj = ProtocolUtil.parseResourceParam(request, SUBJECT_PARAM_NAME, vf); URI pred = ProtocolUtil.parseURIParam(request, PREDICATE_PARAM_NAME, vf); Value obj = ProtocolUtil.parseValueParam(request, OBJECT_PARAM_NAME, vf); Resource[] contexts = ProtocolUtil.parseContextParam(request, CONTEXT_PARAM_NAME, vf); try { repositoryCon.remove(subj, pred, obj, contexts); return new ModelAndView(EmptySuccessView.getInstance()); } catch (RepositoryException e) { throw new ServerHTTPException("Repository update error: " + e.getMessage(), e); } } }