├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── alfresco-ceph-rados
├── Dockerfile
├── assets
│ └── alfresco-global.properties
├── docker-compose.yml
├── pom.xml
└── src
│ └── main
│ ├── amp
│ ├── config
│ │ └── alfresco
│ │ │ └── module
│ │ │ └── alfresco-ceph
│ │ │ ├── alfresco-global.properties
│ │ │ ├── caching-context.xml.sample
│ │ │ ├── log4j.properties
│ │ │ └── module-context.xml
│ └── module.properties
│ └── java
│ └── fr
│ └── jeci
│ └── alfresco
│ └── ceph
│ ├── CephContentReader.java
│ ├── CephContentWriter.java
│ ├── RadosReadableByteChannel.java
│ ├── RadosServiceImpl.java
│ └── RadosWritableByteChannel.java
├── alfresco-object-storage-commons
├── pom.xml
└── src
│ └── main
│ └── java
│ └── fr
│ └── jeci
│ └── alfresco
│ ├── ByteBufWritableByteChannel.java
│ ├── ObjectStorageContentStore.java
│ └── ObjectStorageService.java
├── alfresco-openio
├── Dockerfile
├── assets
│ └── alfresco-global.properties
├── docker-compose.yml
├── pom.xml
└── src
│ └── main
│ ├── amp
│ ├── config
│ │ └── alfresco
│ │ │ └── module
│ │ │ └── alfresco-openio
│ │ │ ├── alfresco-global.properties
│ │ │ ├── caching-context.xml.sample
│ │ │ ├── log4j.properties
│ │ │ └── module-context.xml
│ └── module.properties
│ └── java
│ └── fr
│ └── jeci
│ └── alfresco
│ └── openio
│ ├── OpenIOChannel.java
│ ├── OpenIOContentReader.java
│ ├── OpenIOContentWriter.java
│ └── OpenIOServiceImpl.java
├── alfresco-swift
├── Dockerfile
├── assets
│ └── alfresco-global.properties
├── pom.xml
└── src
│ └── main
│ ├── amp
│ ├── config
│ │ └── alfresco
│ │ │ └── module
│ │ │ └── alfresco-swift
│ │ │ ├── alfresco-global.properties
│ │ │ ├── caching-context.xml.sample
│ │ │ ├── log4j.properties
│ │ │ └── module-context.xml
│ └── module.properties
│ └── java
│ └── fr
│ └── jeci
│ └── alfresco
│ └── swift
│ ├── SwiftChannel.java
│ ├── SwiftContentReader.java
│ ├── SwiftContentWriter.java
│ └── SwiftServiceImpl.java
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | **.idea
3 | **.classpath
4 | **.project
5 | **.ipr
6 | **.iml
7 | **.log
8 | **.log*
9 | **.settings
10 | **bin
11 | *.DS_Store
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: bash
2 |
3 | services:
4 | - docker
5 |
6 | script:
7 | - docker run --rm -v $(pwd):/root/sources -w /root/sources maven:3.3.9-jdk-8 mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
8 |
9 | after_success:
10 | - if [ "$TRAVIS_TAG" ]; then
11 | docker build -t jeci/alfresco-platform-ceph-rados:$TRAVIS_TAG -t jeci/alfresco-platform-ceph-rados:latest alfresco-ceph-rados;
12 | docker build -t jeci/alfresco-platform-openio:$TRAVIS_TAG -t jeci/alfresco-platform-openio:latest alfresco-openio;
13 | docker build -t jeci/alfresco-platform-swift:$TRAVIS_TAG -t jeci/alfresco-platform-swift:latest alfresco-swift;
14 | docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD";
15 | docker push jeci/alfresco-platform-ceph-rados;
16 | docker push jeci/alfresco-platform-openio;
17 | docker push jeci/alfresco-platform-swift;
18 | fi
19 |
20 | deploy:
21 | provider: releases
22 | api_key:
23 | secure: GdwzvHcDmW8RZZZzG6FCRMEf/whE9ER1HukxEQNHPuVS+PCCb9hmsCgyauMP7MvL72GCJ+IB13f2x3BYRjdnG8n9yszUlgYvjYeEKCa58GmnDeHUxd+WS3ireZJhnYBSkLN920PI1WUjczye9d/9Tzma+1j2xTt+SPvmDmYueRQ3gulP1119lGJSdicitBh9xTExSwyPVLrhG1z6KHGFOQE7G5c/iiIoZXgErmzak6acp/HwErGYBAcDAOPLZsazzyyoGrnydbyyTWyQxGhOz1cJ0VPF2POnAgQ/GWGPQTBvOaDqN+XVUMTH1eZ6At6OsOI1GI2UqrN10u7WLClWP6KWetXgL6vaG9DztR3O7XO/EyfSEBua1BX4hiiCdd7nkMVWNAIkkntuDpqGSNthejaLYTS6TXWnLydD33ScVSV+XeL+2gyH5E1GT94mGleZvi0jyzglwGoAxLwN+w6ZWNEkymN5Qm+F7hGvWRv2HsqafPENnK9m/tpqJ2TC8X8gEjVIo9DspCChLphkhYTHZBVj0CitSJIUaHvZEqGkU/emorSHEhXNf1smkYpCg0/kJ2OkNYTO9pKY1e+WCrtHM4+t5Zkng3lNePx11s0Qm8bscQZSllCWe0IRMWb2FMbrYUWEC98W7CK/7AiH5PS7P6dv5h+bY2BRP38iWnSVqDk=
24 | file:
25 | - "alfresco-ceph-rados/target/*.amp"
26 | - "alfresco-openio/target/*.amp"
27 | - "alfresco-swift/target/*.amp"
28 | file_glob: true
29 | skip_cleanup: true
30 | on:
31 | repo: jeci-sarl/alfresco-object-storage-connectors
32 | tags: true
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Object Storage Connectors for Alfresco
2 |
3 | This project provides connectors to use Alfresco with Content Store based on Software Defined Storage.
4 |
5 | Currently 3 connectors for :
6 |
7 | * [Red Hat Ceph Storage](https://www.redhat.com/fr/technologies/storage/ceph) using [rados](http://docs.ceph.com/docs/master/rados/api/librados-intro/)
8 | * [OpenIO](http://openio.io/)
9 | * [Openstack Swift](http://docs.openstack.org/developer/swift/)
10 |
11 | [](https://travis-ci.org/jeci-sarl/alfresco-object-storage-connectors)
12 |
13 |
14 | ## Installation
15 |
16 | All three connectors are available as Alfresco modules AMP, and could be install using the [default procedure](http://docs.alfresco.com/5.2/tasks/amp-install.html).
17 |
18 | Then you have to edit `alfresco-global.properties` for supply configurations and credentials.
19 |
20 | Target properties are different for each connectors
21 |
22 | ### Ceph Rados Configuration
23 |
24 | ``` ini
25 | ceph.configFile=/etc/ceph/ceph.conf
26 | ceph.id=client.admin
27 | ceph.pool=alfresco
28 | ceph.clusterName=ceph
29 | ```
30 |
31 |
32 | ### OpenIO Configuration
33 |
34 | ``` ini
35 | openio.target=http://127.0.0.1:6006
36 | openio.namespace=OPENIO
37 | openio.account=alfresco
38 | openio.container=alfresco
39 | ```
40 |
41 |
42 | ### OpenStack Swift Configuration
43 |
44 | ``` ini
45 | swift.username=user:swift
46 | swift.password=abcdefghijklmnop
47 | swift.url=https://s3.example.org/auth/1.0
48 | swift.tenantId=
49 | swift.tenantName=
50 | swift.containerName=alfresco
51 | ```
52 |
53 |
54 | ## License
55 |
56 | This project is released under the [LGPL, Version 3](https://www.gnu.org/licenses/lgpl.html).
57 |
58 |
59 | ## Reporting Issues and Community Support
60 |
61 | Report issues (and contribute!) [here](https://github.com/jeci-sarl/alfresco-object-storage-connectors/issues?milestone=1&state=open).
62 |
63 | ## Engineering support
64 |
65 | [Jeci SARL](https://jeci.fr/about.html) offers annually contracted Level 3 support.
66 |
67 | - Diagnose and correcting bugs
68 | - Port / adapt code to any Alfresco version (Community or Enterprise)
69 | - Port / adapt code to any SDS version
70 |
71 | ## Authors
72 |
73 | - Jérémie Lesage - [Jeci](https://jeci.fr)
74 | - Florent Manens - [Beezim](https://beezim.fr)
75 |
76 |
77 | ## Alternatives
78 |
79 | * https://github.com/rmberg/alfresco-s3-adapter
80 | * https://github.com/douglascrp/alfresco-cloud-store
81 | * https://github.com/EisenVault/ev-alfresco-azure-adapter
82 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM jeci/alfresco-platform:5.2.f
2 | MAINTAINER Jeremie Lesage
3 |
4 |
5 | ENV ceph_stable_release=jewel
6 |
7 | RUN apt-get update \
8 | && apt-get install -y --no-install-recommends apt-transport-https \
9 | && wget -q -O- 'https://download.ceph.com/keys/release.asc' | apt-key add - \
10 | && echo deb https://download.ceph.com/debian-${ceph_stable_release}/ jessie main | tee /etc/apt/sources.list.d/ceph.list \
11 | && apt-get update \
12 | && apt-get install -y --no-install-recommends \
13 | libcephfs-java \
14 | librados2 \
15 | libjna-java \
16 | && rm -r /var/lib/apt/lists/*
17 |
18 | COPY target/alfresco-ceph-*.amp /root/amp/
19 |
20 | RUN java -jar /root/alfresco-mmt.jar install /root/amp/ /usr/local/tomcat/webapps/alfresco -nobackup -directory
21 |
22 | COPY assets/alfresco-global.properties /usr/local/tomcat/shared/classes/alfresco-global.properties
23 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/assets/alfresco-global.properties:
--------------------------------------------------------------------------------
1 | ceph.configFile=/etc/ceph/ceph.conf
2 | ceph.id=client.admin
3 | ceph.pool=alfresco
4 | ceph.clusterName=ceph
5 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | # CONFIGURATION POUR VPS SSD 3 de OVH
3 | # 2vCores, 8Go RAM, 40Go SSD
4 | volumes:
5 | alf_logs:
6 | alf_data:
7 | share_logs:
8 | solr_logs:
9 | solr_data:
10 | pgsql_data:
11 | ceph-etc:
12 |
13 |
14 | networks:
15 | alf-share:
16 | internal:
17 | driver: bridge
18 | ipam:
19 | driver: default
20 | config:
21 | - subnet: 192.168.165.0/24
22 | gateway: 192.168.165.1
23 |
24 | services:
25 | alfresco:
26 | image: jeci/alfresco-platform-ceph-rados
27 | environment:
28 | CATALINA_OPTS: "-Xms512m -Xmx1G -XX:+UseConcMarkSweepGC "
29 | depends_on:
30 | - postgresql
31 | - libreoffice
32 | - ceph
33 | ulimits:
34 | nproc: 65535
35 | nofile:
36 | soft: 20000
37 | hard: 40000
38 | networks:
39 | - alf-share
40 | - internal
41 | ports:
42 | - "8081:8080"
43 | volumes:
44 | - "alf_logs:/usr/local/tomcat/logs/"
45 | - "alf_data:/opt/alf_data"
46 | - "ceph-etc:/etc/ceph"
47 | tmpfs:
48 | - /tmp
49 | - /usr/local/tomcat/temp/:exec
50 | - /usr/local/tomcat/work/
51 |
52 |
53 | share:
54 | image: jeci/alfresco-share:5.2.e
55 | restart: always
56 | environment:
57 | CATALINA_OPTS: "-Xms512m -Xmx1G -XX:+UseConcMarkSweepGC "
58 | depends_on:
59 | - alfresco
60 | networks:
61 | - alf-share
62 | ports:
63 | - "8080:8080"
64 | volumes:
65 | - "share_logs:/usr/local/tomcat/logs/"
66 | tmpfs:
67 | - /tmp
68 | - /usr/local/tomcat/temp/
69 | - /usr/local/tomcat/work/
70 |
71 | solr:
72 | image: jeci/alfresco-solr:5.2.f
73 | environment:
74 | CATALINA_OPTS: "-Xms512m -Xmx1G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:G1HeapRegionSize=8m -XX:MaxGCPauseMillis=200 "
75 | depends_on:
76 | - alfresco
77 | ulimits:
78 | nproc: 65535
79 | nofile:
80 | soft: 20000
81 | hard: 40000
82 | networks:
83 | - internal
84 | volumes:
85 | - "solr_logs:/usr/local/tomcat/logs/"
86 | - "solr_data:/opt/solr_data"
87 | tmpfs:
88 | - /tmp
89 | - /usr/local/tomcat/temp/
90 | - /usr/local/tomcat/work/
91 |
92 | postgresql:
93 | image: postgres:9.4
94 | environment:
95 | - POSTGRES_DB=alfresco
96 | - POSTGRES_PASSWORD=alfresco
97 | - POSTGRES_USER=alfresco
98 | networks:
99 | - internal
100 | volumes:
101 | - "pgsql_data:/var/lib/postgresql/data"
102 |
103 | libreoffice:
104 | image: jeci/alfresco-libreoffice:5.2.7
105 | restart: always
106 | user: root
107 | networks:
108 | - internal
109 |
110 | ceph:
111 | image: ceph/demo
112 | container_name: ceph-service
113 | environment:
114 | MON_IP: 192.168.165.10
115 | CEPH_PUBLIC_NETWORK: 192.168.165.0/24
116 | volumes:
117 | - "ceph-etc:/etc/ceph"
118 | networks:
119 | internal:
120 | ipv4_address: 192.168.165.10
121 | stdin_open: true
122 | tty: true
123 |
124 | ceph-client:
125 | image: ceph/rados
126 | container_name: ceph-client
127 | volumes:
128 | - "ceph-etc:/etc/ceph"
129 | - "./output:/output"
130 | depends_on:
131 | - ceph
132 | networks:
133 | - internal
134 | # command: "mkpool alfresco"
135 | command: "lspools"
136 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | alfresco-ceph-rados
6 | 1.0
7 | Rados Ceph Connector for Alfresco
8 | amp
9 | Rados Ceph (aka Red Hat Ceph Storage) module connector for Alfresco
10 |
11 |
12 | fr.jeci.alfresco-object-storage
13 | alfresco-object-storage-parent
14 | 1.0.0
15 |
16 |
17 |
18 |
19 | GNU Lesser General Public License v3.0 or later
20 | http://www.gnu.org/licenses/lgpl-3.0-standalone.html
21 |
22 |
23 |
24 |
25 |
26 | fr.jeci.alfresco-object-storage
27 | alfresco-object-storage-commons
28 | 1.0.0
29 |
30 |
31 |
32 | ${alfresco.groupId}
33 | alfresco-repository
34 |
35 |
36 |
37 | com.ceph
38 | rados
39 | 0.3.0
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/src/main/amp/config/alfresco/module/alfresco-ceph/alfresco-global.properties:
--------------------------------------------------------------------------------
1 | ## Ceph Configuration
2 |
3 | ceph.configFile=/etc/ceph/ceph.conf
4 | ceph.id=client.admin
5 | ceph.pool=alfresco
6 | ceph.clusterName=ceph
7 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/src/main/amp/config/alfresco/module/alfresco-ceph/caching-context.xml.sample:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
52 |
53 |
54 |
55 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | org.alfresco.repo.content.caching.cleanup.CachedContentCleanupJob
67 |
68 |
69 |
74 |
75 |
76 |
77 |
80 |
81 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | ${system.content.caching.contentCleanup.cronExpression}
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/src/main/amp/config/alfresco/module/alfresco-ceph/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.logger.fr.jeci.alfresco.ceph=INFO
2 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/src/main/amp/config/alfresco/module/alfresco-ceph/module-context.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/src/main/amp/module.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 |
17 |
18 | # SDK Sample module
19 |
20 | # ==== Beginning of Alfresco required/optional properties ====== #
21 | # NB: These properties are filtered at build time by Maven, single
22 | # sourcing from POM properties
23 | module.id=${project.artifactId}
24 | #module.aliases=myModule-123, my-module
25 | module.title=${project.name}
26 | module.description=${project.description}
27 | module.version=${project.version}
28 |
29 | # The following optional properties can be used to prevent the module from being added
30 | # to inappropriate versions of the WAR file.
31 | # module.repo.version.min=2.0
32 | # module.repo.version.max=2.1
33 |
34 | # FIXME: This dependencies should come out of mvn dependencies on amp
35 |
36 | # The following describe dependencies on other modules
37 | # Depends on net.sf.myproject.module.SupportModuleA version ${version} or later
38 | # module.depends.net.sf.myproject.module.SupportModuleA=${version}-*
39 | # Depends on net.sf.myproject.module.SupportModuleA version ${version} to 2.0
40 | # module.depends.net.sf.myproject.module.SupportModuleB=${version}-2.0
41 | # Depends on net.sf.myproject.module.SupportModuleC - any version
42 | # module.depends.net.sf.myproject.module.SupportModuleB=*
43 |
44 |
45 | # ==== End of Alfresco required/optional properties ======= #
46 |
47 |
48 | # ==== Beginning of module required properties/optional ====== #
--------------------------------------------------------------------------------
/alfresco-ceph-rados/src/main/java/fr/jeci/alfresco/ceph/CephContentReader.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.ceph;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.io.IOException;
20 | import java.nio.channels.ReadableByteChannel;
21 |
22 | import org.alfresco.repo.content.AbstractContentReader;
23 | import org.alfresco.service.cmr.repository.ContentIOException;
24 | import org.alfresco.service.cmr.repository.ContentReader;
25 | import org.apache.commons.logging.Log;
26 | import org.apache.commons.logging.LogFactory;
27 |
28 | import com.ceph.rados.IoCTX;
29 | import com.ceph.rados.Rados;
30 | import com.ceph.rados.exceptions.RadosException;
31 | import com.ceph.rados.jna.RadosObjectInfo;
32 |
33 | public class CephContentReader extends AbstractContentReader {
34 | private static final Log logger = LogFactory.getLog(CephContentReader.class);
35 |
36 | private final Rados rados;
37 | private final String pool;
38 | private final String locator;
39 |
40 | public CephContentReader(Rados rados, String pool, String locator) throws IOException {
41 | super(locator);
42 | this.rados = rados;
43 | this.locator = locator;
44 | this.pool = pool;
45 |
46 | if (locator == null) {
47 | throw new IllegalArgumentException("locator is null");
48 | }
49 |
50 | if (pool == null) {
51 | throw new IllegalArgumentException("pool is null");
52 | }
53 |
54 | if (logger.isDebugEnabled()) {
55 | logger.debug("Ceph Content Reader for " + locator + " in " + pool);
56 | }
57 |
58 | }
59 |
60 | @Override
61 | public boolean exists() {
62 | IoCTX ioctx = null;
63 | try {
64 | ioctx = this.rados.ioCtxCreate(pool);
65 | RadosObjectInfo stat = ioctx.stat(locator);
66 | return stat != null;
67 |
68 | } catch (RadosException e) {
69 | logger.error(e.getMessage(), e);
70 | } finally {
71 | if (ioctx != null) {
72 | this.rados.ioCtxDestroy(ioctx);
73 | }
74 | }
75 |
76 | return false;
77 | }
78 |
79 | @Override
80 | public long getLastModified() {
81 | IoCTX ioctx = null;
82 |
83 | try {
84 | ioctx = this.rados.ioCtxCreate(this.pool);
85 | RadosObjectInfo stat = ioctx.stat(this.locator);
86 |
87 | if (stat != null) {
88 | return stat.getMtime();
89 | }
90 |
91 | } catch (RadosException e) {
92 | logger.error(e.getMessage(), e);
93 | } finally {
94 | if (ioctx != null) {
95 | this.rados.ioCtxDestroy(ioctx);
96 | }
97 | }
98 |
99 | return 0L;
100 | }
101 |
102 | @Override
103 | public long getSize() {
104 | IoCTX ioctx = null;
105 |
106 | try {
107 | ioctx = this.rados.ioCtxCreate(this.pool);
108 | RadosObjectInfo stat = ioctx.stat(this.locator);
109 |
110 | if (stat != null) {
111 | return stat.getSize();
112 | }
113 |
114 | } catch (RadosException e) {
115 | logger.error(e.getMessage(), e);
116 | } finally {
117 | if (ioctx != null) {
118 | this.rados.ioCtxDestroy(ioctx);
119 | }
120 | }
121 |
122 | return 0L;
123 | }
124 |
125 | @Override
126 | protected ContentReader createReader() {
127 | try {
128 | return new CephContentReader(this.rados, this.pool, this.locator);
129 | } catch (IOException e) {
130 | throw new ContentIOException(e.getMessage(), e);
131 | }
132 | }
133 |
134 | @Override
135 | protected ReadableByteChannel getDirectReadableChannel() {
136 | return new RadosReadableByteChannel(this.rados, this.pool, this.locator);
137 | }
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/src/main/java/fr/jeci/alfresco/ceph/CephContentWriter.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.ceph;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.io.IOException;
20 | import java.nio.channels.WritableByteChannel;
21 |
22 | import org.alfresco.repo.content.AbstractContentWriter;
23 | import org.alfresco.service.cmr.repository.ContentIOException;
24 | import org.alfresco.service.cmr.repository.ContentReader;
25 |
26 | import com.ceph.rados.Rados;
27 |
28 | public class CephContentWriter extends AbstractContentWriter {
29 | private final Rados rados;
30 | private final String pool;
31 | private final String locator;
32 |
33 | private final RadosWritableByteChannel channel;
34 |
35 | public CephContentWriter(Rados rados, String pool, String locator) {
36 | super(locator, null);
37 | this.rados = rados;
38 | this.pool = pool;
39 | this.locator = locator;
40 |
41 | if (locator == null) {
42 | throw new IllegalArgumentException("locator is null");
43 | }
44 |
45 | if (pool == null) {
46 | throw new IllegalArgumentException("pool is null");
47 | }
48 | this.channel = new RadosWritableByteChannel(rados, pool, locator);
49 | }
50 |
51 | @Override
52 | protected ContentReader createReader() {
53 | try {
54 | return new CephContentReader(rados, pool, locator);
55 | } catch (IOException e) {
56 | throw new ContentIOException(e.getMessage(), e);
57 | }
58 | }
59 |
60 | @Override
61 | protected WritableByteChannel getDirectWritableChannel() {
62 | return this.channel;
63 | }
64 |
65 | @Override
66 | public long getSize() {
67 | return this.channel.getOffset();
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/src/main/java/fr/jeci/alfresco/ceph/RadosReadableByteChannel.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.ceph;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.io.IOException;
20 | import java.nio.ByteBuffer;
21 | import java.nio.channels.ReadableByteChannel;
22 | import java.nio.channels.spi.AbstractInterruptibleChannel;
23 |
24 | import org.alfresco.service.cmr.repository.ContentIOException;
25 | import org.apache.commons.logging.Log;
26 | import org.apache.commons.logging.LogFactory;
27 |
28 | import com.ceph.rados.IoCTX;
29 | import com.ceph.rados.Rados;
30 | import com.ceph.rados.exceptions.RadosException;
31 |
32 | public class RadosReadableByteChannel extends AbstractInterruptibleChannel implements ReadableByteChannel {
33 | private static final Log logger = LogFactory.getLog(RadosReadableByteChannel.class);
34 |
35 | private static final int TRANSFER_SIZE = 8192;
36 | private byte[] buf = new byte[0];
37 | private Object readLock = new Object();
38 | private final String locator;
39 |
40 | private IoCTX io;
41 | private Rados rados;
42 |
43 | // Offest of byte read from rados
44 | private int offset = 0;
45 |
46 | public RadosReadableByteChannel(Rados rados, String pool, String locator) {
47 | this.rados = rados;
48 | this.locator = locator;
49 |
50 | if (locator == null) {
51 | throw new IllegalArgumentException("locator is null");
52 | }
53 |
54 | try {
55 | this.io = this.rados.ioCtxCreate(pool);
56 | } catch (RadosException e) {
57 | throw new ContentIOException("Error creatinf IO Context", e);
58 | }
59 | }
60 |
61 | @Override
62 | public int read(ByteBuffer dst) throws IOException {
63 | int remaining = dst.remaining();
64 | int totalRead = 0;
65 | int bytesRead = 0;
66 | synchronized (this.readLock) {
67 | long start = System.currentTimeMillis();
68 |
69 | try {
70 | while (totalRead < remaining) {
71 | int bytesToRead = Math.min(remaining - totalRead, TRANSFER_SIZE);
72 | if (buf.length < bytesToRead) {
73 | buf = new byte[bytesToRead];
74 | }
75 |
76 | try {
77 | begin();
78 |
79 | bytesRead = this.io.read(this.locator, bytesToRead, this.offset, this.buf);
80 | this.offset += bytesRead;
81 | } finally {
82 | end(bytesRead >= 0);
83 | }
84 |
85 | // exit when error
86 | if (bytesRead < 0) {
87 | break;
88 | }
89 |
90 | totalRead += bytesRead;
91 |
92 | dst.put(buf, 0, bytesRead);
93 |
94 | // No more bytes to read, exit
95 | if (bytesRead < bytesToRead) {
96 | break;
97 | }
98 |
99 | }
100 |
101 | if (logger.isDebugEnabled()) {
102 | logger.debug("# Rados read " + this.locator + " " + totalRead + " bytes in "
103 | + (System.currentTimeMillis() - start) + " ms");
104 | }
105 |
106 | if ((bytesRead <= 0) && (totalRead == 0)) {
107 | return -1;
108 | }
109 | } catch (RadosException e) {
110 | throw new IOException(e);
111 | }
112 |
113 | return totalRead;
114 | }
115 | }
116 |
117 | @Override
118 | protected void implCloseChannel() throws IOException {
119 | if (this.io != null) {
120 | this.rados.ioCtxDestroy(this.io);
121 | }
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/src/main/java/fr/jeci/alfresco/ceph/RadosServiceImpl.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.ceph;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.io.File;
20 | import java.io.IOException;
21 |
22 | import org.alfresco.service.cmr.repository.ContentReader;
23 | import org.alfresco.service.cmr.repository.ContentWriter;
24 | import org.apache.commons.logging.Log;
25 | import org.apache.commons.logging.LogFactory;
26 |
27 | import com.ceph.rados.IoCTX;
28 | import com.ceph.rados.Rados;
29 | import com.ceph.rados.exceptions.RadosException;
30 | import com.ceph.rados.jna.RadosClusterInfo;
31 |
32 | import fr.jeci.alfresco.ObjectStorageService;
33 |
34 | public class RadosServiceImpl implements ObjectStorageService {
35 | private static final Log logger = LogFactory.getLog(RadosServiceImpl.class);
36 |
37 | private String configFile;
38 | private String id;
39 | private String pool;
40 | private String metadataPool;
41 | private String clusterName;
42 |
43 | private Rados rados;
44 |
45 | private int writebufferSize;
46 | private int readbufferSize;
47 |
48 | public void init() throws RadosException {
49 | if (logger.isInfoEnabled()) {
50 | logger.info("Config : " + this.configFile);
51 | logger.info("Pool : " + this.pool);
52 | logger.info("clusterName : " + this.clusterName);
53 | logger.info("id : " + this.id);
54 | }
55 |
56 | this.rados = new Rados(this.clusterName, this.id, 0);
57 | File file = new File(this.configFile);
58 | if (file.exists()) {
59 | if (logger.isDebugEnabled()) {
60 | logger.debug("Load Rados Conf " + this.configFile);
61 | }
62 | this.rados.confReadFile(file);
63 | } else {
64 | logger.error("File " + this.configFile + " not found");
65 | }
66 | this.rados.connect();
67 | logger.info(printRadosStats(rados));
68 | }
69 |
70 | private String printRadosStats(Rados rados) throws RadosException {
71 | RadosClusterInfo stat = rados.clusterStat();
72 | StringBuilder sb = new StringBuilder();
73 | sb.append("Using RadosServiceImpl");
74 | sb.append(" (").append(stat.kb).append("KB, ");// Cluster size
75 | sb.append(stat.kb_used * 100 / stat.kb).append("%, ");
76 | // sb.append(stat.kb_avail); // KB available
77 | // sb.append("KB, ");
78 | sb.append(stat.num_objects).append(")");// Number of objects
79 | return sb.toString();
80 | }
81 |
82 | @Override
83 | public ContentReader getReader(String locator) throws IOException {
84 | return new CephContentReader(rados, pool, locator);
85 | }
86 |
87 | @Override
88 | public ContentWriter getWriter(String locator) {
89 | return new CephContentWriter(rados, pool, locator);
90 | }
91 |
92 | @Override
93 | public boolean isWriteSupported() {
94 | return true;
95 | }
96 |
97 | @Override
98 | public boolean delete(String locator) {
99 | IoCTX io = null;
100 | try {
101 | io = this.rados.ioCtxCreate(pool);
102 | io.remove(locator);
103 |
104 | return true;
105 | } catch (Exception e) {
106 | logger.error(e.getMessage(), e);
107 | return false;
108 | } finally {
109 | if (io != null) {
110 | this.rados.ioCtxDestroy(io);
111 | }
112 | }
113 | }
114 |
115 | public String getConfigFile() {
116 | return configFile;
117 | }
118 |
119 | public void setConfigFile(String configFile) {
120 | this.configFile = configFile;
121 | }
122 |
123 | public String getId() {
124 | return id;
125 | }
126 |
127 | public void setId(String id) {
128 | this.id = id;
129 | }
130 |
131 | @Override
132 | public String getContainer() {
133 | return pool;
134 | }
135 |
136 | public void setPool(String pool) {
137 | this.pool = pool;
138 | }
139 |
140 | public String getMetadataPool() {
141 | return metadataPool;
142 | }
143 |
144 | public void setMetadataPool(String metadataPool) {
145 | this.metadataPool = metadataPool;
146 | }
147 |
148 | public String getClusterName() {
149 | return clusterName;
150 | }
151 |
152 | public void setClusterName(String clusterName) {
153 | this.clusterName = clusterName;
154 | }
155 |
156 | public Rados getRados() {
157 | return rados;
158 | }
159 |
160 | public void setRados(Rados rados) {
161 | this.rados = rados;
162 | }
163 |
164 | public int getWritebufferSize() {
165 | return writebufferSize;
166 | }
167 |
168 | public void setWritebufferSize(int writebufferSize) {
169 | this.writebufferSize = writebufferSize;
170 | }
171 |
172 | public int getReadbufferSize() {
173 | return readbufferSize;
174 | }
175 |
176 | public void setReadbufferSize(int readbufferSize) {
177 | this.readbufferSize = readbufferSize;
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/alfresco-ceph-rados/src/main/java/fr/jeci/alfresco/ceph/RadosWritableByteChannel.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.ceph;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.io.IOException;
20 | import java.nio.ByteBuffer;
21 | import java.nio.channels.WritableByteChannel;
22 | import java.nio.channels.spi.AbstractInterruptibleChannel;
23 |
24 | import org.alfresco.service.cmr.repository.ContentIOException;
25 | import org.apache.commons.logging.Log;
26 | import org.apache.commons.logging.LogFactory;
27 |
28 | import com.ceph.rados.IoCTX;
29 | import com.ceph.rados.Rados;
30 | import com.ceph.rados.exceptions.RadosException;
31 |
32 | public class RadosWritableByteChannel extends AbstractInterruptibleChannel implements WritableByteChannel {
33 | private static final Log logger = LogFactory.getLog(RadosWritableByteChannel.class);
34 |
35 | private IoCTX io = null;
36 |
37 | private byte[] buf = new byte[0];
38 | // 128k
39 | private static final int TRANSFER_SIZE = 131072;
40 | private Object writeLock = new Object();
41 | private final String locator;
42 | private final Rados rados;
43 | private long offset = 0L;
44 |
45 | public RadosWritableByteChannel(Rados rados, String pool, String locator) {
46 | this.locator = locator;
47 | this.rados = rados;
48 | try {
49 | this.io = this.rados.ioCtxCreate(pool);
50 | } catch (RadosException e) {
51 | throw new ContentIOException("Error creatinf IO Context", e);
52 | }
53 | }
54 |
55 | @Override
56 | public int write(ByteBuffer src) throws IOException {
57 | int len = src.remaining();
58 | int totalWritten = 0;
59 | synchronized (this.writeLock) {
60 | long start = System.currentTimeMillis();
61 |
62 | try {
63 | while (totalWritten < len) {
64 | int bytesToWrite = Math.min((len - totalWritten), TRANSFER_SIZE);
65 | if (this.buf.length != bytesToWrite) {
66 | this.buf = new byte[bytesToWrite];
67 | }
68 |
69 | src.get(this.buf, 0, bytesToWrite);
70 | try {
71 | begin();
72 | this.io.write(this.locator, this.buf, this.offset);
73 | this.offset += bytesToWrite;
74 | } finally {
75 | end(bytesToWrite > 0);
76 | }
77 | totalWritten += bytesToWrite;
78 | }
79 |
80 | if (this.offset == 0) {
81 | /* Create empty object */
82 | this.io.write(this.locator, "", this.offset);
83 | }
84 |
85 | if (logger.isDebugEnabled()) {
86 | logger.debug("# Rados write " + this.locator + " " + totalWritten + " bytes in "
87 | + (System.currentTimeMillis() - start) + " ms");
88 | }
89 |
90 | } catch (RadosException e) {
91 | throw new IOException(e);
92 | }
93 | return totalWritten;
94 | }
95 | }
96 |
97 | @Override
98 | protected void implCloseChannel() throws IOException {
99 | if (this.io != null) {
100 | this.rados.ioCtxDestroy(this.io);
101 | }
102 | }
103 |
104 | public long getOffset() {
105 | return offset;
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/alfresco-object-storage-commons/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | alfresco-object-storage-commons
6 | 1.0.0
7 | Object Storage Commons Libs
8 | jar
9 | Object Storage Commons Libs for Alfresco connectors
10 |
11 |
12 | fr.jeci.alfresco-object-storage
13 | alfresco-object-storage-parent
14 | 1.0.0
15 |
16 |
17 |
18 |
19 | GNU Lesser General Public License v3.0 or later
20 | http://www.gnu.org/licenses/lgpl-3.0-standalone.html
21 |
22 |
23 |
24 |
25 |
26 | ${alfresco.groupId}
27 | alfresco-repository
28 |
29 |
30 |
31 | io.netty
32 | netty-buffer
33 | 4.1.4.Final
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/alfresco-object-storage-commons/src/main/java/fr/jeci/alfresco/ByteBufWritableByteChannel.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.io.IOException;
20 | import java.nio.ByteBuffer;
21 | import java.nio.channels.WritableByteChannel;
22 | import java.nio.channels.spi.AbstractInterruptibleChannel;
23 |
24 | import io.netty.buffer.ByteBuf;
25 | import io.netty.buffer.Unpooled;
26 |
27 | public abstract class ByteBufWritableByteChannel extends AbstractInterruptibleChannel implements WritableByteChannel {
28 |
29 | private ByteBuf buffer = Unpooled.buffer(0, Integer.MAX_VALUE);
30 |
31 | private long size = 0L;
32 |
33 | @Override
34 | public int write(ByteBuffer src) throws IOException {
35 | int start = this.buffer.writerIndex();
36 | this.buffer.writeBytes(src);
37 | int written = this.buffer.writerIndex() - start;
38 | this.size += written;
39 | return written;
40 | }
41 |
42 | @Override
43 | protected void implCloseChannel() throws IOException {
44 | try {
45 | implCloseChannel(this.buffer);
46 | } finally {
47 | this.buffer.release();
48 | }
49 | }
50 |
51 | /**
52 | * @return Total byte written
53 | */
54 | public long getSize() {
55 | return size;
56 | }
57 |
58 | protected abstract void implCloseChannel(ByteBuf buffer);
59 |
60 | }
--------------------------------------------------------------------------------
/alfresco-object-storage-commons/src/main/java/fr/jeci/alfresco/ObjectStorageContentStore.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.io.IOException;
20 | import java.io.Serializable;
21 | import java.util.Collections;
22 | import java.util.concurrent.locks.ReentrantReadWriteLock;
23 | import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
24 |
25 | import org.alfresco.repo.content.AbstractContentStore;
26 | import org.alfresco.repo.content.ContentStoreCreatedEvent;
27 | import org.alfresco.service.cmr.repository.ContentReader;
28 | import org.alfresco.service.cmr.repository.ContentWriter;
29 | import org.alfresco.util.GUID;
30 | import org.apache.commons.lang3.StringUtils;
31 | import org.apache.commons.logging.Log;
32 | import org.apache.commons.logging.LogFactory;
33 | import org.springframework.context.ApplicationContext;
34 | import org.springframework.context.ApplicationContextAware;
35 | import org.springframework.context.ApplicationEvent;
36 | import org.springframework.context.ApplicationListener;
37 | import org.springframework.context.event.ContextRefreshedEvent;
38 |
39 | /**
40 | * Common ContentStore to all commector implementation.
41 | *
42 | * @author jeremie.lesage
43 | *
44 | */
45 | public class ObjectStorageContentStore extends AbstractContentStore
46 | implements ApplicationContextAware, ApplicationListener {
47 | private static final Log logger = LogFactory.getLog(ObjectStorageContentStore.class);
48 |
49 | // NUM_LOCKS absolutely must be a power of 2 for the use of locks to be
50 | // evenly balanced
51 | private static final int NUM_LOCKS = 256;
52 | private static final ReentrantReadWriteLock[] LOCKS;
53 |
54 | private ObjectStorageService objectStorageService;
55 | private ApplicationContext applicationContext;
56 |
57 | private String storeProtocole = null;
58 |
59 | static {
60 | LOCKS = new ReentrantReadWriteLock[NUM_LOCKS];
61 | for (int i = 0; i < NUM_LOCKS; i++) {
62 | LOCKS[i] = new ReentrantReadWriteLock();
63 | }
64 | }
65 |
66 | @Override
67 | public boolean isWriteSupported() {
68 | return this.objectStorageService.isWriteSupported();
69 | }
70 |
71 | @Override
72 | public ContentReader getReader(String contentUrl) {
73 | if (logger.isDebugEnabled()) {
74 | logger.debug(String.format("Content Reader for %s", contentUrl));
75 | }
76 |
77 | // Use pool of locks - which one is determined by a hash of the URL.
78 | // This will stop the content from being read/cached multiple times from
79 | // the backing store
80 | // when it should only be read once - cached versions should be returned
81 | // after that.
82 | ReadLock readLock = readWriteLock(contentUrl).readLock();
83 | readLock.lock();
84 | try {
85 | return this.objectStorageService.getReader(contentUrl);
86 | } catch (IOException e) {
87 | logger.error(e.getMessage(), e);
88 | } finally {
89 | readLock.unlock();
90 | }
91 |
92 | return null;
93 | }
94 |
95 | @Override
96 | protected ContentWriter getWriterInternal(final ContentReader existingContentReader, final String newContentUrl) {
97 | String contentUrl = newContentUrl;
98 | if (StringUtils.isBlank(contentUrl)) {
99 | contentUrl = createNewUrl();
100 | }
101 | if (logger.isDebugEnabled()) {
102 | logger.debug(String.format("ContentWriter for %s", contentUrl));
103 | }
104 | try {
105 | return this.objectStorageService.getWriter(contentUrl);
106 | } catch (IOException e) {
107 | logger.error(e.getMessage(), e);
108 | }
109 |
110 | return null;
111 | }
112 |
113 | @Override
114 | public boolean isContentUrlSupported(String contentUrl) {
115 | String startUrl = String.format("%s://%s/", this.storeProtocole, this.objectStorageService.getContainer());
116 | return contentUrl.startsWith(startUrl);
117 | }
118 |
119 | protected String createNewUrl() {
120 | return String.format("%s://%s/%s", this.storeProtocole, this.objectStorageService.getContainer(),
121 | GUID.generate());
122 | }
123 |
124 | @Override
125 | public boolean delete(String contentUrl) {
126 | if (logger.isDebugEnabled()) {
127 | logger.debug(String.format("Delete %s", contentUrl));
128 | }
129 | ReentrantReadWriteLock readWriteLock = readWriteLock(contentUrl);
130 | ReadLock readLock = readWriteLock.readLock();
131 | readLock.lock();
132 | try {
133 | return this.objectStorageService.delete(contentUrl);
134 | } finally {
135 | readLock.unlock();
136 | }
137 | }
138 |
139 | @Override
140 | public void onApplicationEvent(ApplicationEvent event) {
141 | // Once the context has been refreshed, we tell other interested beans
142 | // about the existence of this content store
143 | // (e.g. for monitoring purposes)
144 | if (event instanceof ContextRefreshedEvent && event.getSource() == this.applicationContext) {
145 | ApplicationContext context = ((ContextRefreshedEvent) event).getApplicationContext();
146 | context.publishEvent(new ContentStoreCreatedEvent(this, Collections.emptyMap()));
147 | }
148 | }
149 |
150 | /**
151 | * Get a ReentrantReadWriteLock for a given URL. The lock is from a pool
152 | * rather than per URL, so some contention is expected.
153 | *
154 | * @param url
155 | * String
156 | * @return ReentrantReadWriteLock
157 | */
158 | public ReentrantReadWriteLock readWriteLock(String url) {
159 | return LOCKS[lockIndex(url)];
160 | }
161 |
162 | private int lockIndex(String url) {
163 | return url.hashCode() & (NUM_LOCKS - 1);
164 | }
165 |
166 | @Override
167 | public void setApplicationContext(ApplicationContext applicationContext) {
168 | this.applicationContext = applicationContext;
169 | }
170 |
171 | public void setObjectStorageService(ObjectStorageService objectStorageService) {
172 | this.objectStorageService = objectStorageService;
173 | }
174 |
175 | public void setStoreProtocole(String storeProtocole) {
176 | this.storeProtocole = storeProtocole;
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/alfresco-object-storage-commons/src/main/java/fr/jeci/alfresco/ObjectStorageService.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.io.IOException;
20 |
21 | import org.alfresco.service.cmr.repository.ContentReader;
22 | import org.alfresco.service.cmr.repository.ContentWriter;
23 |
24 | /**
25 | * Common Interface for all ObjectStorage implementation
26 | *
27 | * @author jeremie.lesage
28 | *
29 | */
30 | public interface ObjectStorageService {
31 |
32 | ContentReader getReader(String contentUrl) throws IOException;
33 |
34 | boolean isWriteSupported();
35 |
36 | boolean delete(String contentUrl);
37 |
38 | ContentWriter getWriter(String contentUrl) throws IOException;
39 |
40 | /**
41 | * Storage Container or Pool
42 | *
43 | * @return
44 | */
45 | String getContainer();
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/alfresco-openio/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM jeci/alfresco-platform:5.2.f
2 | MAINTAINER Jeremie Lesage
3 |
4 | COPY target/alfresco-openio-*.amp /root/amp/
5 |
6 | RUN java -jar /root/alfresco-mmt.jar install /root/amp/ /usr/local/tomcat/webapps/alfresco -nobackup -directory -force \
7 | && rm -r /usr/local/tomcat/webapps/alfresco/WEB-INF/classes/alfresco/module/backup
8 |
9 | COPY assets/alfresco-global.properties /usr/local/tomcat/shared/classes/alfresco-global.properties
10 |
--------------------------------------------------------------------------------
/alfresco-openio/assets/alfresco-global.properties:
--------------------------------------------------------------------------------
1 | ## OpenIO Configuration
2 |
3 | openio.target=http://openio:6006
4 | openio.namespace=OPENIO
5 | openio.account=alfresco
6 | openio.container=alfresco
7 |
--------------------------------------------------------------------------------
/alfresco-openio/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | # CONFIGURATION POUR VPS SSD 3 de OVH
3 | # 2vCores, 8Go RAM, 40Go SSD
4 | volumes:
5 | alf_logs:
6 | alf_data:
7 | share_logs:
8 | solr_logs:
9 | solr_data:
10 | pgsql_data:
11 |
12 |
13 | networks:
14 | alf-share:
15 | internal:
16 | driver: bridge
17 | ipam:
18 | driver: default
19 | config:
20 | - subnet: 192.168.168.0/24
21 | gateway: 192.168.168.1
22 |
23 | services:
24 | alfresco:
25 | image: jeci/alfresco-platform-openio
26 | environment:
27 | CATALINA_OPTS: "-Xms512m -Xmx1G -XX:+UseConcMarkSweepGC "
28 | depends_on:
29 | - postgresql
30 | - libreoffice
31 | - openio
32 | ulimits:
33 | nproc: 65535
34 | nofile:
35 | soft: 20000
36 | hard: 40000
37 | networks:
38 | - alf-share
39 | - internal
40 | ports:
41 | - "8081:8080"
42 | volumes:
43 | - "alf_logs:/usr/local/tomcat/logs/"
44 | - "alf_data:/opt/alf_data"
45 | tmpfs:
46 | - /tmp
47 | - /usr/local/tomcat/temp/:exec
48 | - /usr/local/tomcat/work/
49 |
50 | share:
51 | image: jeci/alfresco-share:5.2.e
52 | restart: always
53 | environment:
54 | CATALINA_OPTS: "-Xms512m -Xmx1G -XX:+UseConcMarkSweepGC "
55 | depends_on:
56 | - alfresco
57 | networks:
58 | - alf-share
59 | ports:
60 | - "8080:8080"
61 | volumes:
62 | - "share_logs:/usr/local/tomcat/logs/"
63 | tmpfs:
64 | - /tmp
65 | - /usr/local/tomcat/temp/
66 | - /usr/local/tomcat/work/
67 |
68 | solr:
69 | image: jeci/alfresco-solr:5.2.f
70 | environment:
71 | CATALINA_OPTS: "-Xms512m -Xmx1G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:G1HeapRegionSize=8m -XX:MaxGCPauseMillis=200 "
72 | depends_on:
73 | - alfresco
74 | ulimits:
75 | nproc: 65535
76 | nofile:
77 | soft: 20000
78 | hard: 40000
79 | networks:
80 | - internal
81 | volumes:
82 | - "solr_logs:/usr/local/tomcat/logs/"
83 | - "solr_data:/opt/solr_data"
84 | tmpfs:
85 | - /tmp
86 | - /usr/local/tomcat/temp/
87 | - /usr/local/tomcat/work/
88 |
89 | postgresql:
90 | image: postgres:9.4
91 | environment:
92 | - POSTGRES_DB=alfresco
93 | - POSTGRES_PASSWORD=alfresco
94 | - POSTGRES_USER=alfresco
95 | networks:
96 | - internal
97 | volumes:
98 | - "pgsql_data:/var/lib/postgresql/data"
99 |
100 | libreoffice:
101 | image: jeci/alfresco-libreoffice:5.2.7
102 | restart: always
103 | user: root
104 | networks:
105 | - internal
106 |
107 | openio:
108 | image: openio/sds
109 | container_name: openio-service
110 | environment:
111 | OIO_NS: OPENIO
112 | OIO_ACCOUNT: alfresco
113 | OPENIO_IPADDR: 192.168.168.10
114 | networks:
115 | internal:
116 | ipv4_address: 192.168.168.10
117 | expose:
118 | - "6006"
119 | stdin_open: true
120 | tty: true
121 |
--------------------------------------------------------------------------------
/alfresco-openio/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | alfresco-openio
6 | 1.0
7 | OpenIO Connector for Alfresco
8 | amp
9 | OpenIO module connector for Alfresco
10 |
11 |
12 | fr.jeci.alfresco-object-storage
13 | alfresco-object-storage-parent
14 | 1.0.0
15 |
16 |
17 |
18 |
19 | GNU Lesser General Public License v3.0 or later
20 | http://www.gnu.org/licenses/lgpl-3.0-standalone.html
21 |
22 |
23 |
24 |
25 |
26 | fr.jeci.alfresco-object-storage
27 | alfresco-object-storage-commons
28 | 1.0.0
29 |
30 |
31 |
32 | ${alfresco.groupId}
33 | alfresco-repository
34 |
35 |
36 |
37 | io.openio.sds
38 | openio-api
39 | 0.4.0
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/alfresco-openio/src/main/amp/config/alfresco/module/alfresco-openio/alfresco-global.properties:
--------------------------------------------------------------------------------
1 | ## OpenIO Configuration
2 |
3 | openio.target=http://127.0.0.1:6006
4 | openio.namespace=OPENIO
5 | openio.account=alfresco
6 | openio.container=alfresco
7 |
--------------------------------------------------------------------------------
/alfresco-openio/src/main/amp/config/alfresco/module/alfresco-openio/caching-context.xml.sample:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
43 |
44 |
45 |
46 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | org.alfresco.repo.content.caching.cleanup.CachedContentCleanupJob
58 |
59 |
60 |
65 |
66 |
67 |
68 |
71 |
72 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | ${system.content.caching.contentCleanup.cronExpression}
87 |
88 |
89 |
90 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/alfresco-openio/src/main/amp/config/alfresco/module/alfresco-openio/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.logger.fr.jeci.alfresco.openio=INFO
2 |
--------------------------------------------------------------------------------
/alfresco-openio/src/main/amp/config/alfresco/module/alfresco-openio/module-context.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/alfresco-openio/src/main/amp/module.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 |
17 |
18 | # SDK Sample module
19 |
20 | # ==== Beginning of Alfresco required/optional properties ====== #
21 | # NB: These properties are filtered at build time by Maven, single
22 | # sourcing from POM properties
23 | module.id=${project.artifactId}
24 | #module.aliases=myModule-123, my-module
25 | module.title=${project.name}
26 | module.description=${project.description}
27 | module.version=${project.version}
28 |
29 | # The following optional properties can be used to prevent the module from being added
30 | # to inappropriate versions of the WAR file.
31 | # module.repo.version.min=2.0
32 | # module.repo.version.max=2.1
33 |
34 | # FIXME: This dependencies should come out of mvn dependencies on amp
35 |
36 | # The following describe dependencies on other modules
37 | # Depends on net.sf.myproject.module.SupportModuleA version ${version} or later
38 | # module.depends.net.sf.myproject.module.SupportModuleA=${version}-*
39 | # Depends on net.sf.myproject.module.SupportModuleA version ${version} to 2.0
40 | # module.depends.net.sf.myproject.module.SupportModuleB=${version}-2.0
41 | # Depends on net.sf.myproject.module.SupportModuleC - any version
42 | # module.depends.net.sf.myproject.module.SupportModuleB=*
43 |
44 |
45 | # ==== End of Alfresco required/optional properties ======= #
46 |
47 |
48 | # ==== Beginning of module required properties/optional ====== #
--------------------------------------------------------------------------------
/alfresco-openio/src/main/java/fr/jeci/alfresco/openio/OpenIOChannel.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.openio;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import fr.jeci.alfresco.ByteBufWritableByteChannel;
20 | import io.netty.buffer.ByteBuf;
21 | import io.netty.buffer.ByteBufInputStream;
22 | import io.openio.sds.Client;
23 | import io.openio.sds.models.OioUrl;
24 |
25 | public class OpenIOChannel extends ByteBufWritableByteChannel {
26 |
27 | private Client client;
28 | private OioUrl oioUrl;
29 |
30 | public OpenIOChannel(Client client, OioUrl oioUrl) {
31 | this.client = client;
32 | this.oioUrl = oioUrl;
33 | }
34 |
35 | @Override
36 | protected void implCloseChannel(ByteBuf buffer) {
37 | long size = (long) (buffer.writerIndex() - buffer.readerIndex());
38 | this.client.putObject(oioUrl, size, new ByteBufInputStream(buffer));
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/alfresco-openio/src/main/java/fr/jeci/alfresco/openio/OpenIOContentReader.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.openio;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.nio.channels.Channels;
20 | import java.nio.channels.ReadableByteChannel;
21 |
22 | import org.alfresco.repo.content.AbstractContentReader;
23 | import org.alfresco.service.cmr.repository.ContentIOException;
24 | import org.alfresco.service.cmr.repository.ContentReader;
25 | import org.apache.commons.logging.Log;
26 | import org.apache.commons.logging.LogFactory;
27 |
28 | import io.openio.sds.Client;
29 | import io.openio.sds.models.ObjectInfo;
30 | import io.openio.sds.models.OioUrl;
31 |
32 | /**
33 | * Created by manens on 23/07/16.
34 | */
35 | public class OpenIOContentReader extends AbstractContentReader {
36 | private static final Log logger = LogFactory.getLog(OpenIOContentReader.class);
37 |
38 | private Client client;
39 | private OioUrl oioUrl;
40 |
41 | protected OpenIOContentReader(Client client, OioUrl urlFile) {
42 | super(urlFile.object());
43 | this.client = client;
44 | this.oioUrl = urlFile;
45 | }
46 |
47 | @Override
48 | protected ContentReader createReader() {
49 | if (logger.isDebugEnabled()) {
50 | logger.debug("Called createReader for contentUrl -> " + getContentUrl());
51 | }
52 | return new OpenIOContentReader(this.client, this.oioUrl);
53 | }
54 |
55 | @Override
56 | protected ReadableByteChannel getDirectReadableChannel() {
57 | if (!exists()) {
58 | throw new ContentIOException("Content object does not exist on OpenIO");
59 | }
60 |
61 | try {
62 | final ObjectInfo fileObjectMetadata = client.getObjectInfo(this.oioUrl);
63 | return Channels.newChannel(this.client.downloadObject(fileObjectMetadata));
64 | } catch (Exception e) {
65 | throw new ContentIOException("Unable to retrieve content object from OpenIO", e);
66 | }
67 |
68 | }
69 |
70 | @Override
71 | public boolean exists() {
72 | return client.getObjectInfo(this.oioUrl) != null;
73 | }
74 |
75 | @Override
76 | public long getLastModified() {
77 | if (!exists()) {
78 | return 0L;
79 | }
80 |
81 | final ObjectInfo fileObjectMetadata = client.getObjectInfo(this.oioUrl);
82 | return fileObjectMetadata.ctime();
83 | }
84 |
85 | @Override
86 | public long getSize() {
87 | if (!exists()) {
88 | return 0L;
89 | }
90 |
91 | final ObjectInfo fileObjectMetadata = client.getObjectInfo(this.oioUrl);
92 | return fileObjectMetadata.size();
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/alfresco-openio/src/main/java/fr/jeci/alfresco/openio/OpenIOContentWriter.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.openio;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.nio.channels.WritableByteChannel;
20 |
21 | import org.alfresco.repo.content.AbstractContentWriter;
22 | import org.alfresco.service.cmr.repository.ContentReader;
23 |
24 | import io.openio.sds.Client;
25 | import io.openio.sds.models.OioUrl;
26 |
27 | /**
28 | * Created by manens on 23/07/16.
29 | */
30 | public class OpenIOContentWriter extends AbstractContentWriter {
31 | private Client client;
32 | private OioUrl oioUrl;
33 |
34 | private final OpenIOChannel channel;
35 |
36 | protected OpenIOContentWriter(Client client, OioUrl urlFile) {
37 | super(urlFile.object(), null);
38 | this.client = client;
39 | this.oioUrl = urlFile;
40 |
41 | this.channel = new OpenIOChannel(this.client, this.oioUrl);
42 | }
43 |
44 | @Override
45 | protected ContentReader createReader() {
46 | return new OpenIOContentReader(this.client, this.oioUrl);
47 | }
48 |
49 | @Override
50 | protected WritableByteChannel getDirectWritableChannel() {
51 | return this.channel;
52 | }
53 |
54 | @Override
55 | public long getSize() {
56 | return this.channel.getSize();
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/alfresco-openio/src/main/java/fr/jeci/alfresco/openio/OpenIOServiceImpl.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.openio;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.io.IOException;
20 |
21 | import org.alfresco.service.cmr.repository.ContentReader;
22 | import org.alfresco.service.cmr.repository.ContentWriter;
23 | import org.apache.commons.logging.Log;
24 | import org.apache.commons.logging.LogFactory;
25 |
26 | import fr.jeci.alfresco.ObjectStorageService;
27 | import io.openio.sds.Client;
28 | import io.openio.sds.ClientBuilder;
29 | import io.openio.sds.Settings;
30 | import io.openio.sds.models.OioUrl;
31 |
32 | public class OpenIOServiceImpl implements ObjectStorageService {
33 | private static final Log logger = LogFactory.getLog(OpenIOServiceImpl.class);
34 |
35 | private String target;
36 | private String namespace;
37 | private String account;
38 | private Client client;
39 | private String container;
40 |
41 | public void init() {
42 | Settings settings = new Settings();
43 | settings.proxy().ns(namespace).url(target);
44 | client = ClientBuilder.newClient(settings);
45 | }
46 |
47 | @Override
48 | public boolean isWriteSupported() {
49 | // this.container.getcontainerWritePermission()
50 | return true;
51 | }
52 |
53 | @Override
54 | public ContentReader getReader(String contentUrl) throws IOException {
55 | if (logger.isDebugEnabled()) {
56 | logger.debug("GETTING OBJECT - BUCKET: " + container + " KEY: " + contentUrl);
57 | }
58 | OioUrl urlFile = OioUrl.url(account, container, contentUrl);
59 | return new OpenIOContentReader(this.client, urlFile);
60 | }
61 |
62 | @Override
63 | public ContentWriter getWriter(String contentUrl) throws IOException {
64 | OioUrl urlFile = OioUrl.url(account, container, contentUrl);
65 | return new OpenIOContentWriter(client, urlFile);
66 | }
67 |
68 | @Override
69 | public boolean delete(String contentUrl) {
70 | try {
71 | OioUrl urlFile = OioUrl.url(account, container, contentUrl);
72 | client.deleteObject(urlFile);
73 | if (logger.isDebugEnabled()) {
74 | logger.debug("Deleting object from OpenIO with url: " + contentUrl);
75 | }
76 | return true;
77 | } catch (Exception e) {
78 | logger.error("Error deleting OpenIO Object", e);
79 | }
80 | return false;
81 | }
82 |
83 | public String getTarget() {
84 | return target;
85 | }
86 |
87 | public void setTarget(String target) {
88 | this.target = target;
89 | }
90 |
91 | public String getNamespace() {
92 | return namespace;
93 | }
94 |
95 | public void setNamespace(String namespace) {
96 | this.namespace = namespace;
97 | }
98 |
99 | public String getAccount() {
100 | return account;
101 | }
102 |
103 | public void setAccount(String account) {
104 | this.account = account;
105 | }
106 |
107 | public Client getClient() {
108 | return client;
109 | }
110 |
111 | public void setClient(Client client) {
112 | this.client = client;
113 | }
114 |
115 | @Override
116 | public String getContainer() {
117 | return container;
118 | }
119 |
120 | public void setContainer(String container) {
121 | this.container = container;
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/alfresco-swift/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM jeci/alfresco-platform:5.2.f
2 | MAINTAINER Jeremie Lesage
3 |
4 | COPY target/alfresco-swift-*.amp /root/amp/
5 |
6 | RUN java -jar /root/alfresco-mmt.jar install /root/amp/ /usr/local/tomcat/webapps/alfresco -nobackup -directory -force \
7 | && rm -r /usr/local/tomcat/webapps/alfresco/WEB-INF/classes/alfresco/module/backup
8 |
9 | COPY assets/alfresco-global.properties /usr/local/tomcat/shared/classes/alfresco-global.properties
10 |
--------------------------------------------------------------------------------
/alfresco-swift/assets/alfresco-global.properties:
--------------------------------------------------------------------------------
1 | ## Swift Configuration
2 |
3 | swift.username=user:swift
4 | swift.password=abcdefghijklmnop
5 | swift.url=https://s3.example.org/auth/1.0
6 | swift.tenantId=
7 | swift.tenantName=
8 | swift.containerName=alfresco
9 |
--------------------------------------------------------------------------------
/alfresco-swift/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | alfresco-swift
6 | 1.0
7 | Swift Connector for Alfresco
8 | amp
9 | OpenStack Swift module connector for Alfresco
10 |
11 |
12 | fr.jeci.alfresco-object-storage
13 | alfresco-object-storage-parent
14 | 1.0.0
15 |
16 |
17 |
18 |
19 | GNU Lesser General Public License v3.0 or later
20 | http://www.gnu.org/licenses/lgpl-3.0-standalone.html
21 |
22 |
23 |
24 |
25 |
26 | fr.jeci.alfresco-object-storage
27 | alfresco-object-storage-commons
28 | 1.0.0
29 |
30 |
31 |
32 | ${alfresco.groupId}
33 | alfresco-repository
34 |
35 |
36 |
37 | org.javaswift
38 | joss
39 | 0.9.7
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/alfresco-swift/src/main/amp/config/alfresco/module/alfresco-swift/alfresco-global.properties:
--------------------------------------------------------------------------------
1 | ## Swift Configuration
2 |
3 | swift.username=user:swift
4 | swift.password=abcdefghijklmnop
5 | swift.url=https://s3.example.org/auth/1.0
6 | swift.tenantId=
7 | swift.tenantName=
8 | swift.containerName=alfresco
9 |
--------------------------------------------------------------------------------
/alfresco-swift/src/main/amp/config/alfresco/module/alfresco-swift/caching-context.xml.sample:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | org.alfresco.repo.content.caching.cleanup.CachedContentCleanupJob
68 |
69 |
70 |
75 |
76 |
77 |
78 |
81 |
82 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | ${system.content.caching.contentCleanup.cronExpression}
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/alfresco-swift/src/main/amp/config/alfresco/module/alfresco-swift/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.logger.fr.jeci.alfresco.swift=INFO
2 |
--------------------------------------------------------------------------------
/alfresco-swift/src/main/amp/config/alfresco/module/alfresco-swift/module-context.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/alfresco-swift/src/main/amp/module.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 |
17 |
18 | # SDK Sample module
19 |
20 | # ==== Beginning of Alfresco required/optional properties ====== #
21 | # NB: These properties are filtered at build time by Maven, single
22 | # sourcing from POM properties
23 | module.id=${project.artifactId}
24 | #module.aliases=myModule-123, my-module
25 | module.title=${project.name}
26 | module.description=${project.description}
27 | module.version=${project.version}
28 |
29 | # The following optional properties can be used to prevent the module from being added
30 | # to inappropriate versions of the WAR file.
31 | # module.repo.version.min=2.0
32 | # module.repo.version.max=2.1
33 |
34 | # FIXME: This dependencies should come out of mvn dependencies on amp
35 |
36 | # The following describe dependencies on other modules
37 | # Depends on net.sf.myproject.module.SupportModuleA version ${version} or later
38 | # module.depends.net.sf.myproject.module.SupportModuleA=${version}-*
39 | # Depends on net.sf.myproject.module.SupportModuleA version ${version} to 2.0
40 | # module.depends.net.sf.myproject.module.SupportModuleB=${version}-2.0
41 | # Depends on net.sf.myproject.module.SupportModuleC - any version
42 | # module.depends.net.sf.myproject.module.SupportModuleB=*
43 |
44 |
45 | # ==== End of Alfresco required/optional properties ======= #
46 |
47 |
48 | # ==== Beginning of module required properties/optional ====== #
--------------------------------------------------------------------------------
/alfresco-swift/src/main/java/fr/jeci/alfresco/swift/SwiftChannel.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.swift;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import org.javaswift.joss.model.StoredObject;
20 |
21 | import fr.jeci.alfresco.ByteBufWritableByteChannel;
22 | import io.netty.buffer.ByteBuf;
23 | import io.netty.buffer.ByteBufInputStream;
24 |
25 | public class SwiftChannel extends ByteBufWritableByteChannel {
26 |
27 | private StoredObject storedObject;
28 |
29 | public SwiftChannel(StoredObject storedObject) {
30 | this.storedObject = storedObject;
31 | }
32 |
33 | @Override
34 | protected void implCloseChannel(ByteBuf buffer) {
35 | storedObject.uploadObject(new ByteBufInputStream(buffer));
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/alfresco-swift/src/main/java/fr/jeci/alfresco/swift/SwiftContentReader.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.swift;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.nio.channels.Channels;
20 | import java.nio.channels.ReadableByteChannel;
21 |
22 | import org.alfresco.repo.content.AbstractContentReader;
23 | import org.alfresco.service.cmr.repository.ContentIOException;
24 | import org.alfresco.service.cmr.repository.ContentReader;
25 | import org.javaswift.joss.model.StoredObject;
26 |
27 | public class SwiftContentReader extends AbstractContentReader implements ContentReader {
28 |
29 | private StoredObject storedObject;
30 |
31 | public SwiftContentReader(StoredObject object) {
32 | super(object.getName());
33 | this.storedObject = object;
34 | }
35 |
36 | @Override
37 | protected ContentReader createReader() {
38 | return new SwiftContentReader(this.storedObject);
39 | }
40 |
41 | @Override
42 | protected ReadableByteChannel getDirectReadableChannel() {
43 | if (!exists()) {
44 | throw new ContentIOException("Content object does not exist on Swift");
45 | }
46 |
47 | return Channels.newChannel(this.storedObject.downloadObjectAsInputStream());
48 | }
49 |
50 | @Override
51 | public boolean exists() {
52 | return this.storedObject.exists();
53 | }
54 |
55 | @Override
56 | public long getLastModified() {
57 | if (!exists()) {
58 | return 0L;
59 | }
60 |
61 | return this.storedObject.getLastModifiedAsDate().getTime();
62 | }
63 |
64 | @Override
65 | public long getSize() {
66 | if (!exists()) {
67 | return 0L;
68 | }
69 |
70 | return this.storedObject.getContentLength();
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/alfresco-swift/src/main/java/fr/jeci/alfresco/swift/SwiftContentWriter.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.swift;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.nio.channels.WritableByteChannel;
20 |
21 | import org.alfresco.repo.content.AbstractContentWriter;
22 | import org.alfresco.service.cmr.repository.ContentReader;
23 | import org.javaswift.joss.model.StoredObject;
24 |
25 | public class SwiftContentWriter extends AbstractContentWriter {
26 | private StoredObject storedObject;
27 |
28 | private final SwiftChannel channel;
29 |
30 | public SwiftContentWriter(StoredObject object) {
31 | super(object.getName(), null);
32 | this.storedObject = object;
33 | this.channel = new SwiftChannel(storedObject);
34 |
35 | }
36 |
37 | @Override
38 | protected ContentReader createReader() {
39 | return new SwiftContentReader(this.storedObject);
40 | }
41 |
42 | @Override
43 | protected WritableByteChannel getDirectWritableChannel() {
44 | return new SwiftChannel(storedObject);
45 | }
46 |
47 | @Override
48 | public long getSize() {
49 | return this.channel.getSize();
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/alfresco-swift/src/main/java/fr/jeci/alfresco/swift/SwiftServiceImpl.java:
--------------------------------------------------------------------------------
1 | package fr.jeci.alfresco.swift;
2 |
3 | /* Copyright 2016, 2017 - Jeci SARL - http://jeci.fr
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program 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
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see http://www.gnu.org/licenses/.
17 | */
18 |
19 | import java.io.IOException;
20 |
21 | import org.alfresco.service.cmr.repository.ContentReader;
22 | import org.alfresco.service.cmr.repository.ContentWriter;
23 | import org.apache.commons.lang3.StringUtils;
24 | import org.javaswift.joss.client.factory.AccountConfig;
25 | import org.javaswift.joss.client.factory.AccountFactory;
26 | import org.javaswift.joss.client.factory.AuthenticationMethod;
27 | import org.javaswift.joss.model.Account;
28 | import org.javaswift.joss.model.Container;
29 | import org.javaswift.joss.model.StoredObject;
30 |
31 | import fr.jeci.alfresco.ObjectStorageService;
32 |
33 | public class SwiftServiceImpl implements ObjectStorageService {
34 |
35 | private String username;
36 | private String password;
37 | private String url;
38 | private String tenantId;
39 | private String tenantName;
40 | private String containerName;
41 | private Container container;
42 |
43 | public void init() {
44 |
45 | AccountConfig config = new AccountConfig();
46 | config.setAuthenticationMethod(AuthenticationMethod.BASIC);
47 | config.setUsername(username);
48 | config.setPassword(password);
49 | config.setAuthUrl(url);
50 | if (StringUtils.isNotEmpty(tenantId)) {
51 | config.setTenantId(tenantId);
52 | }
53 | if (StringUtils.isNotEmpty(tenantName)) {
54 | config.setTenantName(tenantName);
55 | }
56 | Account account = new AccountFactory(config).createAccount();
57 | this.container = account.getContainer(containerName);
58 | }
59 |
60 | @Override
61 | public boolean isWriteSupported() {
62 | // this.container.getcontainerWritePermission()
63 | return true;
64 | }
65 |
66 | @Override
67 | public ContentReader getReader(String contentUrl) throws IOException {
68 | StoredObject object = this.container.getObject(contentUrl);
69 | return new SwiftContentReader(object);
70 | }
71 |
72 | @Override
73 | public ContentWriter getWriter(String contentUrl) throws IOException {
74 | StoredObject object = this.container.getObject(contentUrl);
75 | return new SwiftContentWriter(object);
76 | }
77 |
78 | @Override
79 | public boolean delete(String contentUrl) {
80 | StoredObject object = this.container.getObject(contentUrl);
81 | object.delete();
82 | return true;
83 | }
84 |
85 | @Override
86 | public String getContainer() {
87 | return container.getName();
88 | }
89 |
90 | public String getUsername() {
91 | return username;
92 | }
93 |
94 | public void setUsername(String username) {
95 | this.username = username;
96 | }
97 |
98 | public String getPassword() {
99 | return password;
100 | }
101 |
102 | public void setPassword(String password) {
103 | this.password = password;
104 | }
105 |
106 | public String getUrl() {
107 | return url;
108 | }
109 |
110 | public void setUrl(String url) {
111 | this.url = url;
112 | }
113 |
114 | public String getTenantId() {
115 | return tenantId;
116 | }
117 |
118 | public void setTenantId(String tenantId) {
119 | this.tenantId = tenantId;
120 | }
121 |
122 | public String getTenantName() {
123 | return tenantName;
124 | }
125 |
126 | public void setTenantName(String tenantName) {
127 | this.tenantName = tenantName;
128 | }
129 |
130 | public void setContainer(String containerName) {
131 | this.containerName = containerName;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | fr.jeci.alfresco-object-storage
6 | 1.0.0
7 | alfresco-object-storage-parent
8 | pom
9 | Object Storage Connectors for Alfresco
10 | Object Storage Connectors (Swift, Red Hat Ceph, OpenIO) for Alfresco
11 |
12 | http://jeci.fr/
13 |
14 |
15 | alfresco-object-storage-commons
16 | alfresco-ceph-rados
17 | alfresco-openio
18 | alfresco-swift
19 |
20 |
21 |
22 | org.alfresco.maven
23 | alfresco-sdk-parent
24 | 2.2.0
25 |
26 |
27 |
28 |
29 | GNU Lesser General Public License v3.0 or later
30 | http://www.gnu.org/licenses/lgpl-3.0-standalone.html
31 |
32 |
33 |
34 |
35 |
36 |
37 | 5.2.f
38 | 1.8
39 |
40 |
41 |
42 |
43 |
44 | jeremie.lesage
45 | Jeremie Lesage
46 | jeremie.lesage@jeci.fr
47 | https://jeci.fr/
48 | Jeci
49 | https://jeci.fr/
50 | +1
51 |
52 |
53 |
54 |
55 |
56 | Florent Manens
57 |
58 |
59 | Beezim
60 | http://www.beezim.fr/
61 | +1
62 |
63 |
64 |
65 |
66 |
67 |
70 |
71 | ${alfresco.groupId}
72 | alfresco-platform-distribution
73 | ${alfresco.version}
74 | pom
75 | import
76 |
77 |
78 |
79 |
80 |
81 |
82 | enterprise
83 |
84 |
85 | ${alfresco.groupId}
86 | alfresco-enterprise-repository
87 | ${alfresco.version}
88 | provided
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------