/*
* Copyright 2012
* Ubiquitous Knowledge Processing (UKP) Lab and FG Language Technology
* Technische Universität Darmstadt
*
* Licensed 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 de.tudarmstadt.ukp.clarin.webanno.webapp.remoteapi;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.annotation.Resource;
import javax.persistence.NoResultException;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.uima.UIMAException;
import org.apache.wicket.ajax.json.JSONArray;
import org.apache.wicket.ajax.json.JSONObject;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import de.tudarmstadt.ukp.clarin.webanno.api.AnnotationSchemaService;
import de.tudarmstadt.ukp.clarin.webanno.api.DocumentService;
import de.tudarmstadt.ukp.clarin.webanno.api.ImportExportService;
import de.tudarmstadt.ukp.clarin.webanno.api.ProjectService;
import de.tudarmstadt.ukp.clarin.webanno.api.SecurityUtil;
import de.tudarmstadt.ukp.clarin.webanno.api.WebAnnoConst;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationDocument;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationDocumentState;
import de.tudarmstadt.ukp.clarin.webanno.model.Mode;
import de.tudarmstadt.ukp.clarin.webanno.model.PermissionLevel;
import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.clarin.webanno.model.ProjectPermission;
import de.tudarmstadt.ukp.clarin.webanno.model.SourceDocument;
import de.tudarmstadt.ukp.clarin.webanno.model.SourceDocumentState;
import de.tudarmstadt.ukp.clarin.webanno.security.UserDao;
import de.tudarmstadt.ukp.clarin.webanno.security.model.User;
import de.tudarmstadt.ukp.clarin.webanno.support.ZipUtils;
import de.tudarmstadt.ukp.clarin.webanno.tsv.WebannoTsv3Writer;
/**
* Expose some functions of WebAnno via a RESTful remote API.
*/
@RequestMapping("/api/v1")
@Controller
public class RemoteApiController
{
private static final String META_INF = "META-INF/";
private static final String PROJECTS = "projects";
private static final String DOCUMENTS = "sourcedocs";
private static final String ANNOTATIONS = "annos";
private static final String CURATION = "curationdoc";
private static final String PARAM_PROJECT_ID = "projectId";
private static final String PARAM_DOCUMENT_ID = "documentId";
private static final String PARAM_USERNAME = "username";
private static final String PARAM_FILE = "file";
private static final String PARAM_FILETYPE = "filetype";
private static final String PARAM_NAME = "name";
private static final String PARAM_FORMAT = "format";
private final Logger LOG = LoggerFactory.getLogger(getClass());
@Resource(name = "projectService")
private ProjectService projectRepository;
@Resource(name = "documentService")
private DocumentService documentRepository;
@Resource(name = "importExportService")
private ImportExportService importExportService;
@Resource(name = "annotationService")
private AnnotationSchemaService annotationService;
@Resource(name = "userRepository")
private UserDao userRepository;
/**
* Create a new project.
*
* To test when running in Eclipse, use the Linux "curl" command.
*
* curl -v -F 'file=@test.zip' -F 'name=Test' -F 'filetype=tcf'
* 'http://USERNAME:PASSWORD@localhost:8080/webanno-webapp/api/projects'
*
* @param aName
* the name of the project to create.
* @param aFileType
* the type of the files contained in the ZIP. The possible file types are configured
* in the formats.properties configuration file of WebAnno.
* @param aFile
* a ZIP file containing the project data.
* @throws Exception if there was an error.
*/
@RequestMapping(
value = ("/" + PROJECTS),
method = RequestMethod.POST,
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> projectCreate(
@RequestParam(PARAM_FILE) MultipartFile aFile,
@RequestParam(PARAM_NAME) String aName,
@RequestParam(PARAM_FILETYPE) String aFileType)
throws Exception
{
// Get current user
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.get(username);
if (user == null) {
return ResponseEntity
.badRequest()
.body("User [" + username + "] not found.");
}
// Check for the access
boolean hasAccess =
SecurityUtil.isProjectCreator(projectRepository, user) ||
SecurityUtil.isSuperAdmin(projectRepository, user);
if (!hasAccess) {
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body("User [" + username + "] is not allowed to create projects");
}
// Existing project
if (projectRepository.existsProject(aName)) {
return ResponseEntity
.status(HttpStatus.CONFLICT)
.body("A project with name [" + aName + "] already exists");
}
// Check archive
if (!ZipUtils.isZipStream(aFile.getInputStream())) {
return ResponseEntity.badRequest().body("Invalid ZIP file");
}
// Create the project and initialize tags
LOG.info("Creating project [" + aName + "]");
Project project = new Project();
project.setName(aName);
projectRepository.createProject(project);
annotationService.initializeTypesForProject(project);
// Create permission for the project creator
projectRepository.createProjectPermission(
new ProjectPermission(project, username, PermissionLevel.ADMIN));
projectRepository.createProjectPermission(
new ProjectPermission(project, username, PermissionLevel.CURATOR));
projectRepository.createProjectPermission(
new ProjectPermission(project, username, PermissionLevel.USER));
// Iterate through all the files in the ZIP
// If the current filename does not start with "." and is in the root folder of the ZIP,
// import it as a source document
File zipFile = File.createTempFile(aFile.getOriginalFilename(), ".zip");
aFile.transferTo(zipFile);
ZipFile zip = new ZipFile(zipFile);
for (Enumeration<?> zipEnumerate = zip.entries(); zipEnumerate.hasMoreElements();) {
// Get ZipEntry which is a file or a directory
ZipEntry entry = (ZipEntry) zipEnumerate.nextElement();
// If it is the zip name, ignore it
if ((FilenameUtils.removeExtension(aFile.getOriginalFilename()) + "/").equals(entry
.toString())) {
continue;
}
// IF the current filename is META-INF/webanno/source-meta-data.properties store it as
// project meta data
else if (entry.toString().replace("/", "")
.equals((META_INF + "webanno/source-meta-data.properties").replace("/", ""))) {
InputStream zipStream = zip.getInputStream(entry);
projectRepository.savePropertiesFile(project, zipStream, entry.toString());
}
// File not in the Zip's root folder OR not
// META-INF/webanno/source-meta-data.properties
else if (StringUtils.countMatches(entry.toString(), "/") > 1) {
continue;
}
// If the current filename does not start with "." and is in the root folder of the
// ZIP, import it as a source document
else if (!FilenameUtils.getExtension(entry.toString()).equals("")
&& !FilenameUtils.getName(entry.toString()).equals(".")) {
uploadSourceDocument(zip, entry, project, aFileType);
}
}
LOG.info("Successfully created project [" + aName + "] for user [" + username + "]");
JSONObject projectJSON = new JSONObject();
long pId = projectRepository.getProject(aName).getId();
projectJSON.append(aName, pId);
return ResponseEntity.ok(projectJSON.toString());
}
/**
* List all the projects for a given user with their roles
*
* Test when running in Eclipse: Open your browser, paste following URL with appropriate values
* for username and password:
*
* http://USERNAME:PASSWORD@localhost:8080/webanno-webapp/api/projects
*
* @return JSON string of project where user has access to and respective roles in the project.
* @throws Exception
* if there was an error.
*/
@RequestMapping(
value = ("/" + PROJECTS),
method = RequestMethod.GET)
public ResponseEntity<String> projectList()
throws Exception
{
// Get current user
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.get(username);
if (user == null) {
return ResponseEntity
.badRequest()
.body("User [" + username + "] not found.");
}
// Get projects with permission
List<Project> accessibleProjects = projectRepository.listAccessibleProjects(user);
// Add permissions for each project into JSON array and store in JSON object
JSONObject returnJSONObj = new JSONObject();
for (Project project : accessibleProjects) {
String projectId = Long.toString(project.getId());
List<ProjectPermission> projectPermissions = projectRepository
.listProjectPermissionLevel(user, project);
JSONArray permissionArr = new JSONArray();
JSONObject projectJSON = new JSONObject();
for (ProjectPermission p : projectPermissions) {
permissionArr.put(p.getLevel().getName().toString());
}
projectJSON.put(project.getName(), permissionArr);
returnJSONObj.put(projectId, projectJSON);
}
return ResponseEntity.ok(returnJSONObj.toString());
}
/**
* Delete a project where user has a ADMIN role
*
* To test when running in Eclipse, use the Linux "curl" command.
*
* curl -v -X DELETE
* 'http://USERNAME:PASSOWRD@localhost:8080/webanno-webapp/api/projects/{aProjectId}'
*
* @param aProjectId
* The id of the project.
* @throws Exception
* if there was an error.
*/
@RequestMapping(
value = ("/" + PROJECTS + "/{" + PARAM_PROJECT_ID + "}"),
method = RequestMethod.DELETE)
public ResponseEntity<String> projectDelete(
@PathVariable(PARAM_PROJECT_ID) long aProjectId)
throws Exception
{
// Get current user
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.get(username);
if (user == null) {
return ResponseEntity
.badRequest()
.body("User [" + username + "] not found.");
}
// Get project
Project project;
try {
project = projectRepository.getProject(aProjectId);
}
catch (NoResultException e) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("Project [" + aProjectId + "] not found.");
}
// Check for the access
boolean hasAccess =
SecurityUtil.isProjectAdmin(project, projectRepository, user) ||
SecurityUtil.isSuperAdmin(projectRepository, user);
if (!hasAccess) {
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body("User ["+username+"] is not allowed to access project [" + aProjectId + "]");
}
// remove project is user has admin access
LOG.info("Deleting project [" + aProjectId + "]");
projectRepository.removeProject(project);
LOG.info("Successfully deleted project [" + aProjectId + "]");
return ResponseEntity.ok("Project [" + aProjectId + "] deleted.");
}
/**
* Show source documents in given project where user has ADMIN access
*
* http://USERNAME:PASSWORD@localhost:8080/webanno-webapp/api/projects/{aProjectId}/sourcedocs
*
* @param aProjectId the project ID
* @return JSON with {@link SourceDocument} : id
*/
@RequestMapping(
value = "/" + PROJECTS + "/{" + PARAM_PROJECT_ID + "}/" + DOCUMENTS,
method = RequestMethod.GET)
public ResponseEntity<String> sourceDocumentList(
@PathVariable(PARAM_PROJECT_ID) long aProjectId)
throws Exception
{
// Get current user
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.get(username);
if (user == null) {
return ResponseEntity
.badRequest()
.body("User [" + username + "] not found.");
}
// Get project
Project project;
try {
project = projectRepository.getProject(aProjectId);
}
catch (NoResultException e) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("Project [" + aProjectId + "] not found.");
}
// Check for the access
boolean hasAccess =
SecurityUtil.isProjectAdmin(project, projectRepository, user) ||
SecurityUtil.isSuperAdmin(projectRepository, user);
if (!hasAccess) {
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body("User ["+username+"] is not allowed to access project [" + aProjectId + "]");
}
List<SourceDocument> srcDocumentList = documentRepository.listSourceDocuments(project);
JSONArray sourceDocumentJSONArr = new JSONArray();
for (SourceDocument s : srcDocumentList) {
JSONObject sourceDocumentJSONObj = new JSONObject();
sourceDocumentJSONObj.put("id", s.getId());
sourceDocumentJSONObj.put("name", s.getName());
sourceDocumentJSONObj.put("state", s.getState());
sourceDocumentJSONArr.put(sourceDocumentJSONObj);
}
return ResponseEntity.ok(sourceDocumentJSONArr.toString());
}
/**
* Delete the source document in project if user has an ADMIN permission
*
* To test when running in Eclipse, use the Linux "curl" command.
*
* curl -v -X DELETE
* 'http://USERNAME:PASSWORD@localhost:8080/webanno-webapp/api/projects/{aProjectId}/sourcedocs/
* {aSourceDocumentId}'
*
* @param aProjectId
* {@link Project} ID.
* @param aSourceDocumentId
* {@link SourceDocument} ID.
* @throws Exception
* if there was an error.
*/
@RequestMapping(
value = "/" + PROJECTS + "/{"+PARAM_PROJECT_ID+"}/" + DOCUMENTS + "/{"+PARAM_DOCUMENT_ID+"}",
method = RequestMethod.DELETE)
public ResponseEntity<String> sourceDocumentDelete(
@PathVariable(PARAM_PROJECT_ID) long aProjectId,
@PathVariable(PARAM_DOCUMENT_ID) long aSourceDocumentId)
throws Exception
{
// Get current user
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.get(username);
if (user == null) {
return ResponseEntity
.badRequest()
.body("User [" + username + "] not found.");
}
// Get project
Project project;
try {
project = projectRepository.getProject(aProjectId);
}
catch (NoResultException e) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("Project [" + aProjectId + "] not found.");
}
// Check for the access
boolean hasAccess =
SecurityUtil.isProjectAdmin(project, projectRepository, user) ||
SecurityUtil.isSuperAdmin(projectRepository, user);
if (!hasAccess) {
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body("User ["+username+"] is not allowed to access project [" + aProjectId + "]");
}
LOG.info("Deleting document [" + project.getName() + "]");
// Get source document
SourceDocument srcDocument;
try {
srcDocument = documentRepository.getSourceDocument(aProjectId,
aSourceDocumentId);
}
catch (NoResultException e) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("Source document [" + aSourceDocumentId + "] not found in project [" +
aProjectId + "] not found.");
}
documentRepository.removeSourceDocument(srcDocument);
LOG.info("Successfully deleted project : [" + aProjectId + "]");
return ResponseEntity.ok("Source document [" + aSourceDocumentId + "] in project ["
+ aProjectId + "] deleted.");
}
/**
* Upload a source document into project where user has "ADMIN" role
*
* Test when running in Eclipse, use the Linux "curl" command.
*
* curl -v -F 'file=@test.txt' -F 'filetype=text'
* 'http://USERNAME:PASSWORD@localhost:8080/webanno-webapp/api/projects/{aProjectId}/sourcedocs/
* '
*
* @param aFile
* File for {@link SourceDocument}.
* @param aProjectId
* {@link Project} id.
* @param aFileType
* Extension of the file.
* @return returns JSON string with id to the created source document.
* @throws Exception
* if there was an error.
*/
@RequestMapping(
value = "/" + PROJECTS + "/{"+PARAM_PROJECT_ID+"}/" + DOCUMENTS,
method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> sourceDocumentCreate(
@RequestParam(PARAM_FILE) MultipartFile aFile,
@RequestParam(PARAM_FILETYPE) String aFileType,
@PathVariable(PARAM_PROJECT_ID) long aProjectId)
throws Exception
{
// Get current user
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.get(username);
if (user == null) {
return ResponseEntity
.badRequest()
.body("User [" + username + "] not found.");
}
// Get project
Project project;
try {
project = projectRepository.getProject(aProjectId);
}
catch (NoResultException e) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("Project [" + aProjectId + "] not found.");
}
// Check for the access
boolean hasAccess =
SecurityUtil.isProjectAdmin(project, projectRepository, user) ||
SecurityUtil.isSuperAdmin(projectRepository, user);
if (!hasAccess) {
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body("User ["+username+"] is not allowed to access project [" + aProjectId + "]");
}
// Existing project
if (documentRepository.existsSourceDocument(project, aFile.getOriginalFilename())) {
return ResponseEntity
.status(HttpStatus.CONFLICT)
.body("A document with name [" + aFile.getOriginalFilename() + "] already exists");
}
// Check if file already present or not
try (InputStream is = aFile.getInputStream()) {
uploadSourceDocumentFile(is,aFile.getOriginalFilename(), project, aFileType);
}
// add id of added source document in return json string
JSONObject returnJSON = new JSONObject();
returnJSON.put("id",
documentRepository.getSourceDocument(project, aFile.getOriginalFilename()).getId());
return ResponseEntity.ok(returnJSON.toString());
}
/**
* List annotation documents for a source document in a projects where user is ADMIN
*
* Test when running in Eclipse: Open your browser, paste following URL with appropriate values:
*
* http://USERNAME:PASSWORD@localhost:8080/webanno-webapp/api/projects/{aProjectId}/sourcedocs/{
* aSourceDocumentId}/annos
*
* @param aProjectId
* {@link Project} ID
* @param aSourceDocumentId
* {@link SourceDocument} ID
* @return JSON string of all the annotation documents with their projects.
* @throws Exception
* if there was an error.
*/
@RequestMapping(
value = "/"+PROJECTS+"/{"+PARAM_PROJECT_ID+"}/"+DOCUMENTS+"/{"+PARAM_DOCUMENT_ID+"}/"+ANNOTATIONS,
method = RequestMethod.GET)
public ResponseEntity<String> annotationDocumentList(
@PathVariable(PARAM_PROJECT_ID) long aProjectId,
@PathVariable(PARAM_DOCUMENT_ID) long aSourceDocumentId)
throws Exception
{
// Get current user
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.get(username);
if (user == null) {
return ResponseEntity
.badRequest()
.body("User [" + username + "] not found.");
}
// Get project
Project project;
try {
project = projectRepository.getProject(aProjectId);
}
catch (NoResultException e) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("Project [" + aProjectId + "] not found.");
}
// Check for the access
boolean hasAccess =
SecurityUtil.isProjectAdmin(project, projectRepository, user) ||
SecurityUtil.isSuperAdmin(projectRepository, user);
if (!hasAccess) {
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body("User ["+username+"] is not allowed to access project [" + aProjectId + "]");
}
// Get source document
SourceDocument srcDocument;
try {
srcDocument = documentRepository.getSourceDocument(aProjectId,
aSourceDocumentId);
}
catch (NoResultException e) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("Source document [" + aSourceDocumentId + "] not found in project [" +
aProjectId + "] not found.");
}
List<AnnotationDocument> annList = documentRepository
.listAllAnnotationDocuments(srcDocument);
JSONArray annDocArr = new JSONArray();
for (AnnotationDocument annDoc : annList) {
if(
annDoc.getState().equals(AnnotationDocumentState.FINISHED) ||
annDoc.getState().equals(AnnotationDocumentState.IN_PROGRESS))
{
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd'T'HH:mm:ssZ");
JSONObject annDocObj = new JSONObject();
annDocObj.put("user", annDoc.getUser());
annDocObj.put("state", annDoc.getState().getId());
if (annDoc.getTimestamp() != null) {
annDocObj.put("timestamp", sdf.format(annDoc.getTimestamp()));
}
annDocArr.put(annDocObj);
}
}
JSONObject returnJSON = new JSONObject();
returnJSON.put(srcDocument.getName(),annDocArr);
return ResponseEntity.ok(returnJSON.toString());
}
/**
* Download annotation document with requested parameters
*
* Test when running in Eclipse: Open your browser, paste following URL with appropriate values:
*
* http://USERNAME:PASSWORD@localhost:8080/webanno-webapp/api/projects/{aProjectId}/sourcedocs/{
* aSourceDocumentId}/annos/{annotatorName}?format="text"
*
* @param response
* HttpServletResponse.
* @param aProjectId
* {@link Project} ID.
* @param aSourceDocumentId
* {@link SourceDocument} ID.
* @param annotatorName
* {@link User} name.
* @param format
* Export format.
* @throws Exception
* if there was an error.
*/
@RequestMapping(
value = "/"+PROJECTS+"/{"+PARAM_PROJECT_ID+"}/"+DOCUMENTS+"/{"+PARAM_DOCUMENT_ID+"}/"+ANNOTATIONS+"/{"+PARAM_USERNAME+"}",
method = RequestMethod.GET)
public void annotationDocumentRead(HttpServletResponse response,
@PathVariable(PARAM_PROJECT_ID) long aProjectId,
@PathVariable(PARAM_DOCUMENT_ID) long aSourceDocumentId,
@PathVariable(PARAM_USERNAME) String annotatorName,
@RequestParam(value = PARAM_FORMAT, required = false) String format)
throws Exception
{
// Get current user
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.get(username);
if (user == null) {
response.sendError(HttpStatus.BAD_REQUEST.value(),
"User [" + username + "] not found.");
return;
}
// Get project
Project project;
try {
project = projectRepository.getProject(aProjectId);
}
catch (NoResultException e) {
response.sendError(HttpStatus.NOT_FOUND.value(),
"Project" + aProjectId + "] not found.");
return;
}
// Check for the access
boolean hasAccess =
SecurityUtil.isProjectAdmin(project, projectRepository, user) ||
SecurityUtil.isSuperAdmin(projectRepository, user);
if (!hasAccess) {
response.sendError(HttpStatus.FORBIDDEN.value(),
"User ["+username+"] is not allowed to access project [" + aProjectId + "]");
return;
}
// Get annotator user
User annotator = userRepository.get(annotatorName);
if (annotator == null) {
response.sendError(HttpStatus.BAD_REQUEST.value(),
"Annotator user [" + annotatorName + "] not found.");
return;
}
// Get source document
SourceDocument srcDoc;
try {
srcDoc = documentRepository.getSourceDocument(aProjectId, aSourceDocumentId);
}
catch (NoResultException e) {
response.sendError(HttpStatus.NOT_FOUND.value(),
"Document [" + aSourceDocumentId + "] not found in project [" + aProjectId + "].");
return;
}
// Get annotation document
AnnotationDocument annDoc;
try {
annDoc = documentRepository.getAnnotationDocument(srcDoc, annotator);
}
catch (NoResultException e) {
response.sendError(HttpStatus.NOT_FOUND.value(),
"Annotations for user [" + annotatorName + "] not found on document ["
+ aSourceDocumentId + "] in project [" + aProjectId + "].");
return;
}
String formatId;
if (format == null) {
formatId = srcDoc.getFormat();
}
else {
formatId = format;
}
Class<?> writer = importExportService.getWritableFormats().get(formatId);
if (writer == null) {
String msg = "[" + srcDoc.getName() + "] No writer found for format [" + formatId
+ "] - exporting as WebAnno TSV instead.";
LOG.info(msg);
writer = WebannoTsv3Writer.class;
}
// Temporary file of annotation document
File downloadableFile = importExportService.exportAnnotationDocument(srcDoc,
annotatorName, writer, annDoc.getName(), Mode.ANNOTATION);
try {
// Set mime type
String mimeType = URLConnection
.guessContentTypeFromName(downloadableFile.getName());
if (mimeType == null) {
LOG.info("mimetype is not detectable, will take default");
mimeType = "application/octet-stream";
}
// Set response
response.setContentType(mimeType);
response.setContentType("application/force-download");
response.setHeader("Content-Disposition",
String.format("inline; filename=\"" + downloadableFile.getName() + "\""));
response.setContentLength((int) downloadableFile.length());
InputStream inputStream = new BufferedInputStream(
new FileInputStream(downloadableFile));
FileCopyUtils.copy(inputStream, response.getOutputStream());
}
catch (Exception e) {
LOG.info("Exception occured" + e.getMessage());
}
finally {
if (downloadableFile.exists()) {
downloadableFile.delete();
}
}
}
/**
* Download curated document with requested parameters
*
* Test when running in Eclipse: Open your browser, paste following URL with appropriate values:
*
* http://USERNAME:PASSWORD@localhost:8080/webanno-webapp/api/projects/{aProjectId}/curationdoc/
* {aSourceDocumentId}?format=xmi
*
* @param response
* HttpServletResponse.
* @param aProjectId
* {@link Project} ID.
* @param aSourceDocumentId
* {@link SourceDocument} ID.
* @param format
* Export format.
* @throws Exception
* if there was an error.
*/
@RequestMapping(
value = "/"+PROJECTS+"/{"+PARAM_PROJECT_ID+"}/"+CURATION+"/{"+PARAM_DOCUMENT_ID+"}",
method = RequestMethod.GET)
public void curationDocumentRead(HttpServletResponse response,
@PathVariable(PARAM_PROJECT_ID) long aProjectId,
@PathVariable(PARAM_DOCUMENT_ID) long aSourceDocumentId,
@RequestParam(value = PARAM_FORMAT, required = false) String format)
throws Exception
{
// Get current user
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.get(username);
if (user == null) {
response.sendError(
HttpStatus.BAD_REQUEST.value(),
"User [" + username + "] not found.");
return;
}
// Get project
Project project;
try {
project = projectRepository.getProject(aProjectId);
}
catch (NoResultException e) {
response.sendError(
HttpStatus.NOT_FOUND.value(),
"Project" + aProjectId + "] not found.");
return;
}
// Check for the access
boolean hasAccess =
SecurityUtil.isProjectAdmin(project, projectRepository, user) ||
SecurityUtil.isSuperAdmin(projectRepository, user);
if (!hasAccess) {
response.sendError(
HttpStatus.FORBIDDEN.value(),
"User ["+username+"] is not allowed to access project [" + aProjectId + "]");
return;
}
// Get source document
SourceDocument srcDocument;
try {
srcDocument = documentRepository.getSourceDocument(aProjectId,
aSourceDocumentId);
}
catch (NoResultException e) {
response.sendError(
HttpStatus.NOT_FOUND.value(),
"Source document [" + aSourceDocumentId + "] not found in project [" +
aProjectId + "] not found.");
return;
}
// Check if curation is complete
if (!SourceDocumentState.CURATION_FINISHED.equals(srcDocument.getState())) {
response.sendError(
HttpStatus.NOT_FOUND.value(),
"Curation of source document [" + aSourceDocumentId + "] not yet complete.");
return;
}
String formatId;
if (format == null) {
formatId = srcDocument.getFormat();
}
else {
formatId = format;
}
Class<?> writer = importExportService.getWritableFormats().get(formatId);
if (writer == null) {
LOG.info("[" + srcDocument.getName() + "] No writer found for format ["
+ formatId + "] - exporting as WebAnno TSV instead.");
writer = WebannoTsv3Writer.class;
}
// Temporary file of annotation document
File downloadableFile = importExportService.exportAnnotationDocument(srcDocument,
WebAnnoConst.CURATION_USER, writer, srcDocument.getName(), Mode.CURATION);
try {
// Set mime type
String mimeType = URLConnection
.guessContentTypeFromName(downloadableFile.getName());
if (mimeType == null) {
LOG.info("mimetype is not detectable, will take default");
mimeType = "application/octet-stream";
}
// Set response
response.setContentType(mimeType);
response.setContentType("application/force-download");
response.setHeader("Content-Disposition", String
.format("inline; filename=\"" + downloadableFile.getName() + "\""));
response.setContentLength((int) downloadableFile.length());
InputStream inputStream = new BufferedInputStream(
new FileInputStream(downloadableFile));
FileCopyUtils.copy(inputStream, response.getOutputStream());
}
catch (Exception e) {
LOG.info("Exception occured" + e.getMessage());
}
finally {
if (downloadableFile.exists()) {
downloadableFile.delete();
}
}
}
private void uploadSourceDocumentFile(InputStream is, String name, Project project,
String aFileType)
throws IOException, UIMAException
{
// Check if it is a property file
if (name.equals("source-meta-data.properties")) {
projectRepository.savePropertiesFile(project, is, name);
}
else {
SourceDocument document = new SourceDocument();
document.setName(name);
document.setProject(project);
document.setFormat(aFileType);
// Meta data entry to the database
documentRepository.createSourceDocument(document);
// Import source document to the project repository folder
documentRepository.uploadSourceDocument(is, document);
}
}
private void uploadSourceDocument(ZipFile zip, ZipEntry entry, Project project,
String aFileType)
throws IOException, UIMAException
{
String fileName = FilenameUtils.getName(entry.toString());
InputStream zipStream = zip.getInputStream(entry);
SourceDocument document = new SourceDocument();
document.setName(fileName);
document.setProject(project);
document.setFormat(aFileType);
// Meta data entry to the database
documentRepository.createSourceDocument(document);
// Import source document to the project repository folder
documentRepository.uploadSourceDocument(zipStream, document);
}
}