package org.sigmah.server.dao.impl; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * 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/gpl-3.0.html>. * #L% */ import java.io.IOException; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.Query; import javax.persistence.TypedQuery; import org.apache.commons.collections4.CollectionUtils; import org.sigmah.client.util.ClientUtils; import org.sigmah.server.dao.FileDAO; import org.sigmah.server.dao.base.AbstractDAO; import org.sigmah.server.dispatch.impl.UserDispatch; import org.sigmah.server.domain.User; import org.sigmah.server.domain.element.FlexibleElement; import org.sigmah.server.domain.value.File; import org.sigmah.server.domain.value.FileVersion; import org.sigmah.server.domain.value.Value; import org.sigmah.shared.dto.value.FileDTO.LoadingScope; import org.sigmah.shared.dto.value.FileUploadUtils; import org.sigmah.shared.util.ValueResultUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.persist.Transactional; import org.sigmah.server.domain.util.DomainFilters; import static org.sigmah.shared.util.ValueResultUtils.normalizeFileName; /** * {@link FileDAO} implementation. * * @author Denis Colliot (dcolliot@ideia.fr) (v2.0) */ public class FileHibernateDAO extends AbstractDAO<File, Integer> implements FileDAO { /** * Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(UserDispatch.class); /** * {@inheritDoc} */ @Override public List<FileVersion> findVersions(final Collection<Integer> filesIds, LoadingScope loadingScope) { if (CollectionUtils.isEmpty(filesIds)) { throw new IllegalArgumentException("Invalid files ids collection."); } if (loadingScope == null) { loadingScope = LoadingScope.LAST_VERSION; } // NOTE : StringBuilder has been removed here since all the strings used here are constants. final String request; switch (loadingScope) { case ALL_VERSIONS: // Retrieves all versions of each file. request = "SELECT " + " fv " + "FROM " + " File f INNER JOIN f.versions fv " + "WHERE " + " f.id IN (:filesIds)"; break; case LAST_VERSION: // Retrieves only the last version of each file. request = "SELECT " + " fv " + "FROM " + " File f INNER JOIN f.versions fv " + "WHERE " + " f.id IN (:filesIds) " + " AND fv.versionNumber IN (" + " SELECT max(fv2.versionNumber) FROM FileVersion fv2 WHERE fv2.parentFile = f" + " )"; break; case LAST_VERSION_FROM_NOT_DELETED_FILES: // Retrieves only the last version of each file if the file has not been deleted. request = "SELECT " + " fv " + "FROM " + " File f INNER JOIN f.versions fv " + "WHERE " + " f.id IN (:filesIds) " + " AND f.dateDeleted IS NULL " + " AND fv.versionNumber IN (" + " SELECT max(fv2.versionNumber) FROM FileVersion fv2 WHERE fv2.parentFile = f" + " )"; break; default: throw new IllegalArgumentException("Invalid file versions loading mode."); } final TypedQuery<FileVersion> query = em().createQuery(request, FileVersion.class); query.setParameter("filesIds", filesIds); return query.getResultList(); } /** * {@inheritDoc} */ @Override public FileVersion getVersion(final Integer versionId) { final TypedQuery<FileVersion> query = em().createQuery("SELECT fv FROM FileVersion fv WHERE fv.id = :id", FileVersion.class); query.setParameter("id", versionId); return query.getSingleResult(); } /** * {@inheritDoc} */ @Override public FileVersion getLastVersion(Integer fileId) { final TypedQuery<FileVersion> query = em().createQuery("SELECT fv FROM FileVersion fv WHERE fv.parentFile.id = :fileId ORDER BY fv.versionNumber DESC", FileVersion.class); query.setParameter("fileId", fileId); query.setMaxResults(1); return query.getSingleResult(); } /** * {@inheritDoc} */ @Override public Integer saveOrUpdate(final Map<String, String> properties, final String physicalName, final int size) { // Uploaded file's id. Integer id = ClientUtils.asInt(properties.get(FileUploadUtils.DOCUMENT_ID)); // Author id. final int authorId = ClientUtils.asInt(properties.get(FileUploadUtils.DOCUMENT_AUTHOR), 0); try { if (id == null) { // New file (first version). id = saveNewFile(properties, physicalName, size, authorId); } else { // New version. id = saveNewVersion(properties, physicalName, size, id, authorId); } } catch (IOException e) { final String name = properties.get(FileUploadUtils.DOCUMENT_NAME); throw new IllegalStateException("Error while trying to save the file '" + name + "' (id #" + id + ") for author #" + authorId + ".", e); } return id; } /** * Saves a new file. * * @param properties * The properties map of the uploaded file (see {@link FileUploadUtils}). * @param physicalName * The uploaded file content. * @param size * Size of the uploaded file. * @param authorId * The author id. * @return The id of the just saved file. * @throws IOException */ @Transactional protected Integer saveNewFile(Map<String, String> properties, String physicalName, int size, int authorId) throws IOException { final EntityManager em = em(); LOGGER.debug("[saveNewFile] New file."); // -------------------------------------------------------------------- // STEP 1 : saves the file. // -------------------------------------------------------------------- LOGGER.debug("[saveNewFile] Saves the new file."); final File file = new File(); // Gets the details of the name of the file. final String fullName = ValueResultUtils.normalizeFileName(properties.get(FileUploadUtils.DOCUMENT_NAME)); final int index = fullName.indexOf('.'); final String name = index > 0 ? fullName.substring(0, index) : fullName; final String extension = index > 0 && index < fullName.length() ? fullName.substring(index + 1) : null; file.setName(name); // Creates and adds the new version. file.addVersion(createVersion(1, name, extension, authorId, physicalName, size)); em.persist(file); // -------------------------------------------------------------------- // STEP 2 : gets the current value for this list of files. // -------------------------------------------------------------------- // Element id. final int elementId = ClientUtils.asInt(properties.get(FileUploadUtils.DOCUMENT_FLEXIBLE_ELEMENT), 0); // Project id. final int projectId = ClientUtils.asInt(properties.get(FileUploadUtils.DOCUMENT_PROJECT), 0); // Retrieving the current value final TypedQuery<Value> query = em.createQuery("SELECT v FROM Value v WHERE v.containerId = :projectId and v.element.id = :elementId", Value.class); query.setParameter("projectId", projectId); query.setParameter("elementId", elementId); Value currentValue = null; try { currentValue = query.getSingleResult(); } catch (NoResultException nre) { // No current value } // -------------------------------------------------------------------- // STEP 3 : creates or updates the value with the new file id. // -------------------------------------------------------------------- // The value already exists, must update it. if (currentValue != null) { currentValue.setLastModificationAction('U'); // Sets the value (adds a new file id). currentValue.setValue(currentValue.getValue() + ValueResultUtils.DEFAULT_VALUE_SEPARATOR + String.valueOf(file.getId())); } // The value for this list of files doesn't exist already, must // create it. else { currentValue = new Value(); // Creation of the value currentValue.setLastModificationAction('C'); // Parent element final FlexibleElement element = em.find(FlexibleElement.class, elementId); currentValue.setElement(element); // Container currentValue.setContainerId(projectId); // Sets the value (one file id). currentValue.setValue(String.valueOf(file.getId())); } // Modifier final User user = em.find(User.class, authorId); currentValue.setLastModificationUser(user); // Last update date currentValue.setLastModificationDate(new Date()); // Saves or updates the new value. em.merge(currentValue); return file.getId(); } /** * Saves a new file. * * @param properties * The properties map of the uploaded file (see {@link FileUploadUtils}). * @param physicalName * The uploaded file content. * @param size * Size of the uploaded file. * @param id * The file which gets a new version. * @param authorId * The author id. * @return The file id (must be the same as the parameter). * @throws IOException */ @Transactional protected Integer saveNewVersion(Map<String, String> properties, String physicalName, int size, Integer id, int authorId) throws IOException { final EntityManager em = em(); LOGGER.debug("[save] New file version."); // Gets the details of the name of the file. final String fullName = normalizeFileName(properties.get(FileUploadUtils.DOCUMENT_NAME)); final int index = fullName.indexOf('.'); final String name = index > 0 ? fullName.substring(0, index) : fullName; final String extension = index > 0 && index < fullName.length() ? fullName.substring(index + 1) : null; // Creates and adds the new version. final File file = em.find(File.class, Integer.valueOf(id)); if (LOGGER.isDebugEnabled()) { LOGGER.debug("[save] Found file: " + file.getName() + "."); } Integer versionNumber; DomainFilters.disableDeletedFilter(em); final Query query = em.createQuery("SELECT max(fv.versionNumber)+1 AS newVersionNumber FROM FileVersion AS fv WHERE parentFile=:parentFile"); query.setParameter("parentFile", file); versionNumber = (Integer) query.getSingleResult(); if (versionNumber == null) { versionNumber = 0; } final FileVersion version = createVersion(versionNumber, name, extension, authorId, physicalName, size); version.setComments(properties.get(FileUploadUtils.DOCUMENT_COMMENTS)); file.addVersion(version); em.persist(file); return file.getId(); } /** * Creates a file version with the given number and author. * * @param versionNumber * The version number. * @param name * The version name. * @param extension * The version extension. * @param authorId * The author id. * @param content * The version content. * @return The version just created. * @throws IOException */ private static FileVersion createVersion(int versionNumber, String name, String extension, int authorId, String physicalName, int size) throws IOException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("[createVersion] Creates a new file version # + " + versionNumber + "."); } final FileVersion version = new FileVersion(); // Sets attributes. version.setVersionNumber(versionNumber); version.setName(name); version.setExtension(extension); version.setAddedDate(new Date()); version.setSize((long) size); final User user = new User(); user.setId(authorId); version.setAuthor(user); // Saves content. version.setPath(physicalName); return version; } }