All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.milton.http.json.PutJsonResource Maven / Gradle / Ivy

Go to download

Milton Community Edition: Supports DAV level 1 and is available on Apache2 license

There is a newer version: 4.0.3.2215
Show newest version
/*
 * 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 io.milton.http.json;

import io.milton.resource.ReplaceableResource;
import io.milton.common.FileUtils;
import io.milton.common.Utils;
import io.milton.event.EventManager;
import io.milton.event.PutEvent;
import io.milton.http.*;
import io.milton.http.Request.Method;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.ConflictException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.resource.DeletableResource;
import io.milton.resource.PostableResource;
import io.milton.resource.PutableResource;
import io.milton.resource.Resource;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.json.JSON;
import net.sf.json.JSONSerializer;
import net.sf.json.JsonConfig;
import net.sf.json.util.CycleDetectionStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Will use milton's PUT framework to support file uploads using POST and
 * multipart encoding
 * 

* This will save the uploaded files with their given names into the parent * collection resource. *

* If a file already exists with the same name a ConflictException is thrown, * unless you set the _autoname request parameter. If this parameter is present * (ie with any value) the file will be saved with a non-conflicting file name *

* Save file information is returned as JSON in the response content * * @author brad */ public class PutJsonResource extends JsonResource implements PostableResource { private static final Logger log = LoggerFactory.getLogger(PutJsonResource.class); public static final String PARAM_AUTONAME = "_autoname"; public static final String PARAM_NAME = "name"; public static final String PARAM_OVERWRITE = "overwrite"; private final EventManager eventManager; private final PutableResource wrapped; private final String href; private List newFiles; private String errorMessage; public PutJsonResource(PutableResource putableResource, String href, EventManager eventManager) { super(putableResource, Request.Method.PUT.code, null); this.eventManager = eventManager; this.wrapped = putableResource; this.href = href; } @Override public String getContentType(String accepts) { //String s = "application/x-javascript; charset=utf-8"; return "text/plain"; //return "application/json"; } @Override public String processForm(Map parameters, Map files) throws ConflictException, NotAuthorizedException, BadRequestException { log.info("processForm: " + wrapped.getClass()); newFiles = new ArrayList<>(); try { if (parameters.containsKey("content")) { String name = parameters.get("name"); String content = parameters.get("content"); String contentType = parameters.get("Content-Type"); byte[] arr = toArray(content); ByteArrayInputStream in = new ByteArrayInputStream(arr); long length = arr.length; NewFile nf = new NewFile(); nf.setOriginalName(name); nf.setContentType(contentType); nf.setLength(length); newFiles.add(nf); processFile(name, in, length, contentType, nf); } for (FileItem file : files.values()) { NewFile nf = new NewFile(); String ua = HttpManager.request().getUserAgentHeader(); String f = Utils.truncateFileName(ua, file.getName()); nf.setOriginalName(f); nf.setContentType(file.getContentType()); nf.setLength(file.getSize()); newFiles.add(nf); String newName = getName(f, parameters); log.info("creating resource: " + newName + " size: " + file.getSize()); InputStream in = null; try { in = file.getInputStream(); processFile(newName, in, file.getSize(), file.getContentType(), nf); } finally { FileUtils.close(in); } } log.trace("completed all POST processing"); } catch (BadRequestException e) { log.warn("BadRequestException", e); errorMessage = "Bad Request: " + e.getReason(); } catch (NotAuthorizedException e) { log.warn("NotAuthorizedException", e); errorMessage = "Not authorised: " + e.getMessage(); } catch (ConflictException e) { log.warn("ConflictException", e); errorMessage = "Conflict: " + e.getMessage(); } return null; } private byte[] toArray(String s) { try { return s.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } private void processFile(String newName, InputStream in, Long length, String contentType, NewFile nf) throws BadRequestException, NotAuthorizedException, ConflictException { Resource newResource; try { Resource existing = wrapped.child(newName); if (existing != null) { if (existing instanceof ReplaceableResource) { log.trace("existing resource is replaceable, so replace content"); ReplaceableResource rr = (ReplaceableResource) existing; rr.replaceContent(in, null); log.trace("completed POST processing for file. Updated: " + existing.getName()); eventManager.fireEvent(new PutEvent(rr)); newResource = rr; } else { log.trace("existing resource is not replaceable, will be deleted"); if (existing instanceof DeletableResource) { DeletableResource dr = (DeletableResource) existing; dr.delete(); newResource = wrapped.createNew(newName, in, length, contentType); log.trace("completed POST processing for file. Deleted, then created: " + newResource.getName()); eventManager.fireEvent(new PutEvent(newResource)); } else { throw new BadRequestException(existing, "existing resource could not be deleted, is not deletable"); } } } else { newResource = wrapped.createNew(newName, in, length, contentType); if (newResource != null) { log.info("completed POST processing for file. Created: " + newResource.getName()); } else { log.info("completed POST processing for file. null resource returned"); } eventManager.fireEvent(new PutEvent(newResource)); } String newHref = buildNewHref(href, newResource.getName()); nf.setHref(newHref); } catch (IOException ex) { throw new RuntimeException("Exception creating resource", ex); } finally { } } /** * Returns a JSON representation of the newly created hrefs * * @param out * @param range * @param params * @param contentType * @throws IOException * @throws NotAuthorizedException */ @Override public void sendContent(OutputStream out, Range range, Map params, String contentType) throws IOException, NotAuthorizedException { JsonConfig cfg = new JsonConfig(); cfg.setIgnoreTransientFields(true); cfg.setCycleDetectionStrategy(CycleDetectionStrategy.LENIENT); Writer writer = new PrintWriter(out); if (errorMessage != null) { Map map = new HashMap(); map.put("error", errorMessage); JSON json = JSONSerializer.toJSON(map, cfg); json.write(writer); } else { NewFile[] arr; if (newFiles != null) { arr = new NewFile[newFiles.size()]; newFiles.toArray(arr); } else { arr = new NewFile[0]; } JSON json = JSONSerializer.toJSON(arr, cfg); json.write(writer); } writer.flush(); } @Override public Method applicableMethod() { return Method.PUT; } private String getName(String filename, Map parameters) throws ConflictException, NotAuthorizedException, BadRequestException { String initialName = filename; if (parameters.containsKey(PARAM_NAME)) { initialName = parameters.get(PARAM_NAME); } boolean nonBlankName = initialName != null && initialName.trim().length() > 0; boolean autoname = (parameters.get(PARAM_AUTONAME) != null); boolean overwrite = (parameters.get(PARAM_OVERWRITE) != null); if (nonBlankName) { Resource child = wrapped.child(initialName); if (child == null) { log.trace("no existing file with that name"); return initialName; } else { if (overwrite) { log.trace("file exists, and overwrite parameters is set, so allow overwrite: " + initialName); return initialName; } else { if (!autoname) { log.warn("Conflict: Can't create resource with name " + initialName + " because it already exists. To rename automatically use request parameter: " + PARAM_AUTONAME + ", or to overwrite use " + PARAM_OVERWRITE); throw new ConflictException(this); } else { log.trace("file exists and autoname is set, so will find acceptable name"); } } } } else { initialName = getDateAsName("upload"); log.trace("no name given in request"); } return findAcceptableName(initialName); } private String getDateAsName(String base) { Calendar cal = Calendar.getInstance(); return base + "_" + cal.get(Calendar.YEAR) + "-" + cal.get(Calendar.MONTH) + "-" + cal.get(Calendar.DAY_OF_MONTH); } private String findAcceptableName(String initialName) throws ConflictException, NotAuthorizedException, BadRequestException { String baseName = FileUtils.stripExtension(initialName); String ext = FileUtils.getExtension(initialName); return findAcceptableName(baseName, ext, 1); } private String findAcceptableName(String baseName, String ext, int i) throws ConflictException, NotAuthorizedException, BadRequestException { String candidateName = baseName + "_" + i; if (ext != null && ext.length() > 0) { candidateName += "." + ext; } if (wrapped.child(candidateName) == null) { return candidateName; } else { if (i < 100) { return findAcceptableName(baseName, ext, i + 1); } else { log.warn("Too many files with similar names: " + candidateName); throw new ConflictException(this); } } } private String buildNewHref(String href, String newName) { String s = href; int pos = href.lastIndexOf("_DAV"); s = s.substring(0, pos - 1); if (!s.endsWith("/")) { s += "/"; } s += newName; return s; } public static class NewFile { private String href; private String originalName; private long length; private String contentType; public String getHref() { return href; } public void setHref(String href) { this.href = href; } public String getOriginalName() { return originalName; } public void setOriginalName(String originalName) { this.originalName = originalName; } public long getLength() { return length; } public void setLength(long length) { this.length = length; } public String getContentType() { return contentType; } public void setContentType(String contentType) { this.contentType = contentType; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy