View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * 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  package org.argeo.slc.lib.jcr;
17  
18  import java.util.UUID;
19  import java.util.jar.Attributes;
20  import java.util.jar.JarEntry;
21  import java.util.jar.JarOutputStream;
22  import java.util.jar.Manifest;
23  
24  import javax.jcr.Credentials;
25  import javax.jcr.Node;
26  import javax.jcr.NodeIterator;
27  import javax.jcr.Repository;
28  import javax.jcr.RepositoryFactory;
29  import javax.jcr.Session;
30  import javax.jcr.SimpleCredentials;
31  
32  import org.apache.commons.io.IOUtils;
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.commons.vfs2.FileObject;
36  import org.apache.commons.vfs2.FileSelectInfo;
37  import org.apache.commons.vfs2.FileSelector;
38  import org.apache.commons.vfs2.FileSystemException;
39  import org.apache.commons.vfs2.FileSystemManager;
40  import org.argeo.jcr.JcrUtils;
41  import org.argeo.node.NodeUtils;
42  import org.argeo.slc.SlcException;
43  
44  /** Backups a JCR repository */
45  public class JcrRepositoryBackup implements Runnable {
46  	private final static Log log = LogFactory.getLog(JcrRepositoryBackup.class);
47  
48  	private String sourceRepo;
49  	private String sourceDatastore;
50  	private String targetFile;
51  
52  	private String sourceWksp;
53  
54  	private String sourceUsername;
55  	private char[] sourcePassword;
56  
57  	private RepositoryFactory repositoryFactory;
58  	private FileSystemManager fileSystemManager;
59  
60  	public void run() {
61  		Session sourceDefaultSession = null;
62  		try {
63  			long begin = System.currentTimeMillis();
64  
65  			FileObject archiveRoot = fileSystemManager.resolveFile(targetFile);
66  			archiveRoot.createFolder();
67  
68  			String datastoreFolderName = "datastore";
69  			if (hasDatastore())
70  				backupDataStore(archiveRoot.resolveFile(datastoreFolderName));
71  
72  			Repository sourceRepository = NodeUtils.getRepositoryByUri(
73  					repositoryFactory, sourceRepo);
74  			Credentials sourceCredentials = null;
75  			if (sourceUsername != null)
76  				sourceCredentials = new SimpleCredentials(sourceUsername,
77  						sourcePassword);
78  
79  			sourceDefaultSession = sourceRepository.login(sourceCredentials);
80  			for (String sourceWorkspaceName : sourceDefaultSession
81  					.getWorkspace().getAccessibleWorkspaceNames()) {
82  				if (Thread.interrupted()) {
83  					log.error("Workspace backup interrupted");
84  					Thread.currentThread().interrupt();
85  					return;
86  				}
87  
88  				if (sourceWksp != null && !sourceWksp.trim().equals("")
89  						&& !sourceWorkspaceName.equals(sourceWksp))
90  					continue;
91  				Session sourceSession = null;
92  				JarOutputStream out = null;
93  				FileObject workspaceBackup = null;
94  				try {
95  					Manifest manifest = new Manifest();
96  					manifest.getMainAttributes().put(
97  							Attributes.Name.MANIFEST_VERSION, "1.0");
98  					manifest.getMainAttributes().putValue("Backup-UUID",
99  							UUID.randomUUID().toString());
100 					manifest.getMainAttributes().putValue("Backup-Timestamp",
101 							Long.toString(System.currentTimeMillis()));
102 					manifest.getMainAttributes().putValue(
103 							"Backup-JCR-Workspace", sourceWorkspaceName);
104 					workspaceBackup = fileSystemManager.resolveFile(targetFile
105 							+ "/" + sourceWorkspaceName + ".jar");
106 
107 					out = new JarOutputStream(workspaceBackup.getContent()
108 							.getOutputStream(), manifest);
109 					sourceSession = sourceRepository.login(sourceCredentials,
110 							sourceWorkspaceName);
111 					backupWorkspace(sourceSession, out);
112 				} finally {
113 					JcrUtils.logoutQuietly(sourceSession);
114 					IOUtils.closeQuietly(out);
115 					if (workspaceBackup != null)
116 						workspaceBackup.close();
117 				}
118 			}
119 
120 			// in case some binaries have been added during the backup
121 			if (hasDatastore())
122 				backupDataStore(archiveRoot.resolveFile(datastoreFolderName));
123 
124 			long duration = (System.currentTimeMillis() - begin) / 1000;// s
125 			log.info("Backed-up " + sourceRepo + " in " + (duration / 60)
126 					+ "min " + (duration % 60) + "s");
127 		} catch (Exception e) {
128 			throw new SlcException("Cannot backup " + sourceRepo, e);
129 		} finally {
130 			JcrUtils.logoutQuietly(sourceDefaultSession);
131 		}
132 	}
133 
134 	protected Boolean hasDatastore() {
135 		return sourceDatastore != null && !sourceDatastore.trim().equals("");
136 	}
137 
138 	protected void backupWorkspace(Session sourceSession, JarOutputStream out) {
139 		try {
140 			if (log.isTraceEnabled())
141 				log.trace("Backup " + sourceSession.getWorkspace().getName()
142 						+ "...");
143 			Boolean skipBinaries = hasDatastore();
144 			for (NodeIterator it = sourceSession.getRootNode().getNodes(); it
145 					.hasNext();) {
146 				if (Thread.interrupted()) {
147 					log.error("Node backup interrupted");
148 					Thread.currentThread().interrupt();
149 					return;
150 				}
151 				Node node = it.nextNode();
152 				JarEntry entry = new JarEntry(node.getPath());
153 				out.putNextEntry(entry);
154 				sourceSession.exportSystemView(node.getPath(), out,
155 						skipBinaries, false);
156 				out.flush();
157 				out.closeEntry();
158 			}
159 			if (log.isDebugEnabled())
160 				log.debug("Backed up " + sourceSession.getWorkspace().getName());
161 		} catch (Exception e) {
162 			throw new SlcException("Cannot backup "
163 					+ sourceSession.getWorkspace().getName(), e);
164 		}
165 	}
166 
167 	protected void backupDataStore(final FileObject targetDatastore) {
168 		try {
169 			targetDatastore.createFolder();
170 			final FileObject sourceDataStore = fileSystemManager
171 					.resolveFile(sourceDatastore);
172 			if (log.isDebugEnabled())
173 				log.debug("Backup " + sourceDatastore);
174 			targetDatastore.copyFrom(sourceDataStore, new FileSelector() {
175 				public boolean traverseDescendents(FileSelectInfo fileInfo)
176 						throws Exception {
177 					return true;
178 				}
179 
180 				public boolean includeFile(FileSelectInfo fileInfo)
181 						throws Exception {
182 					String relativeName = fileInfo
183 							.getFile()
184 							.getName()
185 							.getPath()
186 							.substring(
187 									sourceDataStore.getName().getPath()
188 											.length());
189 					FileObject target = targetDatastore
190 							.resolveFile(relativeName);
191 					if (target.exists()) {
192 						return false;
193 					} else {
194 						return true;
195 					}
196 				}
197 			});
198 			if (log.isDebugEnabled())
199 				log.debug("Backed-up " + sourceDatastore);
200 		} catch (FileSystemException e) {
201 			throw new SlcException("Cannot backup datastore", e);
202 		}
203 	}
204 
205 	public void setSourceRepo(String sourceRepo) {
206 		this.sourceRepo = sourceRepo;
207 	}
208 
209 	public void setSourceWksp(String sourceWksp) {
210 		this.sourceWksp = sourceWksp;
211 	}
212 
213 	public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
214 		this.repositoryFactory = repositoryFactory;
215 	}
216 
217 	public void setSourceUsername(String sourceUsername) {
218 		this.sourceUsername = sourceUsername;
219 	}
220 
221 	public void setSourcePassword(char[] sourcePassword) {
222 		this.sourcePassword = sourcePassword;
223 	}
224 
225 	public void setFileSystemManager(FileSystemManager fileSystemManager) {
226 		this.fileSystemManager = fileSystemManager;
227 	}
228 
229 	public void setTargetFile(String targetFile) {
230 		this.targetFile = targetFile;
231 	}
232 
233 	public void setSourceDatastore(String sourceDatastore) {
234 		this.sourceDatastore = sourceDatastore;
235 	}
236 
237 }