Skip to content
This repository was archived by the owner on Sep 16, 2024. It is now read-only.

Commit d604b48

Browse files
committed
#23 Adding support for Roxy module token replacement
1 parent 6cd751e commit d604b48

File tree

9 files changed

+194
-5
lines changed

9 files changed

+194
-5
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
/build
66
/.classpath
77
/.project
8+
.idea
9+
*.iml

deploy/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
These files are only used for testing purposes. They mirror how a MarkLogic Roxy project captures properties.

deploy/default.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
color=red
2+
number=10
3+
vehicle=${color} wagon

deploy/local.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
number=20
2+
outfit=${color} dress

src/main/java/com/marklogic/client/modulesloader/impl/XccAssetLoader.java

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
import java.util.HashSet;
1414
import java.util.Set;
1515

16+
import org.springframework.util.FileCopyUtils;
17+
1618
import com.marklogic.client.helper.LoggingObject;
1719
import com.marklogic.client.modulesloader.ModulesManager;
20+
import com.marklogic.client.modulesloader.tokenreplacer.ModuleTokenReplacer;
1821
import com.marklogic.client.modulesloader.xcc.CommaDelimitedPermissionsParser;
1922
import com.marklogic.client.modulesloader.xcc.DefaultDocumentFormatGetter;
2023
import com.marklogic.client.modulesloader.xcc.DocumentFormatGetter;
@@ -24,6 +27,7 @@
2427
import com.marklogic.xcc.ContentFactory;
2528
import com.marklogic.xcc.ContentSource;
2629
import com.marklogic.xcc.ContentSourceFactory;
30+
import com.marklogic.xcc.DocumentFormat;
2731
import com.marklogic.xcc.SecurityOptions;
2832
import com.marklogic.xcc.Session;
2933
import com.marklogic.xcc.exceptions.RequestException;
@@ -60,8 +64,11 @@ public class XccAssetLoader extends LoggingObject implements FileVisitor<Path> {
6064
private Path currentRootPath;
6165
private Set<File> filesLoaded;
6266

67+
// Manages when modules were last loaded
6368
private ModulesManager modulesManager;
6469

70+
private ModuleTokenReplacer moduleTokenReplacer;
71+
6572
/**
6673
* For walking one or many paths and loading modules in each of them.
6774
*/
@@ -123,13 +130,13 @@ protected void closeActiveSession() {
123130
public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attributes) throws IOException {
124131
boolean accept = fileFilter.accept(path.toFile());
125132
if (accept) {
126-
if (logger.isDebugEnabled()) {
127-
logger.debug("Visiting directory: " + path);
133+
if (logger.isTraceEnabled()) {
134+
logger.trace("Visiting directory: " + path);
128135
}
129136
return FileVisitResult.CONTINUE;
130137
} else {
131-
if (logger.isDebugEnabled()) {
132-
logger.debug("Skipping directory: " + path);
138+
if (logger.isTraceEnabled()) {
139+
logger.trace("Skipping directory: " + path);
133140
}
134141
return FileVisitResult.SKIP_SUBTREE;
135142
}
@@ -189,7 +196,7 @@ protected void loadFile(String uri, File f) {
189196
logger.info(format("Inserting module with URI: %s", uri));
190197
}
191198

192-
Content content = ContentFactory.newContent(uri, f, options);
199+
Content content = buildContent(uri, f, options);
193200
try {
194201
activeSession.insertContent(content);
195202
if (modulesManager != null) {
@@ -200,6 +207,36 @@ protected void loadFile(String uri, File f) {
200207
}
201208
}
202209

210+
/**
211+
* If we have a ModuleTokenReplacer, we try to use it. But if we can't load the file as a string, we just assume we
212+
* can't replace any tokens in it.
213+
*
214+
* @param uri
215+
* @param f
216+
* @param options
217+
* @return
218+
*/
219+
protected Content buildContent(String uri, File f, ContentCreateOptions options) {
220+
Content content = null;
221+
if (moduleTokenReplacer != null && moduleCanBeReadAsString(options.getFormat())) {
222+
try {
223+
String text = new String(FileCopyUtils.copyToByteArray(f));
224+
text = moduleTokenReplacer.replaceTokensInModule(text);
225+
content = ContentFactory.newContent(uri, text, options);
226+
} catch (IOException ie) {
227+
content = ContentFactory.newContent(uri, f, options);
228+
}
229+
} else {
230+
content = ContentFactory.newContent(uri, f, options);
231+
}
232+
return content;
233+
}
234+
235+
protected boolean moduleCanBeReadAsString(DocumentFormat format) {
236+
return format != null && (format.equals(DocumentFormat.JSON) || format.equals(DocumentFormat.TEXT)
237+
|| format.equals(DocumentFormat.XML));
238+
}
239+
203240
@Override
204241
public FileVisitResult postVisitDirectory(Path path, IOException exception) throws IOException {
205242
return FileVisitResult.CONTINUE;
@@ -253,4 +290,8 @@ public void setModulesManager(ModulesManager modulesManager) {
253290
public void setFileFilter(FileFilter fileFilter) {
254291
this.fileFilter = fileFilter;
255292
}
293+
294+
public void setModuleTokenReplacer(ModuleTokenReplacer moduleTokenReplacer) {
295+
this.moduleTokenReplacer = moduleTokenReplacer;
296+
}
256297
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.marklogic.client.modulesloader.tokenreplacer;
2+
3+
public interface ModuleTokenReplacer {
4+
5+
public String replaceTokensInModule(String moduleText);
6+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package com.marklogic.client.modulesloader.tokenreplacer;
2+
3+
import java.io.File;
4+
import java.io.FileReader;
5+
import java.io.IOException;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.Properties;
9+
10+
import org.springframework.beans.factory.config.PlaceholderConfigurerSupport;
11+
import org.springframework.util.PropertyPlaceholderHelper;
12+
13+
import com.marklogic.client.helper.LoggingObject;
14+
15+
/**
16+
* Follows the conventions used by Roxy, where properties are prefixed with "@ml.".
17+
*
18+
* By default, this will load properties from "deploy/default.properties", "deploy/build.properties", and
19+
* "deploy/local.properties", if any of those exist.
20+
*/
21+
public class RoxyModuleTokenReplacer extends LoggingObject implements ModuleTokenReplacer {
22+
23+
private Properties properties;
24+
private PropertyPlaceholderHelper helper;
25+
private String propertyPrefix = "@ml.";
26+
27+
public RoxyModuleTokenReplacer() {
28+
List<String> filePaths = new ArrayList<>();
29+
filePaths.add("deploy/default.properties");
30+
filePaths.add("deploy/build.properties");
31+
filePaths.add("deploy/local.properties");
32+
initializeProperties(filePaths);
33+
initializeHelper();
34+
}
35+
36+
public RoxyModuleTokenReplacer(List<String> filePaths) {
37+
initializeProperties(filePaths);
38+
initializeHelper();
39+
}
40+
41+
protected void initializeProperties(List<String> filePaths) {
42+
properties = new Properties();
43+
for (String path : filePaths) {
44+
loadPropertiesFromFile(new File(path));
45+
}
46+
}
47+
48+
protected void initializeHelper() {
49+
helper = new PropertyPlaceholderHelper(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_PREFIX,
50+
PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_SUFFIX,
51+
PlaceholderConfigurerSupport.DEFAULT_VALUE_SEPARATOR, true);
52+
}
53+
54+
protected void loadPropertiesFromFile(File file) {
55+
if (file.exists()) {
56+
FileReader reader = null;
57+
try {
58+
reader = new FileReader(file);
59+
if (logger.isInfoEnabled()) {
60+
logger.info("Loading module properties from: " + file.getAbsolutePath());
61+
}
62+
properties.load(reader);
63+
} catch (IOException ex) {
64+
logger.warn(
65+
"Unable to load properties from file " + file.getAbsolutePath() + "; cause: " + ex.getMessage(),
66+
ex);
67+
} finally {
68+
if (reader != null) {
69+
try {
70+
reader.close();
71+
} catch (IOException ie) {
72+
// Ignore
73+
}
74+
}
75+
}
76+
}
77+
}
78+
79+
@Override
80+
public String replaceTokensInModule(String moduleText) {
81+
for (Object key : properties.keySet()) {
82+
String skey = propertyPrefix != null ? propertyPrefix + key : key.toString();
83+
if (logger.isTraceEnabled()) {
84+
logger.trace("Checking for key in module text: " + skey);
85+
}
86+
if (moduleText.contains(skey)) {
87+
String value = properties.getProperty(key.toString());
88+
value = helper.replacePlaceholders(value, properties);
89+
if (logger.isDebugEnabled()) {
90+
logger.debug(format("Replacing %s with %s", skey, value));
91+
}
92+
moduleText = moduleText.replace(skey, value);
93+
}
94+
}
95+
return moduleText;
96+
}
97+
98+
public void setPropertyPrefix(String propertyPrefix) {
99+
this.propertyPrefix = propertyPrefix;
100+
}
101+
102+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.marklogic.client.modulesloader.impl;
2+
3+
import java.io.File;
4+
5+
import org.junit.Assert;
6+
import org.junit.Test;
7+
import org.springframework.util.FileCopyUtils;
8+
9+
import com.marklogic.client.modulesloader.tokenreplacer.RoxyModuleTokenReplacer;
10+
11+
public class RoxyModuleTokenReplacerTest extends Assert {
12+
13+
@Test
14+
public void test() throws Exception {
15+
RoxyModuleTokenReplacer r = new RoxyModuleTokenReplacer();
16+
String original = new String(
17+
FileCopyUtils.copyToByteArray(new File("src/test/resources/token-replace/ext/test.xqy")));
18+
19+
String modified = r.replaceTokensInModule(original);
20+
assertTrue(modified.contains("<color>red</color>"));
21+
assertTrue(modified.contains("<number>20</number>"));
22+
assertTrue(modified.contains("<vehicle>red wagon</vehicle>"));
23+
assertTrue(modified.contains("<outfit>red dress</outfit>"));
24+
}
25+
26+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<test>
2+
<color>@ml.color</color>
3+
<number>@ml.number</number>
4+
<vehicle>@ml.vehicle</vehicle>
5+
<outfit>@ml.outfit</outfit>
6+
</test>

0 commit comments

Comments
 (0)