diff --git a/celements-model/pom.xml b/celements-model/pom.xml index 9a3468011..942fa784b 100644 --- a/celements-model/pom.xml +++ b/celements-model/pom.xml @@ -32,12 +32,13 @@ 4.0.0 celements-model 7.0-SNAPSHOT - Celements XWiki + Celements Model com.celements celements-config-source 7.0-SNAPSHOT + provided com.celements diff --git a/celements-xwiki-core/src/main/java/com/celements/init/CelementsBootstrap.java b/celements-xwiki-core/src/main/java/com/celements/init/CelementsBootstrap.java index 9737991d0..ff99adf41 100644 --- a/celements-xwiki-core/src/main/java/com/celements/init/CelementsBootstrap.java +++ b/celements-xwiki-core/src/main/java/com/celements/init/CelementsBootstrap.java @@ -2,6 +2,7 @@ import static com.celements.common.lambda.LambdaExceptionUtil.*; import static com.celements.execution.XWikiExecutionProp.*; +import static com.celements.logging.LogUtils.*; import static com.celements.spring.context.SpringContextProvider.*; import static com.google.common.base.Preconditions.*; @@ -98,7 +99,7 @@ public void onApplicationEvent(CelementsStartedEvent event) { XWiki xwiki = bootstrapXWiki(); // make XWiki available to all requests via servlet context, see {@link XWikiProvider} xwikiFuture.complete(xwiki); - LOGGER.info("XWiki initialised"); + LOGGER.info("XWiki initialised: {}", defer(xwiki::printConfig)); } catch (Exception exc) { xwikiFuture.completeExceptionally(exc); LOGGER.error("Celements bootstrap failed"); diff --git a/celements-xwiki-core/src/main/java/com/celements/store/StoreFactory.java b/celements-xwiki-core/src/main/java/com/celements/store/StoreFactory.java index 50e478597..d08522448 100644 --- a/celements-xwiki-core/src/main/java/com/celements/store/StoreFactory.java +++ b/celements-xwiki-core/src/main/java/com/celements/store/StoreFactory.java @@ -1,5 +1,7 @@ package com.celements.store; +import static com.celements.spring.context.SpringContextProvider.*; + import java.util.Optional; import org.xwiki.component.manager.ComponentLookupException; @@ -7,8 +9,12 @@ import org.xwiki.configuration.ConfigurationSource; import com.google.common.primitives.Ints; +import com.xpn.xwiki.store.AttachmentContentStore; +import com.xpn.xwiki.store.AttachmentVersioningStore; +import com.xpn.xwiki.store.XWikiAttachmentStoreInterface; import com.xpn.xwiki.store.XWikiRecycleBinStoreInterface; import com.xpn.xwiki.store.XWikiStoreInterface; +import com.xpn.xwiki.store.hibernate.HibernateAttachmentContentStore; import com.xpn.xwiki.web.Utils; public final class StoreFactory { @@ -28,6 +34,30 @@ public static Optional getRecycleBinStore() { return getOptionalStore(XWikiRecycleBinStoreInterface.class, "celements.store.recyclebin"); } + public static XWikiAttachmentStoreInterface getAttachmentStore() { + try { + String hint = getConfigSource().getProperty("celements.store.attachment"); + return getComponentManager().lookup(XWikiAttachmentStoreInterface.class, hint); + } catch (ComponentLookupException exc) { + throw new IllegalStateException("failed looking up attachment store", exc); + } + } + + public static AttachmentContentStore getAttachmentContentStore() { + String beanName = getConfigSource().getProperty("celements.store.attachment.content", + HibernateAttachmentContentStore.STORE_NAME); + return getBeanFactory().getBean(beanName, AttachmentContentStore.class); + } + + public static AttachmentVersioningStore getAttachmentVersioningStore() { + try { + return getComponentManager().lookup(AttachmentVersioningStore.class, + getConfigSource().getProperty("celements.store.attachment.versioning", "default")); + } catch (ComponentLookupException exc) { + throw new IllegalStateException("failed looking up attachment versioning store", exc); + } + } + private static Optional getOptionalStore(Class type, String key) { try { String enabled = getConfigSource().getProperty(key + ".enabled", "false").toLowerCase(); @@ -42,8 +72,8 @@ private static Optional getOptionalStore(Class type, String key) { } } - private static ConfigurationSource getConfigSource() throws ComponentLookupException { - return getComponentManager().lookup(ConfigurationSource.class, "allproperties"); + private static ConfigurationSource getConfigSource() { + return Utils.getComponent(ConfigurationSource.class, "allproperties"); } private static ComponentManager getComponentManager() { diff --git a/celements-xwiki-core/src/main/java/com/celements/store/att/AttachmentContentPolicy.java b/celements-xwiki-core/src/main/java/com/celements/store/att/AttachmentContentPolicy.java new file mode 100644 index 000000000..feb22e5e0 --- /dev/null +++ b/celements-xwiki-core/src/main/java/com/celements/store/att/AttachmentContentPolicy.java @@ -0,0 +1,57 @@ +package com.celements.store.att; + +import javax.inject.Inject; + +import org.springframework.stereotype.Component; + +import com.celements.init.XWikiProvider; +import com.xpn.xwiki.store.hibernate.HibernateAttachmentContentStore; + +/** + * Policy component that decides whether attachment binary content should be embedded into + * XML-based structures produced by XWiki, namely the attachment archive (RCS/XML history) and the + * attachment recycle bin entry (deleted attachment XML snapshot). + *

+ * The decision is based on the currently active {@code AttachmentContentStore} implementation. + * When the legacy Hibernate content store is used, attachment content is expected to be present in + * the serialized XML (RCS archive and recycle bin) because the Hibernate-based implementations + * historically persisted/consumed the bytes from there. + *

+ * When an alternative content storage is used (for example object storage), embedding raw bytes + * into these XML payloads is typically undesirable (size, duplication) and may be incompatible with + * the alternative store's lookup strategy. In that case, content should be excluded and retrieved + * via the configured {@code AttachmentContentStore} instead. + */ +@Component +public class AttachmentContentPolicy { + + private final XWikiProvider xwikiProvider; + + @Inject + public AttachmentContentPolicy(XWikiProvider xwikiProvider) { + this.xwikiProvider = xwikiProvider; + } + + /** + * Determines whether attachment content should be embedded into the attachment archive XML. + */ + public boolean includeInArchive() { + return isHibernateAttachmentContentStore(); + } + + /** + * Determines whether attachment content should be embedded into recycle bin XML. + */ + public boolean includeInRecycleBin() { + return isHibernateAttachmentContentStore(); + } + + private boolean isHibernateAttachmentContentStore() { + return xwikiProvider.get().orElseThrow(IllegalStateException::new) + .getAttachmentStore() + .getContentStore() + .getStoreName() + .equals(HibernateAttachmentContentStore.STORE_NAME); + } + +} diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/XWiki.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/XWiki.java index 058ed4a34..455d875f9 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/XWiki.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/XWiki.java @@ -50,6 +50,7 @@ import java.util.ListIterator; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; import java.util.TimeZone; @@ -184,9 +185,6 @@ public class XWiki implements EventListener { /** The attachment storage (excluding attachment history). */ private XWikiAttachmentStoreInterface attachmentStore; - /** Store for attachment archives. */ - private AttachmentVersioningStore attachmentVersioningStore; - /** Document versioning storage. */ private XWikiVersioningStoreInterface versioningStore; @@ -377,18 +375,14 @@ protected void initXWiki() throws XWikiException { "com.xpn.xwiki.criteria.impl.XWikiCriteriaServiceImpl", context)); LOGGER.trace("initialising AttachmentStore..."); - setAttachmentStore(Utils.getComponent(XWikiAttachmentStoreInterface.class, Param( - "xwiki.store.attachment.hint"))); + setAttachmentStore(StoreFactory.getAttachmentStore()); + getAttachmentStore().getContentStore(); // force init + getAttachmentStore().getVersioningStore(); // force init LOGGER.trace("initialising VersioningStore..."); setVersioningStore(Utils.getComponent(XWikiVersioningStoreInterface.class, Param( "xwiki.store.versioning.hint"))); - LOGGER.trace("initialising AttachmentVersioningStore..."); - setAttachmentVersioningStore(Utils.getComponent(AttachmentVersioningStore.class, - hasAttachmentVersioning(context) ? Param("xwiki.store.attachment.versioning.hint") - : "void")); - LOGGER.trace("initialising RecycleBinStore..."); StoreFactory.getRecycleBinStore().ifPresent(this::setRecycleBinStore); @@ -414,6 +408,23 @@ protected void initXWiki() throws XWikiException { LOGGER.debug("XWiki init done"); } + public String printConfig() { + return "XWiki " + + "[ mainStore=" + getStore().getClass().getName() + + ", notCacheStore=" + getNotCacheStore().getClass().getName() + + ", versioningStore=" + getVersioningStore().getClass().getName() + + ", recycleBinStore=" + Optional.ofNullable(getRecycleBinStore()) + .map(s -> s.getClass().getName()).orElse("none") + + ", attachmentStore=" + getAttachmentStore().getClass().getName() + + ", attachmentContentStore=" + getAttachmentStore().getContentStore().getClass().getName() + + ", attachmentVersioningStore=" + getAttachmentStore().getVersioningStore() + .getClass().getName() + + ", attachmentRecycleBinStore=" + Optional.ofNullable(getAttachmentRecycleBinStore()) + .map(s -> s.getClass().getName()).orElse("none") + + ", renderingEngine=" + getRenderingEngine().getClass().getName() + + "]"; + } + /** * Ensure that mandatory classes (ie classes XWiki needs to work properly) exist and create them * if they don't exist. @@ -782,7 +793,7 @@ public XWikiAttachmentStoreInterface getAttachmentStore() { } public AttachmentVersioningStore getAttachmentVersioningStore() { - return this.attachmentVersioningStore; + return getAttachmentStore().getVersioningStore(); } public XWikiVersioningStoreInterface getVersioningStore() { @@ -2216,10 +2227,6 @@ public void setAttachmentStore(XWikiAttachmentStoreInterface attachmentStore) { this.attachmentStore = attachmentStore; } - public void setAttachmentVersioningStore(AttachmentVersioningStore avStore) { - this.attachmentVersioningStore = avStore; - } - public void setVersioningStore(XWikiVersioningStoreInterface versioningStore) { this.versioningStore = versioningStore; } @@ -5150,7 +5157,7 @@ public boolean hasVersioning(String fullName, XWikiContext context) { @Deprecated public boolean hasAttachmentVersioning(XWikiContext context) { - return ("1".equals(Param("xwiki.store.attachment.versioning", "1"))); + return getAttachmentStore().getVersioningStore().hasVersioning(); } public String getExternalAttachmentURL(String fullName, String filename, XWikiContext context) { diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/Attachment.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/Attachment.java index 262e92dec..0d698df60 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/Attachment.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/Attachment.java @@ -141,7 +141,7 @@ public String getContentAsString(String charset) throws XWikiException { } public Version[] getVersions() throws XWikiException { - attachment.loadArchive(getXWikiContext()); + attachment.loadArchive(); return attachment.getVersions(); } @@ -150,7 +150,7 @@ public Version[] getVersions() throws XWikiException { * @throws XWikiException */ public List getVersionList() throws XWikiException { - attachment.loadArchive(getXWikiContext()); + attachment.loadArchive(); return attachment.getVersionList(); } diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/DeletedAttachment.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/DeletedAttachment.java index 02c005d30..d707e6df2 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/DeletedAttachment.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/DeletedAttachment.java @@ -22,8 +22,8 @@ import java.util.Calendar; import java.util.Date; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.xpn.xwiki.XWikiConfig; import com.xpn.xwiki.XWikiContext; @@ -47,7 +47,7 @@ public class DeletedAttachment extends Api { /** Logging helper object. */ - private static final Log LOG = LogFactory.getLog(DeletedAttachment.class); + private static final Logger LOG = LoggerFactory.getLogger(DeletedAttachment.class); /** The internal object wrapped by this API. */ private final com.xpn.xwiki.doc.DeletedAttachment deletedAttachment; diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/DeletedDocument.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/DeletedDocument.java index 2476d82d6..4cb8500fd 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/DeletedDocument.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/api/DeletedDocument.java @@ -22,8 +22,8 @@ import java.util.Calendar; import java.util.Date; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.xpn.xwiki.XWikiConfig; import com.xpn.xwiki.XWikiContext; @@ -40,7 +40,7 @@ public class DeletedDocument extends Api { /** Logging helper object. */ - private static final Log LOG = LogFactory.getLog(DeletedDocument.class); + private static final Logger LOG = LoggerFactory.getLogger(DeletedDocument.class); /** * The internal object wrapped by this API. diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/DeletedAttachment.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/DeletedAttachment.java index 5bcd77a98..0f9da79c4 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/DeletedAttachment.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/DeletedAttachment.java @@ -73,20 +73,20 @@ protected DeletedAttachment() {} * User which deleted the attachment. * @param deleteDate * Date of delete action. - * @param context - * The current context. Used for determining the encoding. + * @param bWithAttachmentContent + * True to include the attachment content in the recycle bin * @throws XWikiException * If the attachment cannot be exported to XML. */ public DeletedAttachment(XWikiAttachment attachment, String deleter, Date deleteDate, - XWikiContext context) + boolean bWithAttachmentContent) throws XWikiException { this.docId = attachment.getDocId(); this.docName = attachment.getDoc().getFullName(); this.filename = attachment.getFilename(); this.deleter = deleter; this.date = deleteDate; - setAttachment(attachment, context); + setAttachment(attachment, bWithAttachmentContent); } /** @@ -233,9 +233,9 @@ protected void setXml(String xml) { * @throws XWikiException * if an exception occurs during the XML export */ - protected void setAttachment(XWikiAttachment attachment, XWikiContext context) + protected void setAttachment(XWikiAttachment attachment, boolean bWithAttachmentContent) throws XWikiException { - setXml(attachment.toStringXML(true, true, context)); + setXml(attachment.toStringXML(bWithAttachmentContent, true)); } /** diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachment.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachment.java index 4e1ea79ed..566bbb62b 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachment.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachment.java @@ -21,6 +21,10 @@ package com.xpn.xwiki.doc; +import static com.celements.execution.XWikiExecutionProp.*; +import static com.celements.spring.context.SpringContextProvider.*; + +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -28,11 +32,10 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Objects; +import java.util.Optional; import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; @@ -40,17 +43,26 @@ import org.dom4j.dom.DOMElement; import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.suigeneris.jrcs.rcs.Archive; import org.suigeneris.jrcs.rcs.Version; +import org.xwiki.context.Execution; +import org.xwiki.model.reference.AttachmentReference; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.model.reference.WikiReference; +import com.celements.init.XWikiProvider; +import com.google.common.base.Strings; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.internal.xml.DOMXMLWriter; import com.xpn.xwiki.internal.xml.XMLWriter; +import com.xpn.xwiki.store.XWikiAttachmentStoreInterface; public class XWikiAttachment implements Cloneable { - private static final Log LOG = LogFactory.getLog(XWikiAttachment.class); + private static final Logger LOG = LoggerFactory.getLogger(XWikiAttachment.class); private XWikiDocument doc; @@ -169,7 +181,6 @@ public int getContentSize(XWikiContext context) throws XWikiException { if (this.attachment_content == null) { this.doc.loadAttachmentContent(this, context); } - return this.attachment_content.getSize(); } @@ -245,6 +256,25 @@ public void setDoc(XWikiDocument doc) { this.doc = doc; } + public AttachmentReference getAttachmentReference() { + if ((doc != null) && !Strings.isNullOrEmpty(filename)) { + return new AttachmentReference(filename, doc.getDocumentReference()); + } + return null; + } + + public DocumentReference getDocumentReference() { + return Optional.ofNullable(getAttachmentReference()) + .map(AttachmentReference::getDocumentReference) + .orElse(null); + } + + public WikiReference getWikiReference() { + return Optional.ofNullable(getDocumentReference()) + .map(DocumentReference::getWikiReference) + .orElse(null); + } + public Date getDate() { return this.date; } @@ -282,6 +312,12 @@ public void setMetaDataDirty(boolean metaDataDirty) { this.isMetaDataDirty = metaDataDirty; } + @Deprecated + public String toStringXML(boolean bWithAttachmentContent, boolean bWithVersions, + XWikiContext context) throws XWikiException { + return toStringXML(bWithAttachmentContent, bWithVersions); + } + /** * Retrieve an attachment as an XML string. You should prefer * {@link #toXML(com.xpn.xwiki.internal.xml.XMLWriter, boolean, boolean, com.xpn.xwiki.XWikiContext) @@ -297,8 +333,7 @@ public void setMetaDataDirty(boolean metaDataDirty) { * @throws XWikiException * when an error occurs during wiki operations */ - public String toStringXML(boolean bWithAttachmentContent, boolean bWithVersions, - XWikiContext context) + public String toStringXML(boolean bWithAttachmentContent, boolean bWithVersions) throws XWikiException { // This is very bad. baos holds the entire attachment on the heap, then it makes a copy when // toByteArray @@ -307,14 +342,14 @@ public String toStringXML(boolean bWithAttachmentContent, boolean bWithVersions, ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { XMLWriter wr = new XMLWriter(baos, - new OutputFormat("", true, context.getWiki().getEncoding())); + new OutputFormat("", true, getXContext().getWiki().getEncoding())); Document doc = new DOMDocument(); wr.writeDocumentStart(doc); - toXML(wr, bWithAttachmentContent, bWithVersions, context); + toXML(wr, bWithAttachmentContent, bWithVersions, getXContext()); wr.writeDocumentEnd(doc); byte[] array = baos.toByteArray(); baos = null; - return new String(array, context.getWiki().getEncoding()); + return new String(array, getXContext().getWiki().getEncoding()); } catch (IOException e) { e.printStackTrace(); return ""; @@ -388,10 +423,9 @@ public void toXML(XMLWriter wr, boolean bWithAttachmentContent, boolean bWithVer if (bWithAttachmentContent) { el = new DOMElement("content"); // We need to make sure content is loaded - loadContent(context); - XWikiAttachmentContent acontent = getAttachment_content(); + XWikiAttachmentContent acontent = loadContent(); if (acontent != null) { - wr.writeBase64(el, getAttachment_content().getContentInputStream()); + wr.writeBase64(el, acontent.getContentInputStream()); } else { el.addText(""); wr.write(el); @@ -400,7 +434,7 @@ public void toXML(XMLWriter wr, boolean bWithAttachmentContent, boolean bWithVer if (bWithVersions) { // We need to make sure content is loaded - XWikiAttachmentArchive aarchive = loadArchive(context); + XWikiAttachmentArchive aarchive = loadArchive(); if (aarchive != null) { el = new DOMElement("versions"); try { @@ -464,17 +498,14 @@ public void fromXML(Element docel) throws XWikiException { setAuthor(docel.element("author").getText()); setVersion(docel.element("version").getText()); setComment(docel.element("comment").getText()); - String sdate = docel.element("date").getText(); Date date = new Date(Long.parseLong(sdate)); setDate(date); - - Element contentel = docel.element("content"); - if (contentel != null) { - String base64content = contentel.getText(); - byte[] content = Base64.decodeBase64(base64content.getBytes()); - setContent(content); - } + Optional.ofNullable(docel.element("content")) + .map(el -> el.getText().trim()) + .filter(content -> !content.isEmpty()) + .map(content -> Base64.decodeBase64(content.getBytes())) + .ifPresent(this::setContent); Element archiveel = docel.element("versions"); if (archiveel != null) { String archive = archiveel.getText(); @@ -556,7 +587,6 @@ public void setArchive(Archive archive) { this.attachment_archive = new XWikiAttachmentArchive(); this.attachment_archive.setAttachment(this); } - this.attachment_archive.setRCSArchive(archive); } @@ -565,8 +595,9 @@ public void setArchive(String data) throws XWikiException { this.attachment_archive = new XWikiAttachmentArchive(); this.attachment_archive.setAttachment(this); } - - this.attachment_archive.setArchive(data.getBytes()); + if (data != null) { + this.attachment_archive.setArchive(data.getBytes()); + } } public synchronized Version[] getVersions() { @@ -603,16 +634,16 @@ public synchronized List getVersionList() throws XWikiException { * * @param data * a byte array with the binary content of the attachment - * @deprecated use {@link #setContent(java.io.InputStream, int)} instead + * @deprecated use {@link #setContent(java.io.InputStream)} instead */ @Deprecated public void setContent(byte[] data) { - if (this.attachment_content == null) { - this.attachment_content = new XWikiAttachmentContent(); - this.attachment_content.setAttachment(this); + data = (data != null) ? data : new byte[0]; + try (InputStream is = new ByteArrayInputStream(data)) { + setContent(is); + } catch (IOException e) { + throw new RuntimeException("Failed to set attachment content from byte array", e); } - - this.attachment_content.setContent(data); } /** @@ -624,15 +655,11 @@ public void setContent(byte[] data) { * the length in byte to read * @throws IOException * when an error occurs during streaming operation - * @since 2.3M2 + * @deprecated use {@link #setContent(java.io.InputStream)} instead */ + @Deprecated public void setContent(InputStream is, int length) throws IOException { - if (this.attachment_content == null) { - this.attachment_content = new XWikiAttachmentContent(); - this.attachment_content.setAttachment(this); - } - - this.attachment_content.setContent(is, length); + setContent(is); } /** @@ -645,55 +672,45 @@ public void setContent(InputStream is, int length) throws IOException { * @since 2.6M1 */ public void setContent(InputStream is) throws IOException { - if (this.attachment_content == null) { - this.attachment_content = new XWikiAttachmentContent(this); + if (attachment_content == null) { + attachment_content = new XWikiAttachmentContent(); + attachment_content.setAttachment(this); + } + if (is != null) { + attachment_content.setContent(is); } - - this.attachment_content.setContent(is); } - public void loadContent(XWikiContext context) throws XWikiException { + public XWikiAttachmentContent loadContent() throws XWikiException { if (this.attachment_content == null) { - try { - context.getWiki().getAttachmentStore().loadAttachmentContent(this, context, true); - } catch (Exception ex) { - LOG.warn(String.format("Failed to load content for attachment [%s@%s]. " - + "This attachment is broken, please consider re-uploading it. " + "Internal error: %s", - getFilename(), (this.doc != null) ? this.doc.getFullName() : "", - ex.getMessage())); - } + getAttachmentStore().loadAttachmentContent(this, getXContext(), true); } + return this.attachment_content; } - public XWikiAttachmentArchive loadArchive(XWikiContext context) throws XWikiException { + public XWikiAttachmentArchive loadArchive() throws XWikiException { if (this.attachment_archive == null) { - try { - this.attachment_archive = context.getWiki().getAttachmentVersioningStore().loadArchive(this, - context, true); - } catch (Exception ex) { - LOG.warn(String.format("Failed to load archive for attachment [%s@%s]. " - + "This attachment is broken, please consider re-uploading it. " + "Internal error: %s", - getFilename(), (this.doc != null) ? this.doc.getFullName() : "", - ex.getMessage())); - } + this.attachment_archive = getAttachmentStore().getVersioningStore() + .loadArchive(this, true); } - return this.attachment_archive; } - public void updateContentArchive(XWikiContext context) throws XWikiException { + public void updateContentArchive() throws XWikiException { if (this.attachment_content == null) { return; } + loadArchive().updateArchive(); + } - // XWikiAttachmentArchive no longer uses the byte array passed as it's first parameter making it - // redundant. - loadArchive(context).updateArchive(null, context); + public String getMimeType() { + return getMimeType(getXContext()); } + @Deprecated public String getMimeType(XWikiContext context) { // Choose the right content type - String mimetype = context.getEngineContext().getMimeType(getFilename().toLowerCase()); + String mimetype = getXContext().getEngineContext().getMimeType(getFilename().toLowerCase()); if (mimetype != null) { return mimetype; } else { @@ -701,6 +718,11 @@ public String getMimeType(XWikiContext context) { } } + public boolean isImage() { + return isImage(getXContext()); + } + + @Deprecated public boolean isImage(XWikiContext context) { String contenttype = getMimeType(context); if (contenttype.startsWith("image/")) { @@ -712,11 +734,31 @@ public boolean isImage(XWikiContext context) { public XWikiAttachment getAttachmentRevision(String rev, XWikiContext context) throws XWikiException { - if (StringUtils.equals(rev, this.getVersion())) { + if (Objects.equals(rev, this.getVersion())) { return this; } + return loadArchive().getRevision(rev); + } + + @Override + public String toString() { + return "XWikiAttachment[" + + "doc=" + (getDoc() != null ? getDoc().getFullName() : "") + ", " + + "filename=" + getFilename() + ", " + + "version=" + getVersion() + "]"; + } + + private XWikiAttachmentStoreInterface getAttachmentStore() { + return getBeanFactory().getBean(XWikiProvider.class) + .get().orElseThrow(IllegalStateException::new) + .getAttachmentStore(); + } - return loadArchive(context).getRevision(this, rev, context); + private XWikiContext getXContext() { + return getBeanFactory().getBean(Execution.class) + .getContext() + .get(XWIKI_CONTEXT) + .orElseThrow(IllegalStateException::new); } } diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachmentArchive.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachmentArchive.java index 292cb9327..d37f9528e 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachmentArchive.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachmentArchive.java @@ -21,22 +21,27 @@ package com.xpn.xwiki.doc; +import static com.celements.spring.context.SpringContextProvider.*; + import java.io.ByteArrayInputStream; import java.util.Date; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.suigeneris.jrcs.rcs.Archive; import org.suigeneris.jrcs.rcs.Version; import org.suigeneris.jrcs.rcs.impl.Node; import org.suigeneris.jrcs.util.ToString; -import com.xpn.xwiki.XWikiContext; +import com.celements.store.att.AttachmentContentPolicy; import com.xpn.xwiki.XWikiException; public class XWikiAttachmentArchive implements Cloneable { - private static final Log LOG = LogFactory.getLog(XWikiAttachmentArchive.class); + private static final Logger LOG = LoggerFactory.getLogger(XWikiAttachmentArchive.class); private XWikiAttachment attachment; @@ -87,13 +92,6 @@ public void setRCSArchive(Archive archive) { } public byte[] getArchive() throws XWikiException { - return getArchive(null); - } - - public byte[] getArchive(XWikiContext context) throws XWikiException { - if ((this.archive == null) && (context != null)) { - updateArchive(this.attachment.getContent(context), context); - } if (this.archive == null) { return new byte[0]; } else { @@ -129,11 +127,12 @@ public void setArchive(byte[] data) throws XWikiException { * the XWikiContext for the request used to load the correct attachment content from the * database. */ - public void updateArchive(byte[] data, XWikiContext context) throws XWikiException { + public void updateArchive() throws XWikiException { try { this.attachment.incrementVersion(); this.attachment.setDate(new Date()); - String sdata = this.attachment.toStringXML(true, false, context); + boolean includeContent = getAttachmentContentPolicy().includeInArchive(); + String sdata = this.attachment.toStringXML(includeContent, false); Object[] lines = ToString.stringToArray(sdata); if (this.archive != null) { @@ -168,34 +167,37 @@ public Version[] getVersions() { return versions; } - public XWikiAttachment getRevision(XWikiAttachment attachment, String rev, XWikiContext context) - throws XWikiException { - try { - Archive archive = getRCSArchive(); - - if (archive == null) { - return null; - } + public XWikiAttachment getRevision(String rev) throws XWikiException { + if ((rev == null) || (archive == null)) { + return null; + } + return getRevision(archive.getRevisionVersion(rev)); + } - Version v = archive.getRevisionVersion(rev); - if (v == null) { + public XWikiAttachment getRevision(Version v) throws XWikiException { + try { + if (Objects.equals(v, attachment.getRCSVersion())) { + return attachment; + } else if ((v == null) || (archive == null)) { return null; } - Object[] lines = archive.getRevision(v); - StringBuffer content = new StringBuffer(); - for (int i = 0; i < lines.length; i++) { - String line = lines[i].toString(); - content.append(line); - if (i != (lines.length - 1)) { - content.append("\n"); - } - } - - String scontent = content.toString(); + String xml = Stream.of(archive.getRevision(v)) + .map(Object::toString) + .collect(Collectors.joining("\n")); XWikiAttachment revattach = new XWikiAttachment(); - revattach.fromXML(scontent); + revattach.fromXML(xml); revattach.setDoc(attachment.getDoc()); - revattach.setVersion(rev); + revattach.setVersion(v.toString()); + /* + * If the RCS archive is loaded from Hibernate (legacy), the content is already injected + * above by fromXML. + * If an alternative content storage is used (e.g. S3), this loads the content here. + * It is expected that AttachmentContentStore.loadContent impl can resolve the correct + * revision based solely on attachment metadata (doc, filename, version). + */ + if (revattach.getAttachment_content() == null) { + revattach.loadContent(); + } return revattach; } catch (Exception e) { Object[] args = { attachment.getFilename() }; @@ -204,4 +206,8 @@ public XWikiAttachment getRevision(XWikiAttachment attachment, String rev, XWiki "Exception while manipulating the archive for file {0}", e, args); } } + + private AttachmentContentPolicy getAttachmentContentPolicy() { + return getBeanFactory().getBean(AttachmentContentPolicy.class); + } } diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachmentContent.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachmentContent.java index 13945672b..322bd5837 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachmentContent.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiAttachmentContent.java @@ -160,13 +160,9 @@ public byte[] getContent() { */ @Deprecated public void setContent(byte[] content) { - try { - byte[] internalContent = {}; - if (content != null) { - internalContent = content; - } - - this.setContent(new ByteArrayInputStream(internalContent)); + content = (content != null) ? content : new byte[0]; + try (InputStream in = new ByteArrayInputStream(content)) { + this.setContent(in); } catch (IOException e) { throw new RuntimeException("Failed to copy data to storage.", e); } @@ -220,23 +216,6 @@ public InputStream getContentInputStream() { } } - /** - * Set the content of the attachment from a portion of an InputStream. - * - * @param is - * the input stream that will be read - * @param len - * the number of bytes to read from the beginning of the stream - * @throws IOException - * when an error occurs during streaming operation - * @since 2.3M2 - */ - public void setContent(InputStream is, int len) throws IOException { - // TODO Fix so this sends a EOS when the limit is reached. - // this.setContent(new LimitedInputStream(is, ((long) len))); - this.setContent(is); - } - /** * Set the content of the attachment from an InputStream. * @@ -250,7 +229,6 @@ public void setContent(InputStream is) throws IOException { this.newFileItem(); IOUtils.copy(is, this.file.getOutputStream()); this.setContentDirty(true); - this.attachment.setFilesize(this.getSize()); } @@ -259,6 +237,11 @@ public void setContent(InputStream is) throws IOException { * @since 2.3M2 */ public int getSize() { - return (int) this.file.getSize(); + long size = file.getSize(); + if (size <= Integer.MAX_VALUE) { + return (int) size; + } else { + throw new IllegalStateException("Attachment size " + size + " exceeds max value"); + } } } diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java index 66432ef77..c332f2b1e 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java @@ -3428,10 +3428,15 @@ public void copyAttachments(XWikiDocument sourceDocument) { setContentDirty(true); } + @Deprecated public void loadAttachments(XWikiContext context) throws XWikiException { + loadAttachments(); + } + + public void loadAttachments() throws XWikiException { for (XWikiAttachment attachment : getAttachmentList()) { - attachment.loadContent(context); - attachment.loadArchive(context); + attachment.loadContent(); + attachment.loadArchive(); } } @@ -5795,8 +5800,8 @@ private void refactorDocumentLinks(DocumentReference oldDocumentReference, */ public XWikiDocument copyDocument(DocumentReference newDocumentReference, XWikiContext context) throws XWikiException { - loadAttachments(context); - loadArchive(context); + loadAttachments(); + getDocumentArchive(); XWikiDocument newdoc = duplicate(newDocumentReference); newdoc.setOriginalDocument(null); diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatch.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatch.java index 92ed6cd85..80939cd06 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatch.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatch.java @@ -23,8 +23,8 @@ import java.util.Arrays; import java.util.List; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.suigeneris.jrcs.util.ToString; import com.xpn.xwiki.XWikiContext; @@ -41,7 +41,7 @@ public class XWikiPatch { /** Logger. */ - private static final Log LOG = LogFactory.getLog(XWikiPatch.class); + private static final Logger LOG = LoggerFactory.getLogger(XWikiPatch.class); /** string serialization for patch. */ private String content; diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiRCSArchive.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiRCSArchive.java index 515d2f8d5..486cd8a5b 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiRCSArchive.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiRCSArchive.java @@ -24,13 +24,12 @@ import java.util.BitSet; import java.util.Collection; import java.util.Date; -import java.util.Iterator; import java.util.List; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.net.URLCodec; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.suigeneris.jrcs.diff.PatchFailedException; import org.suigeneris.jrcs.rcs.Archive; import org.suigeneris.jrcs.rcs.InvalidFileFormatException; @@ -56,7 +55,7 @@ public class XWikiRCSArchive extends Archive { /** logger. */ - private static final Log LOG = LogFactory.getLog(XWikiRCSArchive.class); + private static final Logger LOG = LoggerFactory.getLogger(XWikiRCSArchive.class); /** * Used to serialize {@link XWikiDocumentArchive}. @@ -85,8 +84,8 @@ public XWikiRCSArchive(Collection nodeInfos, XWikiContext cont nodes.put(node.getVersion(), node); } XWikiJRCSNode last = null; - for (Iterator it = nodes.keySet().iterator(); it.hasNext();) { - Version ver = (Version) it.next(); + for (Object element : nodes.keySet()) { + Version ver = (Version) element; XWikiJRCSNode node = (XWikiJRCSNode) nodes.get(ver); if (last != null) { last.setRCSNext(node); @@ -288,8 +287,8 @@ public void patch(List original, boolean annotate) public Collection getNodes(long docId) throws NodeNotFoundException, InvalidFileFormatException, PatchFailedException { Collection result = new ArrayList(nodes.size()); - for (Iterator it = nodes.values().iterator(); it.hasNext();) { - XWikiJRCSNode node = new XWikiJRCSNode((Node) it.next()); + for (Object element : nodes.values()) { + XWikiJRCSNode node = new XWikiJRCSNode((Node) element); XWikiRCSNodeInfo nodeInfo = new XWikiRCSNodeInfo(); nodeInfo.setId(new XWikiRCSNodeId(docId, node.getVersion())); nodeInfo.setDiff(node.isDiff()); diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/AttachmentContentStore.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/AttachmentContentStore.java new file mode 100644 index 000000000..a2d1f5e23 --- /dev/null +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/AttachmentContentStore.java @@ -0,0 +1,32 @@ +package com.xpn.xwiki.store; + +import javax.validation.constraints.NotEmpty; + +import org.xwiki.component.annotation.ComponentRole; + +import com.xpn.xwiki.doc.XWikiAttachment; +import com.xpn.xwiki.doc.XWikiAttachmentContent; + +@ComponentRole +public interface AttachmentContentStore { + + @NotEmpty + String getStoreName(); + + void saveContent(XWikiAttachmentContent content) throws AttachmentContentStoreException; + + void loadContent(XWikiAttachmentContent content) throws AttachmentContentStoreException; + + void deleteContent(XWikiAttachment attachment) throws AttachmentContentStoreException; + + void deleteContent(XWikiAttachmentContent content) throws AttachmentContentStoreException; + + public static class AttachmentContentStoreException extends Exception { + + private static final long serialVersionUID = 1L; + + public AttachmentContentStoreException(String message, Throwable cause) { + super(message, cause); + } + } +} diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/AttachmentVersioningStore.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/AttachmentVersioningStore.java index b3994f2aa..baf47cd75 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/AttachmentVersioningStore.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/AttachmentVersioningStore.java @@ -21,7 +21,6 @@ import org.xwiki.component.annotation.ComponentRole; -import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiAttachmentArchive; @@ -35,21 +34,20 @@ @ComponentRole public interface AttachmentVersioningStore { + boolean hasVersioning(); + /** * Load attachment archive from store. * * @return attachment archive. not null. return empty archive if it is not exist in store. * @param attachment * The attachment of archive. - * @param context - * The current context. * @param bTransaction * Should use old transaction (false) or create new (true). * @throws XWikiException * If an error occurs. */ - XWikiAttachmentArchive loadArchive(XWikiAttachment attachment, XWikiContext context, - boolean bTransaction) + XWikiAttachmentArchive loadArchive(XWikiAttachment attachment, boolean bTransaction) throws XWikiException; /** @@ -57,14 +55,12 @@ XWikiAttachmentArchive loadArchive(XWikiAttachment attachment, XWikiContext cont * * @param archive * The attachment archive to save. - * @param context - * The current context. * @param bTransaction * Should use old transaction (false) or create new (true). * @throws XWikiException * If an error occurs. */ - void saveArchive(XWikiAttachmentArchive archive, XWikiContext context, boolean bTransaction) + void saveArchive(XWikiAttachmentArchive archive, boolean bTransaction) throws XWikiException; /** @@ -72,13 +68,11 @@ void saveArchive(XWikiAttachmentArchive archive, XWikiContext context, boolean b * * @param attachment * The attachment to delete. - * @param context - * The current context. * @param bTransaction * Should use old transaction (false) or create new (true). * @throws XWikiException * If an error occurs. */ - void deleteArchive(XWikiAttachment attachment, XWikiContext context, boolean bTransaction) + void deleteArchive(XWikiAttachment attachment, boolean bTransaction) throws XWikiException; } diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/VoidAttachmentVersioningStore.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/VoidAttachmentVersioningStore.java index 1be831505..93c4af4f9 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/VoidAttachmentVersioningStore.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/VoidAttachmentVersioningStore.java @@ -33,11 +33,8 @@ import com.xpn.xwiki.doc.XWikiAttachmentArchive; /** - * Void store for attachment versioning when it is disabled. ("xwiki.store.attachment.versioning=0" - * parameter is set in - * xwiki.cfg) It says what there is only one version of attachment - latest. It doesn't store - * anything. It is safe to - * use with any stores. + * Void store for attachment versioning when it is disabled. It says what there is only one version + * of attachment - latest. It doesn't store anything. It is safe to use with any stores. * * @version $Id$ * @since 1.4M2 @@ -60,30 +57,25 @@ public VoidAttachmentVersioningStore(XWikiContext context) {} */ public VoidAttachmentVersioningStore() {} - /** - * {@inheritDoc} - */ @Override - public void deleteArchive(XWikiAttachment attachment, XWikiContext context, boolean transaction) + public boolean hasVersioning() { + return false; + } + + @Override + public void deleteArchive(XWikiAttachment attachment, boolean transaction) throws XWikiException { // Don't do anything since it's a void implementation. } - /** - * {@inheritDoc} - */ @Override - public void saveArchive(XWikiAttachmentArchive archive, XWikiContext context, boolean transaction) + public void saveArchive(XWikiAttachmentArchive archive, boolean transaction) throws XWikiException { // Don't do anything since it's a void implementation. } - /** - * {@inheritDoc} - */ @Override - public XWikiAttachmentArchive loadArchive(XWikiAttachment attachment, XWikiContext context, - boolean transaction) + public XWikiAttachmentArchive loadArchive(XWikiAttachment attachment, boolean transaction) throws XWikiException { XWikiAttachmentArchive archive = attachment.getAttachment_archive(); if (!(archive instanceof VoidAttachmentArchive)) { @@ -119,7 +111,7 @@ public VoidAttachmentArchive(XWikiAttachment attachment) { * {@inheritDoc} */ @Override - public void updateArchive(byte[] data, XWikiContext context) throws XWikiException { + public void updateArchive() throws XWikiException { getAttachment().incrementVersion(); getAttachment().setDate(new Date()); } @@ -136,8 +128,8 @@ public void setArchive(byte[] data) throws XWikiException { * {@inheritDoc} */ @Override - public byte[] getArchive(XWikiContext context) throws XWikiException { - String sdata = getAttachment().toStringXML(true, false, context); + public byte[] getArchive() throws XWikiException { + String sdata = getAttachment().toStringXML(true, false); Object[] lines = ToString.stringToArray(sdata); Archive archive = new Archive(lines, getAttachment().getFilename(), getAttachment().getVersion()); @@ -164,9 +156,9 @@ public Version[] getVersions() { * {@inheritDoc} */ @Override - public XWikiAttachment getRevision(XWikiAttachment attachment, String rev, XWikiContext context) + public XWikiAttachment getRevision(Version v) throws XWikiException { - return (attachment.getVersion().equals(rev)) ? attachment : null; + return (getAttachment().getVersion().equals(v.toString())) ? getAttachment() : null; } /** diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiAttachmentStoreInterface.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiAttachmentStoreInterface.java index da634e004..8cb020649 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiAttachmentStoreInterface.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiAttachmentStoreInterface.java @@ -57,4 +57,9 @@ void deleteXWikiAttachment(XWikiAttachment attachment, boolean parentUpdate, XWi boolean bTransaction) throws XWikiException; void cleanUp(XWikiContext context); + + AttachmentContentStore getContentStore(); + + AttachmentVersioningStore getVersioningStore(); + } diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateAttachmentStore.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateAttachmentStore.java index ae7487cb8..57dd425fc 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateAttachmentStore.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateAttachmentStore.java @@ -1,24 +1,37 @@ package com.xpn.xwiki.store; +import static com.celements.common.lambda.LambdaExceptionUtil.*; +import static com.celements.spring.context.SpringContextProvider.*; + import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Stream; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.hibernate.Query; import org.hibernate.Session; import org.xwiki.component.annotation.Component; +import com.celements.store.StoreFactory; +import com.google.common.base.Suppliers; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiAttachmentContent; import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.store.hibernate.HibernateAttachmentContentStore; @Component public class XWikiHibernateAttachmentStore extends XWikiHibernateBaseStore implements XWikiAttachmentStoreInterface { - private static final Log log = LogFactory.getLog(XWikiHibernateAttachmentStore.class); + private final Supplier contentStore = Suppliers + .memoize(StoreFactory::getAttachmentContentStore); + private final Supplier> contentStoreFallback = Suppliers + .memoize(() -> Optional.of(getBeanFactory() + .getBean(HibernateAttachmentContentStore.STORE_NAME, AttachmentContentStore.class)) + .filter(store -> !store.getStoreName().equals(contentStore.get().getStoreName()))); + private final Supplier versioningStore = Suppliers + .memoize(StoreFactory::getAttachmentVersioningStore); /** * Empty constructor needed for component manager. @@ -27,69 +40,47 @@ public XWikiHibernateAttachmentStore() {} @Override public void saveAttachmentContent(XWikiAttachment attachment, XWikiContext context, - boolean bTransaction) - throws XWikiException { + boolean bTransaction) throws XWikiException { saveAttachmentContent(attachment, true, context, bTransaction); } @Override public void saveAttachmentContent(XWikiAttachment attachment, boolean parentUpdate, - XWikiContext context, - boolean bTransaction) throws XWikiException { + XWikiContext context, boolean bTransaction) throws XWikiException { + String currentDb = context.getDatabase(); try { XWikiAttachmentContent content = attachment.getAttachment_content(); if (content.isContentDirty()) { - attachment.updateContentArchive(context); + attachment.updateContentArchive(); + } + var attWiki = attachment.getWikiReference(); + if (attWiki != null) { + context.setDatabase(attWiki.getName()); } if (bTransaction) { - checkHibernate(context); - bTransaction = beginTransaction(context); + bTransaction = beginTransaction(attWiki); } - Session session = getSession(context); - - String db = context.getDatabase(); - String attachdb = (attachment.getDoc() == null) ? null : attachment.getDoc().getDatabase(); - try { - if (attachdb != null) { - context.setDatabase(attachdb); - } - - Query query = session.createQuery( - "select attach.id from XWikiAttachmentContent as attach where attach.id = :id"); - query.setLong("id", content.getId()); - if (query.uniqueResult() == null) { - session.save(content); - } else { - session.update(content); - } - - if (attachment.getAttachment_archive() == null) { - attachment.loadArchive(context); - } - context.getWiki().getAttachmentVersioningStore().saveArchive( - attachment.getAttachment_archive(), - context, false); - - if (parentUpdate) { - context.getWiki().getStore().saveXWikiDoc(attachment.getDoc(), context, true); - } - - } finally { - context.setDatabase(db); + getContentStore().saveContent(content); + if (attachment.getAttachment_archive() == null) { + attachment.loadArchive(); + } + getVersioningStore().saveArchive(attachment.getAttachment_archive(), false); + if (parentUpdate) { + context.getWiki().getStore().saveXWikiDoc(attachment.getDoc(), context, true); } - if (bTransaction) { - endTransaction(context, true); + endTransaction(true); } } catch (Exception e) { - Object[] args = { attachment.getFilename(), attachment.getDoc().getFullName() }; + Object[] args = { attachment }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_ATTACHMENT, - "Exception while saving attachment {0} of document {1}", e, args); + "Exception while saving {0}", e, args); } finally { + context.setDatabase(currentDb); try { if (bTransaction) { - endTransaction(context, false); + endTransaction(false); } } catch (Exception e) {} } @@ -98,15 +89,15 @@ public void saveAttachmentContent(XWikiAttachment attachment, boolean parentUpda @Override public void saveAttachmentsContent(List attachments, XWikiDocument doc, - boolean bParentUpdate, - XWikiContext context, boolean bTransaction) throws XWikiException { + boolean bParentUpdate, XWikiContext context, boolean bTransaction) throws XWikiException { if (attachments == null) { return; } try { if (bTransaction) { - checkHibernate(context); - bTransaction = beginTransaction(context); + bTransaction = beginTransaction(attachments.stream() + .map(XWikiAttachment::getWikiReference) + .findFirst().orElse(null)); } for (XWikiAttachment att : attachments) { saveAttachmentContent(att, false, context, false); @@ -121,7 +112,7 @@ public void saveAttachmentsContent(List attachments, XWikiDocum } finally { try { if (bTransaction) { - endTransaction(context, false); + endTransaction(false); } } catch (Exception e) {} } @@ -130,45 +121,39 @@ public void saveAttachmentsContent(List attachments, XWikiDocum @Override public void loadAttachmentContent(XWikiAttachment attachment, XWikiContext context, - boolean bTransaction) - throws XWikiException { + boolean bTransaction) throws XWikiException { + String currentDb = context.getDatabase(); try { + var attWiki = attachment.getWikiReference(); + if (attWiki != null) { + context.setDatabase(attWiki.getName()); + } if (bTransaction) { - checkHibernate(context); - bTransaction = beginTransaction(false, context); + bTransaction = beginTransaction(attWiki); } - Session session = getSession(context); - - String db = context.getDatabase(); - String attachdb = (attachment.getDoc() == null) ? null : attachment.getDoc().getDatabase(); + XWikiAttachmentContent content = new XWikiAttachmentContent(attachment); + attachment.setAttachment_content(content); try { - if (attachdb != null) { - context.setDatabase(attachdb); - } - XWikiAttachmentContent content = new XWikiAttachmentContent(attachment); - attachment.setAttachment_content(content); - session.load(content, new Long(content.getId())); - - // Hibernate calls setContent which causes isContentDirty to be true. This is not what we - // want. - content.setContentDirty(false); - - } finally { - context.setDatabase(db); + getContentStore().loadContent(content); + } catch (Exception e) { + // content load failed, try legacy fallback store first before rethrowing + contentStoreFallback.get().ifPresent(rethrowConsumer(s -> s.loadContent(content))); + contentStoreFallback.get().orElseThrow(() -> e); } - + content.setContentDirty(false); if (bTransaction) { - endTransaction(context, false, false); + endTransaction(false); } } catch (Exception e) { - Object[] args = { attachment.getFilename(), attachment.getDoc().getFullName() }; + Object[] args = { attachment }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_ATTACHMENT, - "Exception while loading attachment {0} of document {1}", e, args); + "Exception while loading {0}", e, args); } finally { + context.setDatabase(currentDb); try { if (bTransaction) { - endTransaction(context, false, false); + endTransaction(false); } } catch (Exception e) {} } @@ -176,96 +161,80 @@ public void loadAttachmentContent(XWikiAttachment attachment, XWikiContext conte @Override public void deleteXWikiAttachment(XWikiAttachment attachment, XWikiContext context, - boolean bTransaction) - throws XWikiException { + boolean bTransaction) throws XWikiException { deleteXWikiAttachment(attachment, true, context, bTransaction); } @Override public void deleteXWikiAttachment(XWikiAttachment attachment, boolean parentUpdate, - XWikiContext context, - boolean bTransaction) throws XWikiException { + XWikiContext context, boolean bTransaction) throws XWikiException { + String currentDb = context.getDatabase(); try { + var attWiki = attachment.getWikiReference(); + if (attWiki != null) { + context.setDatabase(attWiki.getName()); + } if (bTransaction) { - checkHibernate(context); - bTransaction = beginTransaction(context); + bTransaction = beginTransaction(attWiki); } - - Session session = getSession(context); - - String db = context.getDatabase(); - String attachdb = (attachment.getDoc() == null) ? null : attachment.getDoc().getDatabase(); - try { - if (attachdb != null) { - context.setDatabase(attachdb); - } - - // Delete the three attachment entries - try { - loadAttachmentContent(attachment, context, false); - try { - session.delete(attachment.getAttachment_content()); - } catch (Exception e) { - if (log.isWarnEnabled()) { - log.warn("Error deleting attachment content " + attachment.getFilename() + " of doc " - + attachment.getDoc().getFullName()); + Session session = getSession(); + // delete attachment content + Stream.of(Optional.of(getContentStore()), contentStoreFallback.get()) + .flatMap(Optional::stream).forEach(store -> { + try { + store.deleteContent(attachment); + } catch (Exception e) { + logger.info("Error deleting content for {}", attachment); } - } - } catch (Exception e) { - if (log.isWarnEnabled()) { - log.warn("Error loading attachment content when deleting attachment " - + attachment.getFilename() + " of doc " + attachment.getDoc().getFullName()); - } - } - - context.getWiki().getAttachmentVersioningStore().deleteArchive(attachment, context, false); - - try { - session.delete(attachment); - } catch (Exception e) { - if (log.isWarnEnabled()) { - log.warn("Error deleting attachment meta data " + attachment.getFilename() + " of doc " - + attachment.getDoc().getFullName()); - } - } - - } finally { - context.setDatabase(db); + }); + // delete attachment archive + getVersioningStore().deleteArchive(attachment, false); + // delete attachment meta data + try { + session.delete(attachment); + } catch (Exception e) { + logger.warn("Error deleting meta data for {}", attachment); } - + // update parent document try { if (parentUpdate) { - List list = attachment.getDoc().getAttachmentList(); - for (int i = 0; i < list.size(); i++) { - XWikiAttachment attach = list.get(i); - if (attachment.getFilename().equals(attach.getFilename())) { - list.remove(i); + var iter = attachment.getDoc().getAttachmentList().iterator(); + while (iter.hasNext()) { + if (attachment.getFilename().equals(iter.next().getFilename())) { + iter.remove(); break; } } context.getWiki().getStore().saveXWikiDoc(attachment.getDoc(), context, false); } } catch (Exception e) { - if (log.isWarnEnabled()) { - log.warn("Error updating document when deleting attachment " + attachment.getFilename() - + " of doc " + attachment.getDoc().getFullName()); - } + logger.warn("Error updating document when deleting {}", attachment); } - if (bTransaction) { - endTransaction(context, true); + endTransaction(true); } } catch (Exception e) { - Object[] args = { attachment.getFilename(), attachment.getDoc().getFullName() }; + Object[] args = { attachment }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETING_ATTACHMENT, - "Exception while deleting attachment {0} of document {1}", e, args); + "Exception while deleting {0}", e, args); } finally { + context.setDatabase(currentDb); try { if (bTransaction) { - endTransaction(context, false); + endTransaction(false); } } catch (Exception e) {} } } + + @Override + public AttachmentContentStore getContentStore() { + return contentStore.get(); + } + + @Override + public AttachmentVersioningStore getVersioningStore() { + return versioningStore.get(); + } } diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateBaseStore.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateBaseStore.java index d0cf6d7a3..0ca542bf0 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateBaseStore.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateBaseStore.java @@ -1,5 +1,6 @@ package com.xpn.xwiki.store; +import static com.celements.execution.XWikiExecutionProp.*; import static com.celements.logging.LogUtils.*; import static com.google.common.base.Preconditions.*; @@ -24,6 +25,7 @@ import org.xwiki.component.annotation.Requirement; import org.xwiki.component.phase.Initializable; import org.xwiki.component.phase.InitializationException; +import org.xwiki.configuration.ConfigurationSource; import org.xwiki.context.Execution; import org.xwiki.context.ExecutionContext; import org.xwiki.model.reference.WikiReference; @@ -55,6 +57,9 @@ public class XWikiHibernateBaseStore implements Initializable { @Requirement protected XWikiConfigSource xwikiCfg; + @Requirement("allproperties") + protected ConfigurationSource cfgSrc; + private String hibpath = DEFAULT_CFG_PATH; /** @@ -1006,6 +1011,10 @@ protected final ExecutionContext getEContext() { return execution.getContext(); } + protected XWikiContext getXContext() { + return getEContext().get(XWIKI_CONTEXT).orElseThrow(IllegalStateException::new); + } + protected WikiReference getWikiRef(XWikiContext context) { return Strings.isNullOrEmpty(context.getDatabase()) ? null : new WikiReference(context.getDatabase()); diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateVersioningStore.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateVersioningStore.java index ba46725eb..88c72a128 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateVersioningStore.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateVersioningStore.java @@ -24,11 +24,11 @@ import java.util.Iterator; import java.util.List; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.criterion.Restrictions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.suigeneris.jrcs.rcs.Version; import org.xwiki.component.annotation.Component; @@ -50,7 +50,7 @@ public class XWikiHibernateVersioningStore extends XWikiHibernateBaseStore implements XWikiVersioningStoreInterface { /** Logger. */ - private static final Log LOG = LogFactory.getLog(XWikiHibernateVersioningStore.class); + private static final Logger LOG = LoggerFactory.getLogger(XWikiHibernateVersioningStore.class); /** Colon symbol. */ private static final String COLON = ":"; diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentContentStore.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentContentStore.java new file mode 100644 index 000000000..6f3463129 --- /dev/null +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentContentStore.java @@ -0,0 +1,73 @@ +package com.xpn.xwiki.store.hibernate; + +import javax.inject.Named; + +import org.hibernate.HibernateException; +import org.hibernate.Query; +import org.hibernate.Session; +import org.springframework.stereotype.Component; + +import com.xpn.xwiki.doc.XWikiAttachment; +import com.xpn.xwiki.doc.XWikiAttachmentContent; +import com.xpn.xwiki.store.AttachmentContentStore; +import com.xpn.xwiki.store.XWikiHibernateBaseStore; + +@Component +@Named(HibernateAttachmentContentStore.STORE_NAME) +public class HibernateAttachmentContentStore extends XWikiHibernateBaseStore + implements AttachmentContentStore { + + public static final String STORE_NAME = "store.attachment.content.hibernate"; + + @Override + public String getStoreName() { + return STORE_NAME; + } + + @Override + public void saveContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { + logger.info("saveContent - {}", content.getAttachment()); + try { + Session session = getSession(); + Query query = session.createQuery( + "select attach.id from XWikiAttachmentContent as attach where attach.id = :id"); + query.setLong("id", content.getId()); + if (query.uniqueResult() == null) { + session.save(content); + } else { + session.update(content); + } + } catch (HibernateException e) { + throw new AttachmentContentStoreException("Failed saving attachment", e); + } + } + + @Override + public void loadContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { + logger.info("loadContent - {}", content.getAttachment()); + try { + Session session = getSession(); + session.load(content, Long.valueOf(content.getId())); + } catch (HibernateException e) { + throw new AttachmentContentStoreException("Failed loading attachment", e); + } + } + + @Override + public void deleteContent(XWikiAttachment attachment) throws AttachmentContentStoreException { + logger.info("deleteContent - {}", attachment); + try { + Session session = getSession(); + session.createQuery("delete from " + XWikiAttachmentContent.class.getName() + " where id = ?") + .setLong(0, attachment.getId()) + .executeUpdate(); + } catch (Exception e) { + throw new AttachmentContentStoreException("Failed deleting attachment", e); + } + } + + @Override + public void deleteContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { + deleteContent(content.getAttachment()); + } +} diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentRecycleBinStore.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentRecycleBinStore.java index 9cfbb1c21..d615ee82d 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentRecycleBinStore.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentRecycleBinStore.java @@ -19,6 +19,8 @@ */ package com.xpn.xwiki.store.hibernate; +import static com.celements.spring.context.SpringContextProvider.*; + import java.util.Date; import java.util.List; @@ -30,6 +32,7 @@ import org.hibernate.criterion.Restrictions; import org.xwiki.component.annotation.Component; +import com.celements.store.att.AttachmentContentPolicy; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.DeletedAttachment; @@ -69,8 +72,10 @@ public HibernateAttachmentRecycleBinStore() {} public void saveToRecycleBin(XWikiAttachment attachment, String deleter, Date date, XWikiContext context, boolean bTransaction) throws XWikiException { + boolean includeContent = getBeanFactory().getBean(AttachmentContentPolicy.class) + .includeInRecycleBin(); final DeletedAttachment trashAtachment = new DeletedAttachment(attachment, deleter, date, - context); + includeContent); executeWrite(context, bTransaction, session -> { session.save(trashAtachment); return null; diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentVersioningStore.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentVersioningStore.java index 76855034c..6429e4f4e 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentVersioningStore.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/hibernate/HibernateAttachmentVersioningStore.java @@ -19,12 +19,11 @@ */ package com.xpn.xwiki.store.hibernate; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.hibernate.ObjectNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.xwiki.component.annotation.Component; -import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiAttachmentArchive; @@ -42,23 +41,27 @@ public class HibernateAttachmentVersioningStore extends XWikiHibernateBaseStore implements AttachmentVersioningStore { /** logger. */ - private static final Log LOG = LogFactory.getLog(HibernateAttachmentVersioningStore.class); + private static final Logger LOG = LoggerFactory + .getLogger(HibernateAttachmentVersioningStore.class); /** * Empty constructor needed for component manager. */ public HibernateAttachmentVersioningStore() {} - /** - * {@inheritDoc} - */ @Override - public XWikiAttachmentArchive loadArchive(final XWikiAttachment attachment, XWikiContext context, - boolean bTransaction) throws XWikiException { + public boolean hasVersioning() { + return true; + } + + @Override + public XWikiAttachmentArchive loadArchive(final XWikiAttachment attachment, boolean bTransaction) + throws XWikiException { try { final XWikiAttachmentArchive archive = new XWikiAttachmentArchive(); archive.setAttachment(attachment); - executeRead(context, bTransaction, session -> { + var wiki = attachment.getWikiReference(); + executeRead(wiki, bTransaction, session -> { try { session.load(archive, archive.getId()); } catch (ObjectNotFoundException e) { @@ -76,28 +79,22 @@ public XWikiAttachmentArchive loadArchive(final XWikiAttachment attachment, XWik } } - /** - * {@inheritDoc} - */ @Override - public void saveArchive(final XWikiAttachmentArchive archive, XWikiContext context, - boolean bTransaction) + public void saveArchive(final XWikiAttachmentArchive archive, boolean bTransaction) throws XWikiException { - executeWrite(context, bTransaction, session -> { + var wiki = archive.getAttachment().getWikiReference(); + executeWrite(wiki, bTransaction, session -> { session.saveOrUpdate(archive); return null; }); } - /** - * {@inheritDoc} - */ @Override - public void deleteArchive(final XWikiAttachment attachment, final XWikiContext context, - boolean bTransaction) + public void deleteArchive(final XWikiAttachment attachment, boolean bTransaction) throws XWikiException { try { - executeWrite(context, bTransaction, session -> { + var wiki = attachment.getWikiReference(); + executeWrite(wiki, bTransaction, session -> { XWikiAttachmentArchive archive = new XWikiAttachmentArchive(); archive.setAttachment(attachment); session.delete(archive); diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/migration/hibernate/XWikiHibernateMigrationManager.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/migration/hibernate/XWikiHibernateMigrationManager.java index 0aa42379e..298189a10 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/migration/hibernate/XWikiHibernateMigrationManager.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/migration/hibernate/XWikiHibernateMigrationManager.java @@ -22,8 +22,8 @@ import java.util.ArrayList; import java.util.List; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; @@ -40,7 +40,7 @@ public class XWikiHibernateMigrationManager extends AbstractXWikiMigrationManager { /** logger */ - protected static final Log LOG = LogFactory.getLog(XWikiHibernateMigrationManager.class); + protected static final Logger LOG = LoggerFactory.getLogger(XWikiHibernateMigrationManager.class); /** {@inheritDoc} */ public XWikiHibernateMigrationManager(XWikiContext context) throws XWikiException { diff --git a/celements-xwiki-core/src/test/java/com/xpn/xwiki/store/VoidAttachmentVersioningStoreTest.java b/celements-xwiki-core/src/test/java/com/xpn/xwiki/store/VoidAttachmentVersioningStoreTest.java deleted file mode 100644 index 2a7881d0d..000000000 --- a/celements-xwiki-core/src/test/java/com/xpn/xwiki/store/VoidAttachmentVersioningStoreTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package com.xpn.xwiki.store; - -import org.suigeneris.jrcs.rcs.Version; - -import com.xpn.xwiki.XWiki; -import com.xpn.xwiki.XWikiException; -import com.xpn.xwiki.doc.XWikiAttachment; -import com.xpn.xwiki.doc.XWikiAttachmentArchive; -import com.xpn.xwiki.doc.XWikiDocument; -import com.xpn.xwiki.store.VoidAttachmentVersioningStore.VoidAttachmentArchive; -import com.xpn.xwiki.test.AbstractBridgedXWikiComponentTestCase; - -/** - * Unit tests for {@link VoidAttachmentVersioningStore} and {@link VoidAttachmentArchive}. - * - * @version $Id$ - */ -public class VoidAttachmentVersioningStoreTest extends AbstractBridgedXWikiComponentTestCase { - - AttachmentVersioningStore store; - - @Override - protected void setUp() throws Exception { - super.setUp(); - XWiki xwiki = new XWiki(false); - getContext().setWiki(xwiki); - - this.store = getComponentManager().lookup(AttachmentVersioningStore.class, "void"); - xwiki.setAttachmentVersioningStore(this.store); - } - - public void testStore() throws XWikiException { - // is store correctly inited? - assertEquals(VoidAttachmentVersioningStore.class, this.store.getClass()); - // create doc, attachment & attachment archive - XWikiDocument doc = new XWikiDocument("Main", "Test"); - XWikiAttachment attachment = new XWikiAttachment(doc, "filename"); - attachment.setContent(new byte[] { 1 }); - attachment.updateContentArchive(getContext()); - // is archive correctly inited and cloneable? - this.store.saveArchive(attachment.getAttachment_archive(), this.getContext(), true); - XWikiAttachmentArchive archive = this.store.loadArchive(attachment, this.getContext(), false); - assertEquals(VoidAttachmentArchive.class, archive.getClass()); - assertEquals(VoidAttachmentArchive.class, archive.clone().getClass()); - assertEquals(archive, this.store.loadArchive(attachment, this.getContext(), true)); - - this.store.deleteArchive(attachment, getContext(), true); - } - - public void testHistory() throws XWikiException { - XWikiDocument doc = new XWikiDocument("Main", "Test"); - XWikiAttachment attachment = new XWikiAttachment(doc, "filename"); - // 1.1 - attachment.setContent(new byte[] { 1 }); - attachment.updateContentArchive(this.getContext()); - assertEquals(attachment, attachment.getAttachmentRevision("1.1", this.getContext())); - // 1.2 - attachment.setContent(new byte[] { 2 }); - attachment.updateContentArchive(this.getContext()); - assertEquals(attachment, attachment.getAttachmentRevision("1.2", this.getContext())); - // there should be only 1.2 version. - assertNull(attachment.getAttachmentRevision("1.1", this.getContext())); - assertNull(attachment.getAttachmentRevision("1.3", this.getContext())); - assertEquals(1, attachment.getVersions().length); - assertEquals(new Version(1, 2), attachment.getVersions()[0]); - } -} diff --git a/celements-xwiki-core/src/test/java/com/xpn/xwiki/store/XWikiHibernateStoreTest.java b/celements-xwiki-core/src/test/java/com/xpn/xwiki/store/XWikiHibernateStoreTest.java index f931e1294..1e2f2f718 100644 --- a/celements-xwiki-core/src/test/java/com/xpn/xwiki/store/XWikiHibernateStoreTest.java +++ b/celements-xwiki-core/src/test/java/com/xpn/xwiki/store/XWikiHibernateStoreTest.java @@ -29,6 +29,8 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.xwiki.configuration.ConfigurationSource; +import org.xwiki.test.MockConfigurationSource; import com.xpn.xwiki.XWikiConstant; import com.xpn.xwiki.test.AbstractComponentTest; @@ -44,7 +46,9 @@ public class XWikiHibernateStoreTest extends AbstractComponentTest { XWikiHibernateStore store; @Before - public void setUp() { + public void setUp() throws Exception { + var cfgSrc = new MockConfigurationSource(); + registerComponentMock(ConfigurationSource.class, "allproperties", cfgSrc); store = Utils.getComponent(XWikiHibernateStore.class); store.setPath("whatever"); }