Skip to content

Commit 3f049e1

Browse files
committed
XCOMMONS-3250: Make as easy as possible for various features to use an object store
* Extended the blog store API a bit. * Added an S3 implementation, the initial draft was by Claude Sonnet 4. Untested.
1 parent 77a7560 commit 3f049e1

File tree

20 files changed

+1970
-65
lines changed

20 files changed

+1970
-65
lines changed

pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@
137137
<!-- JAXB -->
138138
<jaxb.tools.version>2.0.15</jaxb.tools.version>
139139

140+
<!-- AWS SDK -->
141+
<aws.java.sdk.version>2.32.15</aws.java.sdk.version>
142+
140143
<!-- By default, Checkstyle, Backward compatibility check, Enforcer, License plugins, etc., are on -->
141144
<xwiki.checkstyle.skip>false</xwiki.checkstyle.skip>
142145
<xwiki.revapi.skip>false</xwiki.revapi.skip>
@@ -1501,6 +1504,13 @@
15011504
<artifactId>jakarta.enterprise.cdi-api</artifactId>
15021505
<version>2.0.2</version>
15031506
</dependency>
1507+
<dependency>
1508+
<groupId>software.amazon.awssdk</groupId>
1509+
<artifactId>bom</artifactId>
1510+
<version>${aws.java.sdk.version}</version>
1511+
<type>pom</type>
1512+
<scope>import</scope>
1513+
</dependency>
15041514
</dependencies>
15051515
</dependencyManagement>
15061516
<build>

xwiki-commons-core/xwiki-commons-store/xwiki-commons-store-blob/xwiki-commons-store-blob-api/src/main/java/org/xwiki/store/blob/Blob.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import java.io.InputStream;
2323
import java.io.OutputStream;
24-
import java.nio.file.Path;
2524

2625
import org.xwiki.stability.Unstable;
2726
import org.xwiki.store.StreamProvider;
@@ -43,12 +42,13 @@ public interface Blob extends StreamProvider
4342
/**
4443
* @return the path of this blob inside its store
4544
*/
46-
Path getPath();
45+
BlobPath getPath();
4746

4847
/**
4948
* @return true if the blob exists, false otherwise
49+
* @throws BlobStoreException when the existence of the blob cannot be determined
5050
*/
51-
boolean exists();
51+
boolean exists() throws BlobStoreException;
5252

5353
/**
5454
* Get the size of this blob.
@@ -59,30 +59,44 @@ public interface Blob extends StreamProvider
5959
long getSize() throws BlobStoreException;
6060

6161
/**
62+
* @param conditions the conditions that must be satisfied before writing to this blob
6263
* @return an OutputStream to write data to this blob
6364
* @throws BlobStoreException if the blob cannot be written, for example because its name is invalid. There is no
6465
* guarantee that in such a case an exception will be thrown, the exception could also only be thrown when data
6566
* is written to the stream, or when the stream is closed.
6667
*/
67-
OutputStream getOutputStream() throws BlobStoreException;
68+
OutputStream getOutputStream(WriteCondition... conditions) throws BlobStoreException;
6869

6970
/**
7071
* Write the content of the given InputStream to this blob.
7172
*
7273
* @param inputStream the InputStream to read data from
74+
* @param conditions the conditions that must be satisfied before writing to this blob
7375
* @throws BlobStoreException if the InputStream cannot be read or the blob cannot be written, for example because
7476
* its name is invalid.
7577
*/
76-
void writeFromStream(InputStream inputStream) throws BlobStoreException;
78+
void writeFromStream(InputStream inputStream, WriteCondition... conditions) throws BlobStoreException;
79+
80+
/**
81+
* Get an InputStream to read data from this blob.
82+
*
83+
* @return an InputStream to read data from this blob
84+
* @throws Exception if the blob cannot be read
85+
* @throws BlobNotFoundException if the blob does not exist
86+
*/
87+
@Override
88+
InputStream getStream() throws Exception;
7789

7890
/**
7991
* Copy this blob to another path within the same store.
8092
*
8193
* @param targetPath the target path for the copy
8294
* @return the new blob at the target path
8395
* @throws BlobStoreException if the copy operation fails, for example because this blob doesn't exist
96+
* @throws BlobNotFoundException if this blob does not exist
97+
* @throws BlobAlreadyExistsException if a blob already exists at the target path
8498
*/
85-
default Blob copyTo(Path targetPath) throws BlobStoreException
99+
default Blob copyTo(BlobPath targetPath) throws BlobStoreException
86100
{
87101
return getStore().copyBlob(getPath(), targetPath);
88102
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* See the NOTICE file distributed with this work for additional
3+
* information regarding copyright ownership.
4+
*
5+
* This is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU Lesser General Public License as
7+
* published by the Free Software Foundation; either version 2.1 of
8+
* the License, or (at your option) any later version.
9+
*
10+
* This software is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this software; if not, write to the Free
17+
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18+
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19+
*/
20+
package org.xwiki.store.blob;
21+
22+
import java.io.Serial;
23+
24+
import org.xwiki.stability.Unstable;
25+
26+
/**
27+
* Exception thrown when trying to copy a blob to a target path that already contains a blob.
28+
*
29+
* @version $Id$
30+
* @since 17.7.0RC1
31+
*/
32+
@Unstable
33+
public class BlobAlreadyExistsException extends BlobStoreException
34+
{
35+
/**
36+
* Provides an id for serialization.
37+
*/
38+
@Serial
39+
private static final long serialVersionUID = 1L;
40+
41+
private static final String BLOB_ALREADY_EXISTS_MESSAGE = "Blob already exists at target path: ";
42+
43+
private final BlobPath targetPath;
44+
45+
/**
46+
* Constructs a new exception with the specified target path.
47+
*
48+
* @param targetPath the path where a blob already exists
49+
*/
50+
public BlobAlreadyExistsException(BlobPath targetPath)
51+
{
52+
super(BLOB_ALREADY_EXISTS_MESSAGE + targetPath);
53+
this.targetPath = targetPath;
54+
}
55+
56+
/**
57+
* Constructs a new exception with the specified target path and cause.
58+
*
59+
* @param targetPath the path where a blob already exists
60+
* @param cause the cause of the exception
61+
*/
62+
public BlobAlreadyExistsException(BlobPath targetPath, Throwable cause)
63+
{
64+
super(BLOB_ALREADY_EXISTS_MESSAGE + targetPath, cause);
65+
this.targetPath = targetPath;
66+
}
67+
68+
/**
69+
* @return the path where a blob already exists
70+
*/
71+
public BlobPath getTargetPath()
72+
{
73+
return this.targetPath;
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* See the NOTICE file distributed with this work for additional
3+
* information regarding copyright ownership.
4+
*
5+
* This is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU Lesser General Public License as
7+
* published by the Free Software Foundation; either version 2.1 of
8+
* the License, or (at your option) any later version.
9+
*
10+
* This software is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this software; if not, write to the Free
17+
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18+
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19+
*/
20+
package org.xwiki.store.blob;
21+
22+
import org.xwiki.stability.Unstable;
23+
24+
/**
25+
* A write condition that requires the blob to not exist before writing.
26+
* This ensures atomic create-only operations.
27+
*
28+
* @version $Id$
29+
* @since 17.7.0RC1
30+
*/
31+
@Unstable
32+
public final class BlobDoesNotExistCondition implements WriteCondition
33+
{
34+
/**
35+
* Singleton instance of this condition.
36+
*/
37+
public static final BlobDoesNotExistCondition INSTANCE = new BlobDoesNotExistCondition();
38+
39+
private BlobDoesNotExistCondition()
40+
{
41+
// Singleton pattern
42+
}
43+
44+
@Override
45+
public String getDescription()
46+
{
47+
return "Blob must not exist";
48+
}
49+
50+
@Override
51+
public String toString()
52+
{
53+
return getDescription();
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* See the NOTICE file distributed with this work for additional
3+
* information regarding copyright ownership.
4+
*
5+
* This is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU Lesser General Public License as
7+
* published by the Free Software Foundation; either version 2.1 of
8+
* the License, or (at your option) any later version.
9+
*
10+
* This software is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this software; if not, write to the Free
17+
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18+
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19+
*/
20+
package org.xwiki.store.blob;
21+
22+
import java.io.Serial;
23+
24+
import org.xwiki.stability.Unstable;
25+
26+
/**
27+
* Exception thrown when a blob cannot be found at the specified path.
28+
*
29+
* @version $Id$
30+
* @since 17.7.0RC1
31+
*/
32+
@Unstable
33+
public class BlobNotFoundException extends BlobStoreException
34+
{
35+
/**
36+
* Provides an id for serialization.
37+
*/
38+
@Serial
39+
private static final long serialVersionUID = 1L;
40+
41+
private static final String BLOB_NOT_FOUND_MESSAGE = "Blob not found at path: ";
42+
43+
private final BlobPath blobPath;
44+
45+
/**
46+
* Constructs a new exception with the specified blob path.
47+
*
48+
* @param blobPath the path of the blob that was not found
49+
*/
50+
public BlobNotFoundException(BlobPath blobPath)
51+
{
52+
super(BLOB_NOT_FOUND_MESSAGE + blobPath);
53+
this.blobPath = blobPath;
54+
}
55+
56+
/**
57+
* Constructs a new exception with the specified blob path and cause.
58+
*
59+
* @param blobPath the path of the blob that was not found
60+
* @param cause the cause of the exception
61+
*/
62+
public BlobNotFoundException(BlobPath blobPath, Throwable cause)
63+
{
64+
super(BLOB_NOT_FOUND_MESSAGE + blobPath, cause);
65+
this.blobPath = blobPath;
66+
}
67+
68+
/**
69+
* @return the path of the blob that was not found
70+
*/
71+
public BlobPath getBlobPath()
72+
{
73+
return this.blobPath;
74+
}
75+
}

0 commit comments

Comments
 (0)