blob: b2358e7090bbb09cee790c0a2c901d0b28489f20 [file] [log] [blame]
/*
* 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.jena.fuseki.servlets;
import static java.lang.String.format;
import java.io.PrintWriter;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.jena.fuseki.Fuseki;
import org.apache.jena.fuseki.system.FusekiNetLib;
import org.apache.jena.fuseki.system.Upload;
import org.apache.jena.fuseki.system.UploadDetailsWithName;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.riot.web.HttpNames;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.Quad;
import org.apache.jena.web.HttpSC;
/**
* Upload data into a graph within a dataset. This is {@code fuseki:serviceUpload}.
*
* It is better to use GSP or quads POST with the body being the content.
*
* This class works with general HTML form file upload where the name is somewhere in the form and that may be
* after the data.
*
* Consider this service useful for small files and use GSP or quads POST for large ones.
*/
public class SPARQL_Upload extends ActionService
{
public SPARQL_Upload() {
super();
}
@Override
public void execPost(HttpAction action) {
executeLifecycle(action);
}
@Override
public void execOptions(HttpAction action) {
ServletBase.setCommonHeadersForOptions(action.response);
action.response.setHeader(HttpNames.hAllow, "POST,PATCH,OPTIONS");
ServletOps.success(action);
}
@Override
public void validate(HttpAction action)
{}
@Override
public void execute(HttpAction action) {
// Only allows one file in the upload.
boolean isMultipart = ServletFileUpload.isMultipartContent(action.request);
if ( ! isMultipart )
ServletOps.error(HttpSC.BAD_REQUEST_400 , "Not a file upload");
long count = upload(action, Fuseki.BaseUpload);
ServletOps.success(action);
try {
action.response.setContentType("text/html");
action.response.setStatus(HttpSC.OK_200);
PrintWriter out = action.response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Success</h1>");
out.println("<p>");
out.println("Triples = "+count + "\n");
out.println("<p>");
out.println("</p>");
out.println("<button onclick=\"timeFunction()\">Back to Fuseki</button>");
out.println("</p>");
out.println("<script type=\"text/javascript\">");
out.println("function timeFunction(){");
out.println("window.location.href = \"/fuseki.html\";}");
out.println("</script>");
out.println("</body>");
out.println("</html>");
out.flush();
ServletOps.success(action);
}
catch (Exception ex) { ServletOps.errorOccurred(ex); }
}
// Also used by SPARQL_REST
static public long upload(HttpAction action, String base) {
if ( action.isTransactional() )
return uploadTxn(action, base);
else
return uploadNonTxn(action, base);
}
/**
* Non-transaction - buffer to a temporary graph so that parse errors
* are caught before inserting any data.
*/
private static long uploadNonTxn(HttpAction action, String base) {
UploadDetailsWithName upload = Upload.multipartUploadWorker(action, base);
String graphName = upload.graphName;
DatasetGraph dataTmp = upload.data;
long count = upload.count;
if ( graphName == null )
action.log.info(format("[%d] Upload: %d Quads(s)",action.id, count));
else
action.log.info(format("[%d] Upload: Graph: %s, %d triple(s)", action.id, graphName, count));
Node gn = null;
if ( graphName != null ) {
gn = graphName.equals(HttpNames.graphTargetDefault)
? Quad.defaultGraphNodeGenerated
: NodeFactory.createURI(graphName);
}
action.beginWrite();
try {
if ( gn != null )
FusekiNetLib.addDataInto(dataTmp.getDefaultGraph(), action.getActiveDSG(), gn);
else
FusekiNetLib.addDataInto(dataTmp, action.getActiveDSG());
action.commit();
return count;
} catch (RuntimeException ex) {
// If anything went wrong, try to backout.
try { action.abort(); } catch (Exception ex2) {}
ServletOps.errorOccurred(ex.getMessage());
return -1;
}
finally { action.end(); }
}
/**
* Transactional - we'd like better handle the data and go straight to the destination, with an abort on parse error.
* For an HTML file upload that's not so simple in the general case.
* Use Graph Store protocol for bulk uploads.
*/
private static long uploadTxn(HttpAction action, String base) {
return uploadNonTxn(action, base);
}
}