001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 2007.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.http.server.repository.statements;
007:
008: import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
009: import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
010: import static org.openrdf.http.protocol.Protocol.BASEURI_PARAM_NAME;
011: import static org.openrdf.http.protocol.Protocol.CONTEXT_PARAM_NAME;
012: import static org.openrdf.http.protocol.Protocol.INCLUDE_INFERRED_PARAM_NAME;
013: import static org.openrdf.http.protocol.Protocol.OBJECT_PARAM_NAME;
014: import static org.openrdf.http.protocol.Protocol.PREDICATE_PARAM_NAME;
015: import static org.openrdf.http.protocol.Protocol.SUBJECT_PARAM_NAME;
016:
017: import java.io.IOException;
018: import java.io.InputStream;
019: import java.util.HashMap;
020: import java.util.Map;
021:
022: import javax.servlet.http.HttpServletRequest;
023: import javax.servlet.http.HttpServletResponse;
024:
025: import org.slf4j.Logger;
026: import org.slf4j.LoggerFactory;
027: import org.springframework.context.ApplicationContextException;
028: import org.springframework.web.servlet.ModelAndView;
029: import org.springframework.web.servlet.mvc.AbstractController;
030: import org.xml.sax.SAXException;
031: import org.xml.sax.SAXParseException;
032:
033: import info.aduna.webapp.util.HttpServerUtil;
034: import info.aduna.webapp.views.EmptySuccessView;
035:
036: import org.openrdf.http.protocol.Protocol;
037: import org.openrdf.http.protocol.error.ErrorInfo;
038: import org.openrdf.http.protocol.error.ErrorType;
039: import org.openrdf.http.protocol.transaction.TransactionReader;
040: import org.openrdf.http.protocol.transaction.operations.TransactionOperation;
041: import org.openrdf.http.server.ClientHTTPException;
042: import org.openrdf.http.server.ProtocolUtil;
043: import org.openrdf.http.server.ServerHTTPException;
044: import org.openrdf.http.server.repository.RepositoryInterceptor;
045: import org.openrdf.model.Resource;
046: import org.openrdf.model.URI;
047: import org.openrdf.model.Value;
048: import org.openrdf.model.ValueFactory;
049: import org.openrdf.repository.Repository;
050: import org.openrdf.repository.RepositoryConnection;
051: import org.openrdf.repository.RepositoryException;
052: import org.openrdf.rio.RDFFormat;
053: import org.openrdf.rio.RDFParseException;
054: import org.openrdf.rio.RDFWriterFactory;
055: import org.openrdf.rio.RDFWriterRegistry;
056: import org.openrdf.rio.Rio;
057: import org.openrdf.rio.UnsupportedRDFormatException;
058:
059: /**
060: * Handles requests for manipulating the statements in a repository.
061: *
062: * @author Herko ter Horst
063: * @author Arjohn Kampman
064: */
065: public class StatementsController extends AbstractController {
066:
067: private Logger logger = LoggerFactory.getLogger(this .getClass());
068:
069: public StatementsController() throws ApplicationContextException {
070: setSupportedMethods(new String[] { METHOD_GET, METHOD_POST,
071: "PUT", "DELETE" });
072: }
073:
074: @Override
075: protected ModelAndView handleRequestInternal(
076: HttpServletRequest request, HttpServletResponse response)
077: throws Exception {
078: ModelAndView result;
079:
080: Repository repository = RepositoryInterceptor
081: .getRepository(request);
082: RepositoryConnection repositoryCon = RepositoryInterceptor
083: .getRepositoryConnection(request);
084:
085: String reqMethod = request.getMethod();
086:
087: if (METHOD_GET.equals(reqMethod)) {
088: logger.info("GET statements");
089: result = getExportStatementsResult(repository,
090: repositoryCon, request, response);
091: } else if (METHOD_POST.equals(reqMethod)) {
092: String mimeType = HttpServerUtil.getMIMEType(request
093: .getContentType());
094:
095: if (Protocol.TXN_MIME_TYPE.equals(mimeType)) {
096: logger.info("POST transaction to repository");
097: result = getTransactionResultResult(repository,
098: repositoryCon, request, response);
099: } else {
100: logger.info("POST data to repository");
101: result = getAddDataResult(repository, repositoryCon,
102: request, response, false);
103: }
104: } else if ("PUT".equals(reqMethod)) {
105: logger.info("PUT data in repository");
106: result = getAddDataResult(repository, repositoryCon,
107: request, response, true);
108: } else if ("DELETE".equals(reqMethod)) {
109: logger.info("DELETE data from repository");
110: result = getDeleteDataResult(repository, repositoryCon,
111: request, response);
112: } else {
113: throw new ClientHTTPException(
114: HttpServletResponse.SC_METHOD_NOT_ALLOWED,
115: "Method not allowed: " + reqMethod);
116: }
117:
118: return result;
119: }
120:
121: /**
122: * Get all statements and export them as RDF.
123: *
124: * @return a model and view for exporting the statements.
125: */
126: private ModelAndView getExportStatementsResult(
127: Repository repository, RepositoryConnection repositoryCon,
128: HttpServletRequest request, HttpServletResponse response)
129: throws ClientHTTPException {
130: ProtocolUtil.logRequestParameters(request);
131:
132: ValueFactory vf = repository.getValueFactory();
133:
134: Resource subj = ProtocolUtil.parseResourceParam(request,
135: SUBJECT_PARAM_NAME, vf);
136: URI pred = ProtocolUtil.parseURIParam(request,
137: PREDICATE_PARAM_NAME, vf);
138: Value obj = ProtocolUtil.parseValueParam(request,
139: OBJECT_PARAM_NAME, vf);
140: Resource[] contexts = ProtocolUtil.parseContextParam(request,
141: CONTEXT_PARAM_NAME, vf);
142: boolean useInferencing = ProtocolUtil.parseBooleanParam(
143: request, INCLUDE_INFERRED_PARAM_NAME, true);
144:
145: RDFWriterFactory rdfWriterFactory = ProtocolUtil
146: .getAcceptableService(request, response,
147: RDFWriterRegistry.getInstance());
148:
149: Map<String, Object> model = new HashMap<String, Object>();
150: model.put(ExportStatementsView.SUBJECT_KEY, subj);
151: model.put(ExportStatementsView.PREDICATE_KEY, pred);
152: model.put(ExportStatementsView.OBJECT_KEY, obj);
153: model.put(ExportStatementsView.CONTEXTS_KEY, contexts);
154: model.put(ExportStatementsView.USE_INFERENCING_KEY, Boolean
155: .valueOf(useInferencing));
156: model.put(ExportStatementsView.FACTORY_KEY, rdfWriterFactory);
157:
158: return new ModelAndView(ExportStatementsView.getInstance(),
159: model);
160: }
161:
162: /**
163: * Process several actions as a transaction.
164: */
165: private ModelAndView getTransactionResultResult(
166: Repository repository, RepositoryConnection repositoryCon,
167: HttpServletRequest request, HttpServletResponse response)
168: throws IOException, ClientHTTPException,
169: ServerHTTPException {
170: InputStream in = request.getInputStream();
171: try {
172: logger.debug("Processing transaction...");
173:
174: TransactionReader reader = new TransactionReader();
175: Iterable<? extends TransactionOperation> txn = reader
176: .parse(in);
177:
178: boolean wasAutoCommit = repositoryCon.isAutoCommit();
179: repositoryCon.setAutoCommit(false);
180:
181: for (TransactionOperation op : txn) {
182: op.execute(repositoryCon);
183: }
184:
185: repositoryCon.setAutoCommit(wasAutoCommit);
186:
187: logger.debug("Transaction processed ");
188:
189: return new ModelAndView(EmptySuccessView.getInstance());
190: } catch (SAXParseException e) {
191: ErrorInfo errInfo = new ErrorInfo(ErrorType.MALFORMED_DATA,
192: e.getMessage());
193: throw new ClientHTTPException(SC_BAD_REQUEST, errInfo
194: .toString());
195: } catch (SAXException e) {
196: throw new ServerHTTPException(
197: "Failed to parse transaction data: "
198: + e.getMessage(), e);
199: } catch (IOException e) {
200: throw new ServerHTTPException("Failed to read data: "
201: + e.getMessage(), e);
202: } catch (RepositoryException e) {
203: throw new ServerHTTPException("Repository update error: "
204: + e.getMessage(), e);
205: }
206: }
207:
208: /**
209: * Upload data to the repository.
210: */
211: private ModelAndView getAddDataResult(Repository repository,
212: RepositoryConnection repositoryCon,
213: HttpServletRequest request, HttpServletResponse response,
214: boolean replaceCurrent) throws IOException,
215: ClientHTTPException, ServerHTTPException {
216: ProtocolUtil.logRequestParameters(request);
217:
218: String mimeType = HttpServerUtil.getMIMEType(request
219: .getContentType());
220:
221: RDFFormat rdfFormat = Rio.getParserFormatForMIMEType(mimeType);
222: if (rdfFormat == null) {
223: throw new ClientHTTPException(SC_UNSUPPORTED_MEDIA_TYPE,
224: "Unsupported MIME type: " + mimeType);
225: }
226:
227: ValueFactory vf = repository.getValueFactory();
228:
229: Resource[] contexts = ProtocolUtil.parseContextParam(request,
230: CONTEXT_PARAM_NAME, vf);
231: URI baseURI = ProtocolUtil.parseURIParam(request,
232: BASEURI_PARAM_NAME, vf);
233:
234: if (baseURI == null) {
235: baseURI = vf.createURI("foo:bar");
236: logger.info("no base URI specified, using dummy '{}'",
237: baseURI);
238: }
239:
240: InputStream in = request.getInputStream();
241: try {
242: boolean wasAutoCommit = repositoryCon.isAutoCommit();
243: repositoryCon.setAutoCommit(false);
244:
245: if (replaceCurrent) {
246: repositoryCon.clear(contexts);
247: }
248: repositoryCon.add(in, baseURI.toString(), rdfFormat,
249: contexts);
250:
251: repositoryCon.setAutoCommit(wasAutoCommit);
252:
253: return new ModelAndView(EmptySuccessView.getInstance());
254: } catch (UnsupportedRDFormatException e) {
255: throw new ClientHTTPException(SC_UNSUPPORTED_MEDIA_TYPE,
256: "No RDF parser available for format "
257: + rdfFormat.getName());
258: } catch (RDFParseException e) {
259: ErrorInfo errInfo = new ErrorInfo(ErrorType.MALFORMED_DATA,
260: e.getMessage());
261: throw new ClientHTTPException(SC_BAD_REQUEST, errInfo
262: .toString());
263: } catch (IOException e) {
264: throw new ServerHTTPException("Failed to read data: "
265: + e.getMessage(), e);
266: } catch (RepositoryException e) {
267: throw new ServerHTTPException("Repository update error: "
268: + e.getMessage(), e);
269: }
270: }
271:
272: /**
273: * Delete data from the repository.
274: */
275: private ModelAndView getDeleteDataResult(Repository repository,
276: RepositoryConnection repositoryCon,
277: HttpServletRequest request, HttpServletResponse response)
278: throws ClientHTTPException, ServerHTTPException {
279: ProtocolUtil.logRequestParameters(request);
280:
281: ValueFactory vf = repository.getValueFactory();
282:
283: Resource subj = ProtocolUtil.parseResourceParam(request,
284: SUBJECT_PARAM_NAME, vf);
285: URI pred = ProtocolUtil.parseURIParam(request,
286: PREDICATE_PARAM_NAME, vf);
287: Value obj = ProtocolUtil.parseValueParam(request,
288: OBJECT_PARAM_NAME, vf);
289: Resource[] contexts = ProtocolUtil.parseContextParam(request,
290: CONTEXT_PARAM_NAME, vf);
291:
292: try {
293: repositoryCon.remove(subj, pred, obj, contexts);
294:
295: return new ModelAndView(EmptySuccessView.getInstance());
296: } catch (RepositoryException e) {
297: throw new ServerHTTPException("Repository update error: "
298: + e.getMessage(), e);
299: }
300: }
301: }
|