View Javadoc
1   package org.argeo.cms.internal.jcr;
2   
3   import java.io.IOException;
4   import java.io.InputStream;
5   import java.net.URI;
6   import java.net.URISyntaxException;
7   import java.nio.file.Files;
8   import java.nio.file.Path;
9   import java.nio.file.Paths;
10  import java.util.Dictionary;
11  import java.util.Enumeration;
12  import java.util.Properties;
13  import java.util.UUID;
14  
15  import javax.jcr.RepositoryException;
16  
17  import org.apache.commons.logging.Log;
18  import org.apache.commons.logging.LogFactory;
19  import org.apache.jackrabbit.core.RepositoryContext;
20  import org.apache.jackrabbit.core.RepositoryImpl;
21  import org.apache.jackrabbit.core.cache.CacheManager;
22  import org.apache.jackrabbit.core.config.RepositoryConfig;
23  import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
24  import org.argeo.api.NodeConstants;
25  import org.argeo.cms.CmsException;
26  import org.argeo.cms.internal.kernel.CmsPaths;
27  import org.argeo.jcr.ArgeoJcrException;
28  import org.xml.sax.InputSource;
29  
30  /** Can interpret properties in order to create an actual JCR repository. */
31  public class RepositoryBuilder {
32  	private final static Log log = LogFactory.getLog(RepositoryBuilder.class);
33  
34  	public RepositoryContext createRepositoryContext(Dictionary<String, ?> properties)
35  			throws RepositoryException, IOException {
36  		RepositoryConfig repositoryConfig = createRepositoryConfig(properties);
37  		RepositoryContext repositoryContext = createJackrabbitRepository(repositoryConfig);
38  		RepositoryImpl repository = repositoryContext.getRepository();
39  
40  		// cache
41  		Object maxCacheMbStr = prop(properties, RepoConf.maxCacheMB);
42  		if (maxCacheMbStr != null) {
43  			Integer maxCacheMB = Integer.parseInt(maxCacheMbStr.toString());
44  			CacheManager cacheManager = repository.getCacheManager();
45  			cacheManager.setMaxMemory(maxCacheMB * 1024l * 1024l);
46  			cacheManager.setMaxMemoryPerCache((maxCacheMB / 4) * 1024l * 1024l);
47  		}
48  
49  		return repositoryContext;
50  	}
51  
52  	RepositoryConfig createRepositoryConfig(Dictionary<String, ?> properties) throws RepositoryException, IOException {
53  		JackrabbitType type = JackrabbitType.valueOf(prop(properties, RepoConf.type).toString());
54  		ClassLoader cl = getClass().getClassLoader();
55  		final String base = "/org/argeo/cms/internal/jcr";
56  		try (InputStream in = cl.getResourceAsStream(base + "/repository-" + type.name() + ".xml")) {
57  			if (in == null)
58  				throw new ArgeoJcrException("Repository configuration not found");
59  			InputSource config = new InputSource(in);
60  			Properties jackrabbitVars = getConfigurationProperties(type, properties);
61  			// RepositoryConfig repositoryConfig = RepositoryConfig.create(config,
62  			// jackrabbitVars);
63  
64  			// custom configuration parser
65  			CustomRepositoryConfigurationParser parser = new CustomRepositoryConfigurationParser(jackrabbitVars);
66  			parser.setAccessControlProviderClassLoader(cl);
67  			RepositoryConfig repositoryConfig = parser.parseRepositoryConfig(config);
68  			repositoryConfig.init();
69  
70  			// set the proper classloaders
71  			repositoryConfig.getSecurityConfig().getSecurityManagerConfig().setClassLoader(cl);
72  			repositoryConfig.getSecurityConfig().getAccessManagerConfig().setClassLoader(cl);
73  //			for (WorkspaceConfig workspaceConfig : repositoryConfig.getWorkspaceConfigs()) {
74  //				workspaceConfig.getSecurityConfig().getAccessControlProviderConfig().setClassLoader(cl);
75  //			}
76  			return repositoryConfig;
77  		}
78  	}
79  
80  	private Properties getConfigurationProperties(JackrabbitType type, Dictionary<String, ?> properties) {
81  		Properties props = new Properties();
82  		for (Enumeration<String> keys = properties.keys(); keys.hasMoreElements();) {
83  			String key = keys.nextElement();
84  			props.put(key, properties.get(key));
85  		}
86  
87  		// cluster id
88  		// cf. https://wiki.apache.org/jackrabbit/Clustering
89  		// TODO deal with multiple repos
90  		String clusterId = System.getProperty("org.apache.jackrabbit.core.cluster.node_id");
91  		String clusterIdProp = props.getProperty(RepoConf.clusterId.name());
92  		if (clusterId != null) {
93  			if (clusterIdProp != null)
94  				throw new CmsException("Cluster id defined as System properties and in deploy config");
95  			props.put(RepoConf.clusterId.name(), clusterId);
96  		} else {
97  			clusterId = clusterIdProp;
98  		}
99  
100 		// home
101 		String homeUri = props.getProperty(RepoConf.labeledUri.name());
102 		Path homePath;
103 		if (homeUri == null) {
104 			String cn = props.getProperty(NodeConstants.CN);
105 			assert cn != null;
106 			if (clusterId != null) {
107 				homePath = CmsPaths.getRepoDirPath(cn + '/' + clusterId);
108 			} else {
109 				homePath = CmsPaths.getRepoDirPath(cn);
110 			}
111 		} else {
112 			try {
113 				URI uri = new URI(homeUri);
114 				String host = uri.getHost();
115 				if (host == null || host.trim().equals("")) {
116 					homePath = Paths.get(uri).toAbsolutePath();
117 				} else {
118 					// TODO remote at this stage?
119 					throw new IllegalArgumentException("Cannot manage repository path for host " + host);
120 				}
121 			} catch (URISyntaxException e) {
122 				throw new CmsException("Invalid repository home URI", e);
123 			}
124 		}
125 		// TODO use Jackrabbit API (?)
126 		Path rootUuidPath = homePath.resolve("repository/meta/rootUUID");
127 		try {
128 			if (!Files.exists(rootUuidPath)) {
129 				Files.createDirectories(rootUuidPath.getParent());
130 				Files.write(rootUuidPath, UUID.randomUUID().toString().getBytes());
131 			}
132 			// File homeDir = homePath.toFile();
133 			// homeDir.mkdirs();
134 		} catch (IOException e) {
135 			throw new CmsException("Cannot set up repository  home " + homePath, e);
136 		}
137 		// home cannot be overridden
138 		props.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, homePath.toString());
139 
140 		setProp(props, RepoConf.indexesBase, CmsPaths.getRepoIndexesBase().toString());
141 		// common
142 		setProp(props, RepoConf.defaultWorkspace);
143 		setProp(props, RepoConf.maxPoolSize);
144 		// Jackrabbit defaults
145 		setProp(props, RepoConf.bundleCacheMB);
146 		// See http://wiki.apache.org/jackrabbit/Search
147 		setProp(props, RepoConf.extractorPoolSize);
148 		setProp(props, RepoConf.searchCacheSize);
149 		setProp(props, RepoConf.maxVolatileIndexSize);
150 
151 		// specific
152 		String dburl;
153 		switch (type) {
154 		case h2:
155 			dburl = "jdbc:h2:" + homePath.toAbsolutePath() + "/h2/repository";
156 			setProp(props, RepoConf.dburl, dburl);
157 			setProp(props, RepoConf.dbuser, "sa");
158 			setProp(props, RepoConf.dbpassword, "");
159 			break;
160 		case postgresql:
161 		case postgresql_ds:
162 		case postgresql_cluster:
163 		case postgresql_cluster_ds:
164 			dburl = "jdbc:postgresql://localhost/demo";
165 			setProp(props, RepoConf.dburl, dburl);
166 			setProp(props, RepoConf.dbuser, "argeo");
167 			setProp(props, RepoConf.dbpassword, "argeo");
168 			break;
169 		case memory:
170 			break;
171 		case localfs:
172 			break;
173 		default:
174 			throw new ArgeoJcrException("Unsupported node type " + type);
175 		}
176 		return props;
177 	}
178 
179 	private void setProp(Properties props, RepoConf key, String def) {
180 		Object value = props.get(key.name());
181 		if (value == null)
182 			value = def;
183 		if (value == null)
184 			value = key.getDefault();
185 		if (value != null)
186 			props.put(key.name(), value.toString());
187 	}
188 
189 	private void setProp(Properties props, RepoConf key) {
190 		setProp(props, key, null);
191 	}
192 
193 	private String prop(Dictionary<String, ?> properties, RepoConf key) {
194 		Object value = properties.get(key.name());
195 		if (value == null)
196 			return key.getDefault() != null ? key.getDefault().toString() : null;
197 		else
198 			return value.toString();
199 	}
200 
201 	private RepositoryContext createJackrabbitRepository(RepositoryConfig repositoryConfig) throws RepositoryException {
202 		ClassLoader currentContextCl = Thread.currentThread().getContextClassLoader();
203 		Thread.currentThread().setContextClassLoader(RepositoryBuilder.class.getClassLoader());
204 		try {
205 			long begin = System.currentTimeMillis();
206 			//
207 			// Actual repository creation
208 			//
209 			RepositoryContext repositoryContext = RepositoryContext.create(repositoryConfig);
210 
211 			double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
212 			if (log.isDebugEnabled())
213 				log.debug(
214 						"Created Jackrabbit repository in " + duration + " s, home: " + repositoryConfig.getHomeDir());
215 
216 			return repositoryContext;
217 		} finally {
218 			Thread.currentThread().setContextClassLoader(currentContextCl);
219 		}
220 	}
221 
222 }