Skip to content

Commit ad1f46e

Browse files
committed
GH-4492 split into separate controllers for each action
1 parent be4bcd5 commit ad1f46e

File tree

12 files changed

+1067
-1
lines changed

12 files changed

+1067
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
package org.eclipse.rdf4j.http.server.repository.transaction;
12+
13+
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
14+
import static org.eclipse.rdf4j.http.protocol.Protocol.CONTEXT_PARAM_NAME;
15+
16+
import java.nio.charset.Charset;
17+
import java.nio.charset.StandardCharsets;
18+
import java.util.HashMap;
19+
import java.util.Map;
20+
import java.util.UUID;
21+
22+
import javax.servlet.http.HttpServletRequest;
23+
import javax.servlet.http.HttpServletResponse;
24+
25+
import org.eclipse.rdf4j.common.webapp.views.SimpleResponseView;
26+
import org.eclipse.rdf4j.http.protocol.Protocol;
27+
import org.eclipse.rdf4j.http.server.ClientHTTPException;
28+
import org.eclipse.rdf4j.http.server.ProtocolUtil;
29+
import org.eclipse.rdf4j.http.server.ServerHTTPException;
30+
import org.eclipse.rdf4j.model.Resource;
31+
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
32+
import org.eclipse.rdf4j.rio.RDFFormat;
33+
import org.eclipse.rdf4j.rio.Rio;
34+
import org.slf4j.Logger;
35+
import org.slf4j.LoggerFactory;
36+
import org.springframework.beans.factory.DisposableBean;
37+
import org.springframework.context.ApplicationContextException;
38+
import org.springframework.web.servlet.ModelAndView;
39+
import org.springframework.web.servlet.mvc.AbstractController;
40+
41+
/**
42+
* Handles requests for transaction creation on a repository.
43+
*
44+
* @author Jeen Broekstra
45+
*/
46+
public abstract class AbstractActionController extends AbstractController implements DisposableBean {
47+
48+
private final Logger logger = LoggerFactory.getLogger(this.getClass());
49+
50+
public AbstractActionController() throws ApplicationContextException {
51+
setSupportedMethods(new String[] { METHOD_POST, "PUT" });
52+
}
53+
54+
@Override
55+
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
56+
throws Exception {
57+
UUID transactionId = getTransactionID(request);
58+
logger.debug("transaction id: {}", transactionId);
59+
logger.debug("request content type: {}", request.getContentType());
60+
61+
Transaction transaction = ActiveTransactionRegistry.INSTANCE.getTransaction(transactionId);
62+
63+
if (transaction == null) {
64+
logger.warn("could not find transaction for transaction id {}", transactionId);
65+
throw new ClientHTTPException(SC_BAD_REQUEST,
66+
"unable to find registered transaction for transaction id '" + transactionId + "'");
67+
}
68+
69+
try {
70+
var result = handleAction(request, response, transaction);
71+
if (!(transaction.isClosed() || transaction.isComplete())) {
72+
ActiveTransactionRegistry.INSTANCE.active(transaction);
73+
}
74+
75+
return result;
76+
} catch (Exception e) {
77+
if (e instanceof ClientHTTPException) {
78+
throw (ClientHTTPException) e;
79+
} else if (e instanceof ServerHTTPException) {
80+
throw (ServerHTTPException) e;
81+
} else {
82+
throw new ServerHTTPException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
83+
"Transaction handling error: " + e.getMessage(), e);
84+
}
85+
}
86+
87+
}
88+
89+
/**
90+
* Handle the specific action as part of the supplied {@link Transaction} object.
91+
*
92+
* @param request the request
93+
* @param transaction the transaction on which the action is to be executed
94+
* @return result of the action (may not be null)
95+
*/
96+
protected abstract ModelAndView handleAction(HttpServletRequest request, HttpServletResponse response,
97+
Transaction transaction) throws Exception;
98+
99+
/* private methods */
100+
101+
private UUID getTransactionID(HttpServletRequest request) throws ClientHTTPException {
102+
String pathInfoStr = request.getPathInfo();
103+
104+
UUID txnID = null;
105+
106+
if (pathInfoStr != null && !pathInfoStr.equals("/")) {
107+
String[] pathInfo = pathInfoStr.substring(1).split("/");
108+
// should be of the form: /<Repository>/transactions/<txnID>
109+
if (pathInfo.length == 3) {
110+
try {
111+
txnID = UUID.fromString(pathInfo[2]);
112+
logger.debug("txnID is '{}'", txnID);
113+
} catch (IllegalArgumentException e) {
114+
throw new ClientHTTPException(SC_BAD_REQUEST, "not a valid transaction id: " + pathInfo[2]);
115+
}
116+
} else {
117+
logger.warn("could not determine transaction id from path info {} ", pathInfoStr);
118+
}
119+
}
120+
121+
return txnID;
122+
}
123+
124+
static RDFFormat getRDFFormat(HttpServletRequest request) {
125+
return Rio.getParserFormatForMIMEType(request.getContentType())
126+
.orElseThrow(Rio.unsupportedFormat(request.getContentType()));
127+
}
128+
129+
static String getBaseURI(HttpServletRequest request) {
130+
String baseURI = request.getParameter(Protocol.BASEURI_PARAM_NAME);
131+
return baseURI == null ? "" : baseURI;
132+
}
133+
134+
static Resource[] getContexts(HttpServletRequest request) throws ClientHTTPException {
135+
return ProtocolUtil.parseContextParam(request, CONTEXT_PARAM_NAME, SimpleValueFactory.getInstance());
136+
}
137+
138+
static Charset getCharset(HttpServletRequest request) {
139+
return request.getCharacterEncoding() != null ? Charset.forName(request.getCharacterEncoding())
140+
: StandardCharsets.UTF_8;
141+
}
142+
143+
/**
144+
* A {@link ModelAndView} for a 200 OK response with an empty body
145+
*/
146+
static ModelAndView emptyOkResponse() {
147+
Map<String, Object> model = new HashMap<>();
148+
model.put(SimpleResponseView.SC_KEY, HttpServletResponse.SC_OK);
149+
return new ModelAndView(SimpleResponseView.getInstance(), model);
150+
}
151+
152+
// Comes from disposableBean interface so to be able to stop the ActiveTransactionRegistry scheduler
153+
@Override
154+
public void destroy() throws Exception {
155+
ActiveTransactionRegistry.INSTANCE.destroyScheduler();
156+
}
157+
158+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
12+
package org.eclipse.rdf4j.http.server.repository.transaction;
13+
14+
import javax.servlet.http.HttpServletRequest;
15+
import javax.servlet.http.HttpServletResponse;
16+
17+
import org.eclipse.rdf4j.http.protocol.Protocol;
18+
import org.eclipse.rdf4j.http.server.ProtocolUtil;
19+
import org.eclipse.rdf4j.rio.RDFFormat;
20+
import org.eclipse.rdf4j.rio.Rio;
21+
import org.springframework.web.servlet.ModelAndView;
22+
23+
/**
24+
* @author jeen
25+
*
26+
*/
27+
public class AddController extends AbstractActionController {
28+
29+
@Override
30+
protected ModelAndView handleAction(HttpServletRequest request, HttpServletResponse response,
31+
Transaction transaction) throws Exception {
32+
33+
var baseURI = getBaseURI(request);
34+
var contexts = getContexts(request);
35+
36+
boolean preserveNodeIds = ProtocolUtil.parseBooleanParam(request, Protocol.PRESERVE_BNODE_ID_PARAM_NAME, false);
37+
RDFFormat format = Rio.getParserFormatForMIMEType(request.getContentType())
38+
.orElseThrow(Rio.unsupportedFormat(request.getContentType()));
39+
40+
transaction.add(request.getInputStream(), baseURI, format, preserveNodeIds, contexts);
41+
42+
return emptyOkResponse();
43+
}
44+
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
12+
package org.eclipse.rdf4j.http.server.repository.transaction;
13+
14+
import javax.servlet.http.HttpServletRequest;
15+
import javax.servlet.http.HttpServletResponse;
16+
17+
import org.springframework.web.servlet.ModelAndView;
18+
19+
/**
20+
* @author jeen
21+
*
22+
*/
23+
public class CommitController extends AbstractActionController {
24+
25+
@Override
26+
protected ModelAndView handleAction(HttpServletRequest request, HttpServletResponse response,
27+
Transaction transaction) throws Exception {
28+
29+
transaction.commit();
30+
// If commit fails with an exception, deregister should be skipped so the user
31+
// has a chance to do a proper rollback. See #725.
32+
ActiveTransactionRegistry.INSTANCE.deregister(transaction);
33+
34+
return emptyOkResponse();
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
12+
package org.eclipse.rdf4j.http.server.repository.transaction;
13+
14+
import javax.servlet.http.HttpServletRequest;
15+
import javax.servlet.http.HttpServletResponse;
16+
17+
import org.springframework.web.servlet.ModelAndView;
18+
19+
/**
20+
* @author jeen
21+
*
22+
*/
23+
public class DeleteController extends AbstractActionController {
24+
25+
@Override
26+
protected ModelAndView handleAction(HttpServletRequest request, HttpServletResponse response,
27+
Transaction transaction) throws Exception {
28+
29+
var baseURI = getBaseURI(request);
30+
var format = getRDFFormat(request);
31+
32+
transaction.delete(format, request.getInputStream(), baseURI);
33+
34+
return emptyOkResponse();
35+
}
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
12+
package org.eclipse.rdf4j.http.server.repository.transaction;
13+
14+
import static org.eclipse.rdf4j.http.protocol.Protocol.CONTEXT_PARAM_NAME;
15+
import static org.eclipse.rdf4j.http.protocol.Protocol.INCLUDE_INFERRED_PARAM_NAME;
16+
import static org.eclipse.rdf4j.http.protocol.Protocol.OBJECT_PARAM_NAME;
17+
import static org.eclipse.rdf4j.http.protocol.Protocol.PREDICATE_PARAM_NAME;
18+
import static org.eclipse.rdf4j.http.protocol.Protocol.SUBJECT_PARAM_NAME;
19+
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
23+
import javax.servlet.http.HttpServletRequest;
24+
import javax.servlet.http.HttpServletResponse;
25+
26+
import org.eclipse.rdf4j.http.server.ClientHTTPException;
27+
import org.eclipse.rdf4j.http.server.ProtocolUtil;
28+
import org.eclipse.rdf4j.model.IRI;
29+
import org.eclipse.rdf4j.model.Resource;
30+
import org.eclipse.rdf4j.model.Value;
31+
import org.eclipse.rdf4j.model.ValueFactory;
32+
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
33+
import org.eclipse.rdf4j.rio.RDFWriterFactory;
34+
import org.eclipse.rdf4j.rio.RDFWriterRegistry;
35+
import org.slf4j.Logger;
36+
import org.slf4j.LoggerFactory;
37+
import org.springframework.web.servlet.ModelAndView;
38+
39+
/**
40+
* @author jeen
41+
*
42+
*/
43+
public class ExportController extends AbstractActionController {
44+
45+
private final Logger logger = LoggerFactory.getLogger(this.getClass());
46+
47+
@Override
48+
protected ModelAndView handleAction(HttpServletRequest request, HttpServletResponse response,
49+
Transaction transaction) throws Exception {
50+
logger.info("{} txn get/export statements request", request.getMethod());
51+
var result = getExportStatementsResult(transaction, request, response);
52+
logger.info("{} txn get/export statements request finished", request.getMethod());
53+
return result;
54+
}
55+
56+
/**
57+
* Get all statements and export them as RDF.
58+
*
59+
* @return a model and view for exporting the statements.
60+
*/
61+
private ModelAndView getExportStatementsResult(Transaction transaction, HttpServletRequest request,
62+
HttpServletResponse response) throws ClientHTTPException {
63+
ProtocolUtil.logRequestParameters(request);
64+
65+
ValueFactory vf = SimpleValueFactory.getInstance();
66+
67+
Resource subj = ProtocolUtil.parseResourceParam(request, SUBJECT_PARAM_NAME, vf);
68+
IRI pred = ProtocolUtil.parseURIParam(request, PREDICATE_PARAM_NAME, vf);
69+
Value obj = ProtocolUtil.parseValueParam(request, OBJECT_PARAM_NAME, vf);
70+
Resource[] contexts = ProtocolUtil.parseContextParam(request, CONTEXT_PARAM_NAME, vf);
71+
boolean useInferencing = ProtocolUtil.parseBooleanParam(request, INCLUDE_INFERRED_PARAM_NAME, true);
72+
73+
RDFWriterFactory rdfWriterFactory = ProtocolUtil.getAcceptableService(request, response,
74+
RDFWriterRegistry.getInstance());
75+
76+
Map<String, Object> model = new HashMap<>();
77+
model.put(TransactionExportStatementsView.SUBJECT_KEY, subj);
78+
model.put(TransactionExportStatementsView.PREDICATE_KEY, pred);
79+
model.put(TransactionExportStatementsView.OBJECT_KEY, obj);
80+
model.put(TransactionExportStatementsView.CONTEXTS_KEY, contexts);
81+
model.put(TransactionExportStatementsView.USE_INFERENCING_KEY, Boolean.valueOf(useInferencing));
82+
model.put(TransactionExportStatementsView.FACTORY_KEY, rdfWriterFactory);
83+
model.put(TransactionExportStatementsView.HEADERS_ONLY, METHOD_HEAD.equals(request.getMethod()));
84+
85+
model.put(TransactionExportStatementsView.TRANSACTION_KEY, transaction);
86+
return new ModelAndView(TransactionExportStatementsView.getInstance(), model);
87+
}
88+
}

0 commit comments

Comments
 (0)