1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.onehippo.forge.cmisreplication;
17
18 import java.io.IOException;
19 import java.util.HashMap;
20 import java.util.LinkedList;
21 import java.util.List;
22 import java.util.Map;
23
24 import javax.jcr.Node;
25 import javax.jcr.NodeIterator;
26 import javax.jcr.RepositoryException;
27 import javax.jcr.query.Query;
28 import javax.jcr.query.QueryManager;
29 import javax.jcr.query.QueryResult;
30
31 import org.apache.chemistry.opencmis.client.api.CmisObject;
32 import org.apache.chemistry.opencmis.client.api.Document;
33 import org.apache.chemistry.opencmis.client.api.Folder;
34 import org.apache.chemistry.opencmis.client.api.ItemIterable;
35 import org.apache.chemistry.opencmis.client.api.OperationContext;
36 import org.apache.chemistry.opencmis.client.api.Session;
37 import org.apache.chemistry.opencmis.client.api.SessionFactory;
38 import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
39 import org.apache.chemistry.opencmis.commons.SessionParameter;
40 import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
41 import org.apache.chemistry.opencmis.commons.enums.BindingType;
42 import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
43 import org.apache.commons.lang.StringUtils;
44 import org.onehippo.forge.cmisreplication.util.AssetMetadata;
45 import org.onehippo.forge.cmisreplication.util.AssetUtils;
46 import org.onehippo.forge.cmisreplication.util.CmisDocumentBinary;
47 import org.onehippo.forge.cmisreplication.util.Codecs;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 public class CmisDocumentsReplicator {
52
53 private static Logger log = LoggerFactory.getLogger(CmisDocumentsReplicator.class);
54
55 private javax.jcr.Session jcrSession;
56 private CmisRepoConfig cmisRepoConfig;
57 private HippoRepoConfig hippoRepoConfig;
58 private boolean migrateCMISDocumentsToHippo;
59 private boolean deleteHippoDocumentsWhenCMISDocumentsRemoved;
60
61 public void setJcrSession(javax.jcr.Session jcrSession) {
62 this.jcrSession = jcrSession;
63 }
64
65 public CmisRepoConfig getCmisRepoConfig() {
66 return cmisRepoConfig;
67 }
68
69 public void setCmisRepoConfig(CmisRepoConfig cmisRepoConfig) {
70 this.cmisRepoConfig = cmisRepoConfig;
71 }
72
73 public HippoRepoConfig getHippoRepoConfig() {
74 return hippoRepoConfig;
75 }
76
77 public void setHippoRepoConfig(HippoRepoConfig hippoRepoConfig) {
78 this.hippoRepoConfig = hippoRepoConfig;
79 }
80
81 public boolean isMigrateCMISDocumentsToHippo() {
82 return migrateCMISDocumentsToHippo;
83 }
84
85 public void setMigrateCMISDocumentsToHippo(boolean migrateCMISDocumentsToHippo) {
86 this.migrateCMISDocumentsToHippo = migrateCMISDocumentsToHippo;
87 }
88
89 public boolean isDeleteHippoDocumentsWhenCMISDocumentsRemoved() {
90 return deleteHippoDocumentsWhenCMISDocumentsRemoved;
91 }
92
93 public void setDeleteHippoDocumentsWhenCMISDocumentsRemoved(boolean deleteHippoDocumentsWhenCMISDocumentsRemoved) {
94 this.deleteHippoDocumentsWhenCMISDocumentsRemoved = deleteHippoDocumentsWhenCMISDocumentsRemoved;
95 }
96
97 public void execute() {
98 log.info("Starting Cmis Documents Replicator Execution ...");
99
100 if (migrateCMISDocumentsToHippo) {
101 try {
102 updateCmisDocumentsToHippoRepository();
103 } catch (Exception e) {
104 log.warn("Failed to update Cmis Documents To Hippo Repository: " + e, e);
105 }
106 }
107
108 if (deleteHippoDocumentsWhenCMISDocumentsRemoved) {
109 try {
110 deleteAssetsNotHavingCorrespondingCMISDocuments();
111 } catch (Exception e) {
112 log.warn("Failed to update Repository Documents To CMIS Repository: " + e, e);
113 }
114 }
115
116 log.info("Stopping Cmis Documents Replicator Execution ...");
117 }
118
119 private void updateCmisDocumentsToHippoRepository() throws RepositoryException, IOException {
120 Session session = null;
121
122 try {
123 session = createSession();
124 OperationContext operationContext = session.createOperationContext();
125 operationContext.setMaxItemsPerPage(cmisRepoConfig.getMaxItemsPerPage());
126
127 CmisObject seed;
128
129 if (StringUtils.isBlank(cmisRepoConfig.getRootPath())) {
130 seed = session.getRootFolder(operationContext);
131 } else {
132 seed = session.getObjectByPath(cmisRepoConfig.getRootPath(), operationContext);
133 }
134
135 List<String> documentIds = new LinkedList<String>();
136 fillAllDocumentIdsFromCMISRepository(seed, documentIds);
137 log.debug("Found {} numnber of documents", documentIds.size());
138 for (String documentId : documentIds) {
139 Document document = null;
140
141 try {
142 document = (Document) session.getObject(documentId);
143 } catch (CmisObjectNotFoundException ignore) {
144 }
145
146 if (document == null) {
147 continue;
148 }
149
150 String remoteDocument = StringUtils.removeStart(StringUtils.substringBeforeLast(document.getPaths().get(0), "/"), cmisRepoConfig.getRootPath());
151
152
153 remoteDocument = StringUtils.removeStart(remoteDocument, "/");
154
155
156 String encodedAssetName = Codecs.encodeNode(document.getName());
157
158 String assetFolderPath = hippoRepoConfig.getRootPath();
159 String assetPath;
160
161
162 if (StringUtils.isEmpty(remoteDocument)) {
163 assetPath = assetFolderPath + "/" + encodedAssetName;
164 } else {
165 assetPath = assetFolderPath + "/" + remoteDocument + "/" + encodedAssetName;
166 }
167
168 AssetMetadata metadata = AssetUtils.getAssetMetadata(jcrSession, assetPath);
169 long documentLastModified = document.getLastModificationDate().getTimeInMillis();
170
171 if (metadata == null || documentLastModified > metadata.getLastModified()) {
172 Node assetFolderNode = AssetUtils.createAssetFolders(jcrSession, assetFolderPath);
173 CmisDocumentBinary binary = new CmisDocumentBinary(document);
174 AssetUtils.updateAsset(jcrSession, assetFolderNode, encodedAssetName, document, binary,
175 cmisRepoConfig.getMetadataIdsToSync());
176
177 binary.dispose();
178 log.info("Updated asset on {}", assetPath);
179 }
180 }
181 } finally {
182 if (session != null) {
183 try {
184 session.clear();
185 } catch (Exception ignore) {
186 }
187 }
188 }
189 }
190
191 private void deleteAssetsNotHavingCorrespondingCMISDocuments() throws RepositoryException, IOException {
192 Session session = null;
193
194 try {
195 session = createSession();
196 OperationContext operationContext = session.createOperationContext();
197 operationContext.setMaxItemsPerPage(cmisRepoConfig.getMaxItemsPerPage());
198
199 Node seed = null;
200
201 if (jcrSession.itemExists(hippoRepoConfig.getRootPath())) {
202 seed = jcrSession.getNode(hippoRepoConfig.getRootPath());
203 }
204
205 if (seed != null) {
206 List<String> documentIds = new LinkedList<String>();
207 fillAllDocumentIdsFromHippoRepository(seed, documentIds);
208
209 for (String documentId : documentIds) {
210 Document document = null;
211
212
213
214
215 try {
216 document = (Document) session.getObject(documentId);
217 } catch (CmisObjectNotFoundException ignore) {
218 }
219
220 if (document == null) {
221 try {
222 removeAssetByDocumentId(jcrSession, documentId);
223 log.info("Removed asset node corresponding to " + documentId);
224 } catch (RepositoryException re) {
225 log.warn("Failed to remove node corresponding to " + documentId);
226 }
227 }
228 }
229 }
230 } finally {
231 if (session != null) {
232 try {
233 session.clear();
234 } catch (Exception ignore) {
235 }
236 }
237 }
238 }
239
240 private Session createSession() {
241 Map<String, String> sessionParams = new HashMap<String, String>();
242
243 sessionParams.put(SessionParameter.USER, cmisRepoConfig.getUsername());
244 sessionParams.put(SessionParameter.PASSWORD, cmisRepoConfig.getPassword());
245 sessionParams.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
246 sessionParams.put(SessionParameter.ATOMPUB_URL, cmisRepoConfig.getUrl());
247 sessionParams.put(SessionParameter.REPOSITORY_ID, cmisRepoConfig.getRepositoryId());
248
249 SessionFactory factory = SessionFactoryImpl.newInstance();
250
251 return factory.createSession(sessionParams);
252 }
253
254 private void fillAllDocumentIdsFromCMISRepository(CmisObject seed, List<String> documentIds) {
255 String baseType = seed.getBaseType().getId();
256 if (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)) {
257 documentIds.add(seed.getId());
258 } else if (BaseTypeId.CMIS_FOLDER.value().equals(baseType)) {
259 ItemIterable<CmisObject> children = ((Folder) seed).getChildren();
260
261 if (children.getTotalNumItems() > 0) {
262 for (CmisObject item : children) {
263 fillAllDocumentIdsFromCMISRepository(item, documentIds);
264 }
265 }
266 }
267 }
268
269 private void fillAllDocumentIdsFromHippoRepository(Node seed, List<String> documentIds) throws RepositoryException {
270 if (seed.isNodeType(CmisReplicationTypes.HIPPO_HANDLE)) {
271 if (seed.hasNode(seed.getName())) {
272 seed = seed.getNode(seed.getName());
273 }
274 }
275
276 if (seed.isNodeType(CmisReplicationTypes.CMIS_DOCUMENT_TYPE) && seed.hasProperty(CmisReplicationTypes.CMIS_OBJECT_ID)) {
277 documentIds.add(seed.getProperty(CmisReplicationTypes.CMIS_OBJECT_ID).getString());
278 } else if (seed.isNodeType(CmisReplicationTypes.HIPPO_ASSET_GALLERY)) {
279 for (NodeIterator nodeIt = seed.getNodes(); nodeIt.hasNext(); ) {
280 Node child = nodeIt.nextNode();
281
282 if (child != null) {
283 fillAllDocumentIdsFromHippoRepository(child, documentIds);
284 }
285 }
286 }
287 }
288
289 private void removeAssetByDocumentId(javax.jcr.Session jcrSession, String documentId) throws RepositoryException {
290 QueryManager queryManager = jcrSession.getWorkspace().getQueryManager();
291 String statement = "//element(*," + CmisReplicationTypes.CMIS_DOCUMENT_TYPE + ")[@" + CmisReplicationTypes.CMIS_OBJECT_ID + "='" + documentId + "']";
292 Query query = queryManager.createQuery(statement, Query.XPATH);
293 QueryResult result = query.execute();
294 boolean removed = false;
295
296 for (NodeIterator nodeIt = result.getNodes(); nodeIt.hasNext(); ) {
297 Node node = nodeIt.nextNode();
298
299 if (node != null) {
300 Node parentNode = node.getParent();
301
302 if (parentNode.isNodeType(CmisReplicationTypes.HIPPO_HANDLE)) {
303 parentNode.remove();
304 removed = true;
305 }
306 }
307 }
308
309 if (removed) {
310 jcrSession.save();
311 }
312 }
313 }