Java tutorial
/** * Copyright 2013 Ben Navetta <ben@bennavetta.com> * * 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 com.bennavetta.appsite.webapi; import java.io.IOException; import java.io.InputStream; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.security.MessageDigest; import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.util.AntPathMatcher; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.HandlerMapping; import com.bennavetta.appsite.file.Resource; import com.bennavetta.appsite.file.ResourceService; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.net.MediaType; @Controller @RequestMapping(value = "/content") public class ContentController { private Logger log = LoggerFactory.getLogger(getClass()); @Autowired private ResourceService resources; @RequestMapping(value = "/upload/**", method = RequestMethod.POST) @ResponseStatus(HttpStatus.CREATED) public void uploadFile(HttpServletRequest request) throws IOException { MediaType type = MediaType.parse(request.getContentType()); String path = extractPathFromPattern(request); log.trace("Uploading resource {} with type {}", path, type); Resource old = Resource.get(path); if (old != null) { log.trace("Deleting prior resource: {}", old); old.delete(); } try (InputStream in = request.getInputStream()) { ReadableByteChannel channel = Channels.newChannel(in); resources.create(path, type, channel); } } /** * Delete the list of files specified by the response body (a JSON array) and return a list of * all files that were successfully deleted. * @param paths * @return * @throws ResourceNotFoundException if any of the resources are not found */ @RequestMapping(value = "/delete/**", method = RequestMethod.DELETE) @ResponseBody public String deleteFiles(HttpServletRequest request) throws ResourceNotFoundException { String path = extractPathFromPattern(request); Resource resource = Resource.get(path); if (resource != null) { log.trace("Deleting resource {}", resource); resource.delete(); return resource.getPath(); } else { throw new ResourceNotFoundException(path); } } /** * Given a list of files and their MD5 hashes, calculate which ones are the same on the server, which ones should be uploaded, * and which ones should be deleted on the server (via a delete request). * @param files * @return */ @RequestMapping(value = "/diff", method = RequestMethod.POST) @ResponseBody public DiffResponse calculateDiff(@RequestBody List<DiffFile> files) { List<String> same = new ArrayList<>(); List<String> different = new ArrayList<>(); List<String> extra = new ArrayList<>(); for (DiffFile file : files) { Resource onServer = Resource.get(file.getPath()); if (onServer == null) { different.add(file.getPath()); continue; } if (MessageDigest.isEqual(file.getMd5(), onServer.getMD5())) { same.add(file.getPath()); } else { different.add(file.getPath()); } } //TODO: find a better way of doing this? for (String path : Resource.allResources()) { if (!Iterables.all(files, new PathMatches(path))) //add the path to extras if none of the files match it { extra.add(path); } } DiffResponse response = new DiffResponse(); response.setDifferent(different); response.setExtra(extra); response.setSame(same); return response; } private static class PathMatches implements Predicate<DiffFile> { private String path; public PathMatches(String path) { this.path = path; } @Override public boolean apply(@Nullable DiffFile input) { return path.equals(input.getPath()); } } /** * Given a request that was matched against a @RequestMapping method, extract a trailing path from it. * For example, given a mapping of '/foo/**' and a request of '/foo/bar/baz', this would return 'bar/baz'. * @param request the matched request * @return the final path component * @see http://stackoverflow.com/questions/3686808/spring-3-requestmapping-get-path-value */ public static String extractPathFromPattern(HttpServletRequest request) { String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); AntPathMatcher apm = new AntPathMatcher(); String finalPath = apm.extractPathWithinPattern(bestMatchPattern, path); return finalPath; } }