Author: echatellier Date: 2014-05-06 17:39:05 +0200 (Tue, 06 May 2014) New Revision: 267 Url: http://forge.codelutin.com/projects/cantharella/repository/revisions/267 Log: fixes #5052: Entity document content are loaded in non lazy mode Added: trunk/cantharella.data/src/main/java/nc/ird/cantharella/data/model/DocumentContent.java trunk/cantharella.data/src/main/sql/cantharella_schema_1.2.sql Modified: trunk/cantharella.data/pom.xml trunk/cantharella.data/src/main/java/nc/ird/cantharella/data/model/Document.java trunk/cantharella.data/src/test/java/nc/ird/cantharella/data/SchemaExporter.java trunk/cantharella.service/src/main/java/nc/ird/cantharella/service/services/impl/DocumentServiceImpl.java trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/config/WebApplicationImpl.java trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/DocumentTooltipColumn.java trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/ManageDocumentPage.java trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/panel/DocumentLinkPanel.java Modified: trunk/cantharella.data/pom.xml =================================================================== --- trunk/cantharella.data/pom.xml 2014-05-05 14:17:00 UTC (rev 266) +++ trunk/cantharella.data/pom.xml 2014-05-06 15:39:05 UTC (rev 267) @@ -118,6 +118,12 @@ <artifactId>hibernate-search-engine</artifactId> </dependency> <dependency> + <groupId>javax.el</groupId> + <artifactId>javax.el-api</artifactId> + <version>2.2.5</version> + <scope>provided</scope> + </dependency> + <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> </dependency> Modified: trunk/cantharella.data/src/main/java/nc/ird/cantharella/data/model/Document.java =================================================================== --- trunk/cantharella.data/src/main/java/nc/ird/cantharella/data/model/Document.java 2014-05-05 14:17:00 UTC (rev 266) +++ trunk/cantharella.data/src/main/java/nc/ird/cantharella/data/model/Document.java 2014-05-06 15:39:05 UTC (rev 267) @@ -25,7 +25,6 @@ import java.util.Date; -import javax.persistence.Basic; import javax.persistence.Embeddable; import javax.persistence.Entity; import javax.persistence.FetchType; @@ -33,6 +32,7 @@ import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.validation.constraints.NotNull; @@ -40,6 +40,8 @@ import nc.ird.cantharella.data.model.utils.AbstractModel; import nc.ird.cantharella.data.validation.LanguageCode; +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; import org.hibernate.annotations.Type; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotEmpty; @@ -109,13 +111,15 @@ private String fileName; /** File data. */ - @Basic(fetch = FetchType.LAZY) @NotNull - private byte[] fileContent; + @OneToOne(orphanRemoval = true, optional = false, fetch = FetchType.LAZY) + @Cascade( { CascadeType.SAVE_UPDATE }) + private DocumentContent fileContent; /** File data thumbnail. */ - @Basic(fetch = FetchType.LAZY) - private byte[] fileContentThumb; + @OneToOne(orphanRemoval = true, optional = true, fetch = FetchType.LAZY) + @Cascade( { CascadeType.SAVE_UPDATE }) + private DocumentContent fileContentThumb; /** File mime type. */ @NotEmpty @@ -325,7 +329,7 @@ * * @return file content */ - public byte[] getFileContent() { + public DocumentContent getFileContent() { return fileContent; } @@ -334,7 +338,7 @@ * * @param fileContent file content */ - public void setFileContent(byte[] fileContent) { + public void setFileContent(DocumentContent fileContent) { this.fileContent = fileContent; } @@ -343,7 +347,7 @@ * * @return file content thumbnail */ - public byte[] getFileContentThumb() { + public DocumentContent getFileContentThumb() { return fileContentThumb; } @@ -352,7 +356,7 @@ * * @param fileContentThumb file content thumbnail */ - public void setFileContentThumb(byte[] fileContentThumb) { + public void setFileContentThumb(DocumentContent fileContentThumb) { this.fileContentThumb = fileContentThumb; } Added: trunk/cantharella.data/src/main/java/nc/ird/cantharella/data/model/DocumentContent.java =================================================================== --- trunk/cantharella.data/src/main/java/nc/ird/cantharella/data/model/DocumentContent.java (rev 0) +++ trunk/cantharella.data/src/main/java/nc/ird/cantharella/data/model/DocumentContent.java 2014-05-06 15:39:05 UTC (rev 267) @@ -0,0 +1,90 @@ +/* + * #%L + * Cantharella :: Data + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2009 - 2014 IRD (Institut de Recherche pour le Developpement) and by respective authors (see below) + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package nc.ird.cantharella.data.model; + +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.validation.constraints.NotNull; + +import nc.ird.cantharella.data.model.utils.AbstractModel; + +/** + * Document entity content. + * + * This entity extract bytea (byte[]) content from document entity for lazy + * loading to not load on file content from database. + * + * @author Eric Chatellier + */ +@Entity +public class DocumentContent extends AbstractModel { + + /** Document content id. */ + @Id + @GeneratedValue + private Integer idDocumentContent; + + /** Binary content. */ + @Basic(fetch = FetchType.LAZY) + @NotNull + private byte[] fileContent; + + /** + * Document content id getter. + * + * @return document id + */ + public Integer getIdDocumentContent() { + return idDocumentContent; + } + + /** + * Document id setter + * + * @param idDocumentContent document id + */ + public void setIdDocumentContent(Integer idDocumentContent) { + this.idDocumentContent = idDocumentContent; + } + + /** + * File content getter. + * + * @return file content + */ + public byte[] getFileContent() { + return fileContent; + } + + /** + * File content setter. + * + * @param fileContent file content + */ + public void setFileContent(byte[] fileContent) { + this.fileContent = fileContent; + } +} Property changes on: trunk/cantharella.data/src/main/java/nc/ird/cantharella/data/model/DocumentContent.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: svn:eol-style + native Added: trunk/cantharella.data/src/main/sql/cantharella_schema_1.2.sql =================================================================== --- trunk/cantharella.data/src/main/sql/cantharella_schema_1.2.sql (rev 0) +++ trunk/cantharella.data/src/main/sql/cantharella_schema_1.2.sql 2014-05-06 15:39:05 UTC (rev 267) @@ -0,0 +1,71 @@ +--- +-- #%L +-- Cantharella :: Data +-- $Id: dev_update_1.1_to_1.2.sql 211 2013-04-30 07:31:44Z acheype $ +-- $HeadURL: http://svn.forge.codelutin.com/svn/cantharella/trunk/cantharella.data/src/ma... $ +-- %% +-- Copyright (C) 2014 IRD (Institut de Recherche pour le Developpement) and by respective authors (see below) +-- %% +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero 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 Affero General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- #L% +--- + +-- insert once connected to cantharella database with the user cantharella +BEGIN; + -- DocumentContent (05/05/2014) + create table DocumentContent ( + idDocumentContent int4 not null, + fileContent bytea not null, + tmpDocument int4, + tmpDocumentThumb int4, + primary key (idDocumentContent) + ); + + -- move all file content to new table + insert into DocumentContent (idDocumentContent, fileContent, tmpDocument) + SELECT nextval('hibernate_sequence'), fileContent, idDocument from document; + -- move all thumbnail content to new table + insert into DocumentContent (idDocumentContent, fileContent, tmpDocumentThumb) + SELECT nextval('hibernate_sequence'), fileContentThumb, idDocument FROM document where fileContentThumb is not NULL; + + -- link documentcontent to document + alter table Document add column fileContent_idDocumentContent int4; + alter table Document add column fileContentThumb_idDocumentContent int4; + + update Document SET fileContent_idDocumentContent = subquery.idDocumentContent + FROM (SELECT idDocumentContent, tmpDocument FROM DocumentContent) AS subquery + WHERE subquery.tmpDocument = Document.idDocument; + update Document SET fileContentThumb_idDocumentContent = subquery.idDocumentContent + FROM (SELECT idDocumentContent, tmpDocumentThumb FROM DocumentContent) AS subquery + WHERE subquery.tmpDocumentThumb = Document.idDocument; + + alter table Document + alter column fileContent_idDocumentContent set not null; + + alter table Document + add constraint FK_3l4n85rsmw88bewfx57o7gg9g + foreign key (fileContent_idDocumentContent) + references DocumentContent; + + alter table Document + add constraint FK_mphot3xs24tg4u5na1bgo1h85 + foreign key (fileContentThumb_idDocumentContent) + references DocumentContent; + + -- clean + alter table Document drop column fileContent; + alter table Document drop column fileContentThumb; + alter table DocumentContent drop column tmpDocument; + alter table DocumentContent drop column tmpDocumentThumb; +COMMIT; \ No newline at end of file Modified: trunk/cantharella.data/src/test/java/nc/ird/cantharella/data/SchemaExporter.java =================================================================== --- trunk/cantharella.data/src/test/java/nc/ird/cantharella/data/SchemaExporter.java 2014-05-05 14:17:00 UTC (rev 266) +++ trunk/cantharella.data/src/test/java/nc/ird/cantharella/data/SchemaExporter.java 2014-05-06 15:39:05 UTC (rev 267) @@ -28,7 +28,7 @@ import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; -import org.hibernate.dialect.PostgreSQL82Dialect; +import org.hibernate.dialect.PostgreSQL9Dialect; import org.hibernate.tool.hbm2ddl.SchemaExport; import org.junit.Test; import org.springframework.orm.hibernate4.LocalSessionFactoryBean; @@ -51,7 +51,7 @@ sessionFactoryBean.setPackagesToScan(new String[] { "nc.ird.cantharella.data.model" }); Properties hibernateProperties = new Properties(); // Hibernate: basic - hibernateProperties.setProperty(Environment.DIALECT, PostgreSQL82Dialect.class.getName()); + hibernateProperties.setProperty(Environment.DIALECT, PostgreSQL9Dialect.class.getName()); hibernateProperties.setProperty(Environment.SHOW_SQL, "false"); sessionFactoryBean.setHibernateProperties(hibernateProperties); sessionFactoryBean.afterPropertiesSet(); Modified: trunk/cantharella.service/src/main/java/nc/ird/cantharella/service/services/impl/DocumentServiceImpl.java =================================================================== --- trunk/cantharella.service/src/main/java/nc/ird/cantharella/service/services/impl/DocumentServiceImpl.java 2014-05-05 14:17:00 UTC (rev 266) +++ trunk/cantharella.service/src/main/java/nc/ird/cantharella/service/services/impl/DocumentServiceImpl.java 2014-05-06 15:39:05 UTC (rev 267) @@ -38,6 +38,7 @@ import nc.ird.cantharella.data.exceptions.DataNotFoundException; import nc.ird.cantharella.data.exceptions.UnexpectedException; import nc.ird.cantharella.data.model.Document; +import nc.ird.cantharella.data.model.DocumentContent; import nc.ird.cantharella.data.model.TypeDocument; import nc.ird.cantharella.data.model.Utilisateur; import nc.ird.cantharella.data.model.Utilisateur.TypeDroit; @@ -180,17 +181,35 @@ // resize original image document.setFileName(clientFileName); document.setFileMimetype(contentType); - document.setFileContent(content); + // original file content + DocumentContent documentContent = document.getFileContent(); + if (documentContent == null) { + documentContent = new DocumentContent(); + document.setFileContent(documentContent); + } + documentContent.setFileContent(content); + // image detection is based on file mimetype + DocumentContent thumbnailContent = document.getFileContentThumb(); if (contentType.startsWith("image/")) { BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(content)); BufferedImage thumbImage = Scalr.resize(originalImage, 100, 100); ByteArrayOutputStream thumbStream = new ByteArrayOutputStream(); ImageIO.write(thumbImage, "png", thumbStream); - document.setFileContentThumb(thumbStream.toByteArray()); + + // thumbnail content + if (thumbnailContent == null) { + thumbnailContent = new DocumentContent(); + } + thumbnailContent.setFileContent(thumbStream.toByteArray()); + } else { + // make sure that changing content type doesn't keep an old + // image thumb + thumbnailContent = null; } + document.setFileContentThumb(thumbnailContent); } catch (IOException ex) { LOG.error("Can't manipulate image", ex); throw new UnexpectedException("Can't manipulate image", ex); Modified: trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/config/WebApplicationImpl.java =================================================================== --- trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/config/WebApplicationImpl.java 2014-05-05 14:17:00 UTC (rev 266) +++ trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/config/WebApplicationImpl.java 2014-05-06 15:39:05 UTC (rev 267) @@ -22,7 +22,6 @@ */ package nc.ird.cantharella.web.config; -import java.io.File; import java.math.BigDecimal; import nc.ird.cantharella.data.config.DataContext; @@ -77,7 +76,6 @@ import nc.ird.cantharella.web.utils.security.AuthStrategy; import org.apache.wicket.ConverterLocator; -import org.apache.wicket.DefaultPageManagerProvider; import org.apache.wicket.IConverterLocator; import org.apache.wicket.RuntimeConfigurationType; import org.apache.wicket.Session; @@ -87,8 +85,6 @@ import org.apache.wicket.core.request.mapper.PackageMapper; import org.apache.wicket.injection.Injector; import org.apache.wicket.javascript.DefaultJavaScriptCompressor; -import org.apache.wicket.pageStore.DiskDataStore; -import org.apache.wicket.pageStore.IDataStore; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.request.Request; import org.apache.wicket.request.Response; @@ -100,7 +96,6 @@ import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.spring.injection.annot.SpringComponentInjector; import org.apache.wicket.util.cookies.CookieUtils; -import org.apache.wicket.util.lang.Bytes; import org.apache.wicket.util.lang.PackageName; import org.apache.wicket.util.time.Duration; import org.slf4j.Logger; @@ -209,14 +204,6 @@ setResourceSettings(); setSecuritySettings(); - setPageManagerProvider(new DefaultPageManagerProvider(this) { - protected IDataStore newDataStore() { - // return new HttpSessionDataStore(getPageManagerContext(), new - // PageNumberEvictionStrategy(10)); - return new DiskDataStore("cantharella", new File("/tmp/test1"), Bytes.megabytes(50)); - } - }); - mountUrls(); } Modified: trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/DocumentTooltipColumn.java =================================================================== --- trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/DocumentTooltipColumn.java 2014-05-05 14:17:00 UTC (rev 266) +++ trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/DocumentTooltipColumn.java 2014-05-06 15:39:05 UTC (rev 267) @@ -22,6 +22,7 @@ */ package nc.ird.cantharella.web.pages.domain.document; +import java.util.Iterator; import java.util.List; import nc.ird.cantharella.data.model.Document; @@ -75,9 +76,13 @@ List<Document> documents = model.getObject().getDocuments(); StringBuilder builder = new StringBuilder(); if (CollectionUtils.isNotEmpty(documents)) { - for (Document document : documents) { + Iterator<Document> itDocument = documents.iterator(); + while (itDocument.hasNext()) { + Document document = itDocument.next(); builder.append(document.getTitre()); - builder.append("\n"); + if (itDocument.hasNext()) { + builder.append("\n"); + } } } Modified: trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/ManageDocumentPage.java =================================================================== --- trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/ManageDocumentPage.java 2014-05-05 14:17:00 UTC (rev 266) +++ trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/ManageDocumentPage.java 2014-05-06 15:39:05 UTC (rev 267) @@ -276,8 +276,8 @@ final FileUpload uploadedFile = fileUploadField.getFileUpload(); if (uploadedFile != null) { try { - documentService.addDocumentContent(document, uploadedFile.getClientFileName(), uploadedFile - .getContentType(), uploadedFile.getBytes()); + documentService.addDocumentContent(document, uploadedFile.getClientFileName(), + uploadedFile.getContentType(), uploadedFile.getBytes()); // if no error validateModel(); @@ -312,6 +312,19 @@ Button updateButton = new SubmittableButton(ACTION_UPDATE, new SubmittableButtonEvents() { @Override public void onProcess() throws DataConstraintException { + Document document = documentModel.getObject(); + final FileUpload uploadedFile = fileUploadField.getFileUpload(); + if (uploadedFile != null) { + try { + documentService.addDocumentContent(document, uploadedFile.getClientFileName(), + uploadedFile.getContentType(), uploadedFile.getBytes()); + + // if no error + validateModel(); + } catch (InvalidFileExtensionException ex) { + error(getString("ManageDocumentPage.Error.notAllowedExtension")); + } + } if (updateWithService) { documentService.updateDocumentAttachable(documentAttachable); } // otherwise, nothing, will be updated by cascade Modified: trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/panel/DocumentLinkPanel.java =================================================================== --- trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/panel/DocumentLinkPanel.java 2014-05-05 14:17:00 UTC (rev 266) +++ trunk/cantharella.web/src/main/java/nc/ird/cantharella/web/pages/domain/document/panel/DocumentLinkPanel.java 2014-05-06 15:39:05 UTC (rev 267) @@ -24,8 +24,8 @@ */ import nc.ird.cantharella.data.model.Document; +import nc.ird.cantharella.data.model.DocumentContent; -import org.apache.commons.lang3.ArrayUtils; import org.apache.wicket.AttributeModifier; import org.apache.wicket.markup.html.WebComponent; import org.apache.wicket.markup.html.image.Image; @@ -43,6 +43,7 @@ /** * Constructor + * * @param id Component id * @param model model */ @@ -50,8 +51,9 @@ super(id); final Document document = model.getObject(); - ResourceLink<Document> link = new ResourceLink<Document>("link", new ByteArrayResource(document - .getFileMimetype(), document.getFileContent(), document.getFileName())); + DocumentContent documentContent = document.getFileContent(); + ResourceLink<Document> link = new ResourceLink<Document>("link", new ByteArrayResource( + document.getFileMimetype(), documentContent.getFileContent(), document.getFileName())); add(link); // les images ne doivent pas être mise en cache car les liens @@ -60,10 +62,12 @@ // supprimées sont retrouvées par leurs lien dans le cache // navigateur WebComponent img; - if (ArrayUtils.isNotEmpty(document.getFileContentThumb())) { + + DocumentContent thumbContent = document.getFileContentThumb(); + if (thumbContent != null) { link.add(new AttributeModifier("class", "colorbox")); link.add(new AttributeModifier("title", document.getFileName())); - img = new Image("image", new ByteArrayResource("image/png", document.getFileContentThumb()) { + img = new Image("image", new ByteArrayResource("image/png", thumbContent.getFileContent()) { @Override protected void configureResponse(ResourceResponse response, Attributes attributes) { response.setCacheDuration(Duration.NONE);