Java tutorial
/** * */ package de.unirostock.sems.cbarchive.web; /* CombineArchiveWeb - a WebInterface to read/create/write/manipulate/... COMBINE archives Copyright (C) 2014 SEMS Group This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import javax.xml.bind.DatatypeConverter; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.http.Header; import org.apache.http.HeaderElement; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import com.fasterxml.jackson.core.JsonProcessingException; import de.binfalse.bflog.LOGGER; import de.unirostock.sems.cbarchive.meta.MetaDataHolder; import de.unirostock.sems.cbarchive.meta.MetaDataObject; import de.unirostock.sems.cbarchive.meta.OmexMetaDataObject; import de.unirostock.sems.cbarchive.meta.omex.OmexDescription; import de.unirostock.sems.cbarchive.meta.omex.VCard; import de.unirostock.sems.cbarchive.web.dataholder.Archive; import de.unirostock.sems.cbarchive.web.dataholder.UserData; import de.unirostock.sems.cbarchive.web.exception.CombineArchiveWebCriticalException; import de.unirostock.sems.cbarchive.web.exception.CombineArchiveWebException; import de.unirostock.sems.cbarchive.web.exception.QuotaException; // TODO: Auto-generated Javadoc /** * The Class Tools. * * @author Martin Scharm */ public class Tools { /** The Constant DATE_FORMATTER. */ public static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss.SSS"); /** * Tries to obtain user instance (workspace), if fails it crates a new one * * @param request the request * @param response the response * @return the user manager * @throws CombineArchiveWebException signals default problems * @throws CombineArchiveWebCriticalException signals critical problems */ public static UserManager doLogin(HttpServletRequest request, HttpServletResponse response) throws CombineArchiveWebException, CombineArchiveWebCriticalException { return doLogin(request, response, true); } /** * Tries to obtain user instance (workspace) <br> * if createNew is true, it also tries to create a new user instance. * * @param request the request * @param response the response * @param createNew try to create new user instance? * @return the user manager * @throws CombineArchiveWebException signals default problems * @throws CombineArchiveWebCriticalException signals critical problems */ public static UserManager doLogin(HttpServletRequest request, HttpServletResponse response, boolean createNew) throws CombineArchiveWebException, CombineArchiveWebCriticalException { // find Cookies // HttpSession session = request.getSession (true); CookieManager cookieManagement = new CookieManager(request, response); // gets the user class UserManager user = null; try { user = getUser(cookieManagement); if (user == null && createNew == true) { user = new UserManager(); storeUserCookies(cookieManagement, user); } } catch (IOException e) { throw new CombineArchiveWebCriticalException("Cannot find and/or obtain working directory", e); } return user; } /** * Gets the user. * * @param cookies the cookies * @return the user * @throws IOException the IO exception */ public static UserManager getUser(CookieManager cookies) throws IOException { Cookie pathCookie = cookies.getCookie(Fields.COOKIE_PATH); if (pathCookie == null) return null; cookies.setCookie(pathCookie); Cookie userInfo = cookies.getCookie(Fields.COOKIE_USER); UserManager user = null; if (WorkspaceManager.getInstance().hasWorkspace(pathCookie.getValue())) { // workspace exists user = new UserManager(pathCookie.getValue()); // parse vCard info if (userInfo != null && !userInfo.getValue().isEmpty()) user.setData(UserData.fromJson(userInfo.getValue())); storeUserCookies(cookies, user); } return user; } /** * Store user cookies. * * @param cookies the cookies * @param user the user */ public static void storeUserCookies(CookieManager cookies, UserManager user) { cookies.setCookie(new Cookie(Fields.COOKIE_PATH, user.getWorkspaceId())); if (user.getData() != null && user.getData().hasInformation()) { UserData userData = user.getData(); try { cookies.setCookie(new Cookie(Fields.COOKIE_USER, userData.toJson())); } catch (JsonProcessingException e) { LOGGER.error(e, "Cannot store cookies, due to json errors"); } } } /** * Extract file name. * * @param part the part * @return the string */ public static final String extractFileName(Part part) { if (part != null) { String header = part.getHeader("content-disposition"); if (header != null) { LOGGER.debug("content-disposition not null: ", header); String[] items = header.split(";"); for (String s : items) { LOGGER.debug("current disposition: ", s); if (s.trim().startsWith("filename")) return s.substring(s.indexOf("=") + 2, s.length() - 1); } } } else LOGGER.debug("file part seems to be null -> cannot extract file name."); return "UploadedFile-" + DATE_FORMATTER.format(new Date()); } /** * Generate hash id. * * @param input the input * @return the hash id */ public static String generateHashId(String input) { try { byte[] hash = MessageDigest.getInstance(Fields.HASH_ALGO).digest(input.getBytes()); return DatatypeConverter.printHexBinary(hash); } catch (NoSuchAlgorithmException e) { // As fallback send the complete String return input; } } /** * Returns false, if a quota is exceeded. Otherwise true * * @param currentValue the current size * @param quota the quota size * @return true, if quota is not exceeded */ public static boolean checkQuota(long currentValue, long quota) { // Quota is set to unlimited if (quota == Fields.QUOTA_UNLIMITED) return true; LOGGER.info(currentValue, " vs ", quota); // check if quota is exceeded if (currentValue >= quota) return false; else return true; } /** * Checks for all quotas required to add/update an file within a CombineArchive. If a quota is exceeded it fails by throwing an QuotaException. * * @param fileSize of the uploaded file * @param archive * @param user * @throws QuotaException */ public static void checkQuotasOrFail(long fileSize, Archive archive, UserManager user) throws QuotaException { // max size for upload if (Fields.QUOTA_UPLOAD_SIZE != Fields.QUOTA_UNLIMITED && fileSize > 0 && Tools.checkQuota(fileSize, Fields.QUOTA_UPLOAD_SIZE) == false) { LOGGER.warn("QUOTA_UPLOAD_SIZE reached in workspace ", user.getWorkspaceId()); throw new QuotaException("QUOTA_UPLOAD_SIZE reached in workspace " + user.getWorkspaceId(), "The fetched file is to big."); } // max files in one archive if (Fields.QUOTA_FILE_LIMIT != Fields.QUOTA_UNLIMITED && Tools.checkQuota(archive.countArchiveEntries() + 1, Fields.QUOTA_FILE_LIMIT) == false) { LOGGER.warn("QUOTA_FILE_LIMIT reached in workspace ", user.getWorkspaceId()); throw new QuotaException("QUOTA_FILE_LIMIT reached in workspace " + user.getWorkspaceId(), "The max amount of files in one archive is reached."); } // max archive size if (Fields.QUOTA_ARCHIVE_SIZE != Fields.QUOTA_UNLIMITED && fileSize > 0 && Tools.checkQuota(user.getWorkspace().getArchiveSize(archive.getId()) + fileSize, Fields.QUOTA_ARCHIVE_SIZE) == false) { LOGGER.warn("QUOTA_ARCHIVE_SIZE reached in workspace ", user.getWorkspaceId()); throw new QuotaException("QUOTA_ARCHIVE_SIZE reached in workspace " + user.getWorkspaceId(), "The maximum size of one archive is reached."); } // max workspace size if (Fields.QUOTA_WORKSPACE_SIZE != Fields.QUOTA_UNLIMITED && fileSize > 0 && Tools.checkQuota(QuotaManager.getInstance().getWorkspaceSize(user.getWorkspace()) + fileSize, Fields.QUOTA_WORKSPACE_SIZE) == false) { LOGGER.warn("QUOTA_WORKSPACE_SIZE reached in workspace ", user.getWorkspaceId()); throw new QuotaException("QUOTA_WORKSPACE_SIZE reached in workspace " + user.getWorkspaceId(), "The maximum size of one workspace is reached."); } // max total size if (Fields.QUOTA_TOTAL_SIZE != Fields.QUOTA_UNLIMITED && fileSize > 0 && Tools.checkQuota(QuotaManager.getInstance().getTotalSize() + fileSize, Fields.QUOTA_TOTAL_SIZE) == false) { LOGGER.warn("QUOTA_TOTAL_SIZE reached in workspace ", user.getWorkspaceId()); throw new QuotaException("QUOTA_TOTAL_SIZE reached in workspace " + user.getWorkspaceId(), "The maximum size is reached."); } } /** * Generates a redirect URI to an archive. * * @param requestContext the request context * @param archiveId the archive id * @return the uri */ public static URI generateArchiveRedirectUri(HttpServletRequest requestContext, String archiveId) { URI newLocation = null; try { if (requestContext != null) { String uri = requestContext.getRequestURL().toString(); uri = uri.substring(0, uri.indexOf("rest/")); LOGGER.info("redirect to ", requestContext.getRequestURL(), " to ", uri); newLocation = new URI(uri + "#archive/" + archiveId); } else newLocation = new URI("../#archive/" + archiveId); } catch (URISyntaxException e) { LOGGER.error(e, "Cannot generate relative URL to main app"); return null; } return newLocation; } /** * Generates Share URI to a workspace. * * @param requestContext the request context * @param workspaceId the workspace id * @return the uri */ public static URI generateWorkspaceRedirectUri(HttpServletRequest requestContext, String workspaceId) { URI newLocation = null; try { if (requestContext != null) { String uri = requestContext.getRequestURL().toString(); uri = uri.substring(0, uri.indexOf("rest/")); LOGGER.info("redirect to ", requestContext.getRequestURL(), " to ", uri); newLocation = new URI(uri + "rest/share/" + workspaceId); } else newLocation = new URI("../rest/share/" + workspaceId); } catch (URISyntaxException e) { LOGGER.error(e, "Cannot generate relative URL to main app"); return null; } return newLocation; } /** * checks whether a filename is blacklisted or not * * @param filename the file name * @return true if filename is blacklisted */ public static boolean isFilenameBlacklisted(String filename) { if (filename == null || filename.isEmpty()) return true; if (Fields.FILENAME_BLACKLIST.contains(FilenameUtils.getName(filename))) return true; return false; } /** * Adds current date as modification and adds the creator, if not done yet, to every Omex description * Also creates new Omex description, if create is set to true and only if necessary. * * @param entity the entity * @param creator the creator * @param create should omex description be created? */ public static void addOmexMetaData(MetaDataHolder entity, VCard creator, boolean create) { addOmexMetaData(entity, creator, null, create); } /** * Adds current date as modification and an additional description, as well as the creator, * if not done yet, to every Omex description. Also creates new Omex description, * if create is set to true and only if necessary * * @param entity the entity * @param creator the creator * @param additionalDescription the additional description * @param create should omex description be created? */ public static void addOmexMetaData(MetaDataHolder entity, VCard creator, String additionalDescription, boolean create) { int added = 0; // save some checks if (creator != null && creator.isEmpty()) creator = null; if (additionalDescription != null && additionalDescription.isEmpty()) additionalDescription = null; // add modified date and own VCard to all omex descriptions for the root element for (MetaDataObject metaObject : entity.getDescriptions()) { if (metaObject instanceof OmexMetaDataObject) { OmexDescription meta = ((OmexMetaDataObject) metaObject).getOmexDescription(); meta.getModified().add(new Date()); if (creator != null && !containsVCard(meta.getCreators(), creator)) // creator is set and not in Omex right now meta.getCreators().add(creator); if (additionalDescription != null) // add additional description to meta (e.g. "derived from") meta.setDescription(meta.getDescription() + "\n" + additionalDescription); added++; } } if (added == 0 && create == true) { // meta was added to non entry -> create OmexDescription meta = new OmexDescription(); meta.getModified().add(meta.getCreated()); if (creator != null) meta.getCreators().add(creator); if (additionalDescription != null) meta.setDescription(additionalDescription); // attach to entity entity.addDescription(new OmexMetaDataObject(meta)); } } /** * Checks if the given VCard exists already in the Collection. * * @param collection the vcard collection * @param vcard the vcard in question * @return true, if collection contains vcard */ public static boolean containsVCard(Collection<VCard> collection, VCard vcard) { if (collection == null) return vcard == null; else if (vcard == null) return false; for (VCard current : collection) if (areVCardEqual(current, vcard)) return true; return false; } /** * Compares 2 VCards and returns true if both are identical in means of String.equal() or if both are null * * @param vcard1 the vcard1 * @param vcard2 the vcard2 * @return true, if vcards are equal */ public static boolean areVCardEqual(VCard vcard1, VCard vcard2) { if (vcard1 == vcard2 || (vcard1 == null && vcard2 == null)) return true; else if (vcard1 == null || vcard2 == null) return false; return (vcard1.getGivenName() == null ? vcard2.getGivenName() == null : vcard1.getGivenName().equals(vcard2.getGivenName())) && (vcard1.getFamilyName() == null ? vcard2.getFamilyName() == null : vcard1.getFamilyName().equals(vcard2.getFamilyName())) && (vcard1.getEmail() == null ? vcard2.getEmail() == null : vcard1.getEmail().equals(vcard2.getEmail())) && (vcard1.getOrganization() == null ? vcard2.getOrganization() == null : vcard1.getOrganization().equals(vcard2.getOrganization())); } /** * Copies an InputStream into an OutputStream and closes all streams afterwards. * Stops at max length. * * @param input the input * @param output the output * @param maxLength the max length * @return the size of the stream copied * @throws IOException the IO exception */ public static long copyStream(InputStream input, OutputStream output, long maxLength) throws IOException { byte[] buffer = new byte[Fields.DEFAULT_BUFFER_SIZE]; long copied = 0; int red = 0; while ((red = input.read(buffer)) != -1) { output.write(buffer, 0, red); copied = copied + red; // abort, if maxLength is reached if (maxLength > 0 && copied > maxLength) break; } input.close(); output.flush(); output.close(); return copied; } /** * writes a input stream entirely into a newly created temp file. * Closes all streams afterwards * * @param tempFileName the temp file name * @param input the input * @return Path to temp file * @throws IOException the IO exception */ public static Path writeStreamToTempFile(String tempFileName, InputStream input) throws IOException { // copy the stream to a temp file Path temp = Files.createTempFile(Fields.TEMP_FILE_PREFIX, tempFileName); // write file to disk OutputStream output = new FileOutputStream(temp.toFile()); IOUtils.copy(input, output); output.flush(); output.close(); input.close(); return temp; } /** * Suggests a filename for the queried file, base on the Content-Disposition Header field or the URL. * * @param request the request * @param response the response * @return A name suggestion or null */ public static String suggestFileNameFromHttpResponse(HttpRequest request, HttpResponse response) { return suggestFileNameFromHttpResponse(request.getRequestLine().getUri(), response); } /** * Suggests a filename for the queried file, base on the Content-Disposition Header field or the URL. * * @param remoteUrl the remote url * @param response the response * @return A name suggestion or null */ public static String suggestFileNameFromHttpResponse(String remoteUrl, HttpResponse response) { // try to evaluate name from url String urlName = FilenameUtils.getName(remoteUrl); // try to extract file name from http header String headerName = suggestFileNameFromHttpResponse(response); return headerName != null && headerName.isEmpty() == false ? headerName : urlName; } /** * Suggests a filename for the queried file, base on the Content-Disposition Header field. * * @param response the response * @return A name suggestion or null */ public static String suggestFileNameFromHttpResponse(HttpResponse response) { // try to extract name from HttpHeader Header dispositionHeader = response.getFirstHeader("Content-Disposition"); if (dispositionHeader != null && dispositionHeader.getValue() != null && dispositionHeader.getValue().isEmpty() == false) { // disposition header is present -> extract name HeaderElement[] fileNameHeaderElements = dispositionHeader.getElements(); if (fileNameHeaderElements.length > 0) { NameValuePair fileNamePair = fileNameHeaderElements[0].getParameterByName("filename"); String suggestedName = fileNamePair != null ? fileNamePair.getValue() : null; LOGGER.debug("Extracted filename ", suggestedName, " from Http header"); return suggestedName; } } return null; } /** * removes all non alpha-numeric symbols from a file name. * * @param fileName the file name * @return the cleaned string */ public static String cleanUpFileName(String fileName) { return fileName.replaceAll("[^A-Za-z0-9\\.]", "_"); } }