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

Commit 25b7b03

Browse files
committed
Refactoring of TDE/QBV processors
Changes: - Renamed client to `schemasDatabaseClient` to make it clear the kind of client that's needed. - Using `FilenameUtil` - not the most glamorous class, but nice to have all the checks in one place. - Deprecated some getters/setters that I shouldn't have added. - Added a schemas database to the test app and changed schemas tests to use that.
1 parent 5004527 commit 25b7b03

File tree

9 files changed

+114
-63
lines changed

9 files changed

+114
-63
lines changed

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ mlAppName=ml-javaclient-util-test
1616
mlRestPort=8028
1717
mlUsername=admin
1818
mlPassword=change in gradle-local.properties
19+
mlConfigPaths=src/test/ml-config

src/main/java/com/marklogic/client/ext/helper/FilenameUtil.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,30 @@
2020
public abstract class FilenameUtil {
2121

2222
public static boolean isXslFile(String filename) {
23-
return filename.endsWith(".xsl") || filename.endsWith(".xslt");
23+
return endsWithExtension(filename, ".xsl", ".xslt");
2424
}
2525

2626
public static boolean isXqueryFile(String filename) {
27-
return filename.endsWith(".xqy") || filename.endsWith(".xq");
27+
return endsWithExtension(filename, ".xqy", ".xqy");
2828
}
2929

3030
public static boolean isJavascriptFile(String filename) {
31-
return filename.endsWith(".sjs") || filename.endsWith(".js");
31+
return endsWithExtension(filename, ".sjs", ".js");
3232
}
3333

34+
public static boolean endsWithExtension(String filename, String... extensions) {
35+
if (filename == null || extensions == null) {
36+
return false;
37+
}
38+
filename = filename.toLowerCase();
39+
for (String extension : extensions) {
40+
if (extension != null && filename.endsWith(extension.toLowerCase())) {
41+
return true;
42+
}
43+
}
44+
return false;
45+
}
46+
3447
public static String getFileExtension(File f) {
3548
String[] split = f.getName().split("\\.");
3649
if (split.length > 1) {

src/main/java/com/marklogic/client/ext/schemasloader/impl/QbvDocumentFileProcessor.java

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.marklogic.client.eval.ServerEvaluationCall;
2121
import com.marklogic.client.ext.file.DocumentFile;
2222
import com.marklogic.client.ext.file.DocumentFileProcessor;
23+
import com.marklogic.client.ext.helper.FilenameUtil;
2324
import com.marklogic.client.ext.helper.LoggingObject;
2425
import com.marklogic.client.extra.jdom.JDOMHandle;
2526
import com.marklogic.client.io.Format;
@@ -29,32 +30,41 @@
2930
import org.jdom2.input.SAXBuilder;
3031
import org.springframework.util.FileCopyUtils;
3132

33+
import java.io.File;
3234
import java.io.IOException;
3335
import java.io.StringReader;
3436
import java.util.ArrayList;
3537
import java.util.List;
3638

39+
/**
40+
* @since 4.6.0
41+
*/
3742
public class QbvDocumentFileProcessor extends LoggingObject implements DocumentFileProcessor {
3843

3944
public static final String QBV_COLLECTION = "http://marklogic.com/xdmp/qbv";
4045
private static final String QBV_XML_PLAN_NAMESPACE = "http://marklogic.com/plan";
4146
private static final String QBV_XML_ROOT_ELEMENT = "query-based-view";
42-
private static final String JAVASCRIPT_EVAL_TEMPLATE = "declareUpdate(); xdmp.invokeFunction(function() {'use strict'; const op = require('/MarkLogic/optic'); return %s }, {database: xdmp.database('%s')})";
43-
private static final String XQUERY_EVAL_TEMPLATE = "xquery version \"1.0-ml\"; import module namespace op=\"http://marklogic.com/optic\" at \"/MarkLogic/optic.xqy\"; xdmp:invoke-function(function() {%s},<options xmlns=\"xdmp:eval\"><database>{xdmp:database('%s')}</database></options>)";
47+
private static final String JAVASCRIPT_EVAL_TEMPLATE = "declareUpdate(); " +
48+
"xdmp.invokeFunction(function() {'use strict'; const op = require('/MarkLogic/optic'); return %s }, " +
49+
"{database: xdmp.database('%s')})";
50+
private static final String XQUERY_EVAL_TEMPLATE = "xquery version \"1.0-ml\"; " +
51+
"import module namespace op=\"http://marklogic.com/optic\" at \"/MarkLogic/optic.xqy\"; " +
52+
"xdmp:invoke-function(function() {%s},<options xmlns=\"xdmp:eval\"><database>{xdmp:database('%s')}</database></options>)";
4453

45-
final private DatabaseClient databaseClient;
54+
final private DatabaseClient schemasDatabaseClient;
4655
final private String qbvGeneratorDatabaseName;
47-
final protected List<DocumentFile> qbvFiles = new ArrayList<>();
56+
final private List<DocumentFile> qbvFiles = new ArrayList<>();
4857
final private XMLDocumentManager docMgr;
4958

5059

5160
/**
52-
* @param databaseClient - a MarkLogic DatabaseClient object for the Schemas database
61+
* @param schemasDatabaseClient database client for the application's schemas database
62+
* @param qbvGeneratorDatabaseName the database to run a script against for generating a QBV
5363
*/
54-
public QbvDocumentFileProcessor(DatabaseClient databaseClient, String qbvGeneratorDatabaseName) {
55-
this.databaseClient = databaseClient;
64+
public QbvDocumentFileProcessor(DatabaseClient schemasDatabaseClient, String qbvGeneratorDatabaseName) {
65+
this.schemasDatabaseClient = schemasDatabaseClient;
5666
this.qbvGeneratorDatabaseName = qbvGeneratorDatabaseName;
57-
this.docMgr = databaseClient.newXMLDocumentManager();
67+
this.docMgr = schemasDatabaseClient.newXMLDocumentManager();
5868
}
5969

6070
@Override
@@ -70,17 +80,11 @@ public DocumentFile processDocumentFile(DocumentFile documentFile) {
7080

7181
private boolean isQbvQuery(DocumentFile documentFile) {
7282
String uri = documentFile.getUri();
73-
if (uri != null && uri.startsWith("/qbv")) {
74-
String extension = documentFile.getFileExtension();
75-
if (extension != null) {
76-
extension = extension.toLowerCase();
77-
}
78-
return
79-
"sjs".equals(extension)
80-
|| "js".equals(extension)
81-
|| "xqy".equals(extension)
82-
|| "xq".equals(extension);
83-
} else return false;
83+
File file = documentFile.getFile();
84+
return uri != null
85+
&& uri.startsWith("/qbv")
86+
&& file != null
87+
&& (FilenameUtil.isXqueryFile(file.getName()) || FilenameUtil.isJavascriptFile(file.getName()));
8488
}
8589

8690
public void processQbvFiles() {
@@ -125,24 +129,18 @@ private ServerEvaluationCall getServerEvaluationCall(DocumentFile qbvFile) {
125129
} catch (IOException e) {
126130
throw new RuntimeException(format("Unable to generate Query-Based View; could not read from file %s; cause: %s", qbvFile.getFile().getAbsolutePath(), e.getMessage()));
127131
}
128-
String extension = qbvFile.getFileExtension();
129-
if (extension != null) {
130-
extension = extension.toLowerCase();
131-
}
132-
if (("xqy".equals(extension)) || ("xq".equals(extension))) {
133-
return buildXqueryCall(fileContent);
134-
} else {
135-
return buildJavascriptCall(fileContent);
136-
}
132+
return FilenameUtil.isXqueryFile(qbvFile.getFile().getName()) ?
133+
buildXqueryCall(fileContent) :
134+
buildJavascriptCall(fileContent);
137135
}
138136

139137
private ServerEvaluationCall buildJavascriptCall(String fileContent) {
140138
String script = format(JAVASCRIPT_EVAL_TEMPLATE, fileContent, qbvGeneratorDatabaseName);
141-
return databaseClient.newServerEval().javascript(script);
139+
return schemasDatabaseClient.newServerEval().javascript(script);
142140
}
143141

144142
private ServerEvaluationCall buildXqueryCall(String fileContent) {
145143
String script = format(XQUERY_EVAL_TEMPLATE, fileContent, qbvGeneratorDatabaseName);
146-
return databaseClient.newServerEval().xquery(script);
144+
return schemasDatabaseClient.newServerEval().xquery(script);
147145
}
148146
}

src/main/java/com/marklogic/client/ext/schemasloader/impl/TdeDocumentFileProcessor.java

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import com.marklogic.client.eval.ServerEvaluationCall;
2121
import com.marklogic.client.ext.file.DocumentFile;
2222
import com.marklogic.client.ext.file.DocumentFileProcessor;
23-
import com.marklogic.client.ext.helper.ClientHelper;
23+
import com.marklogic.client.ext.helper.FilenameUtil;
2424
import com.marklogic.client.ext.helper.LoggingObject;
2525
import com.marklogic.client.io.Format;
2626
import com.marklogic.client.io.JacksonHandle;
@@ -32,7 +32,7 @@
3232

3333
public class TdeDocumentFileProcessor extends LoggingObject implements DocumentFileProcessor {
3434

35-
private DatabaseClient databaseClient;
35+
private DatabaseClient schemasDatabaseClient;
3636
private String tdeValidationDatabase;
3737
private Boolean templateBatchInsertSupported;
3838

@@ -45,31 +45,34 @@ public TdeDocumentFileProcessor() {
4545
/**
4646
* Use this constructor when you want to validate a TDE template before writing it to MarkLogic.
4747
*
48-
* @param databaseClient
48+
* @param schemasDatabaseClient database client for the application's schemas database
49+
* @param tdeValidationDatabase the database to run a script against for validating a TDE
4950
*/
50-
public TdeDocumentFileProcessor(DatabaseClient databaseClient, String tdeValidationDatabase) {
51-
this.databaseClient = databaseClient;
51+
public TdeDocumentFileProcessor(DatabaseClient schemasDatabaseClient, String tdeValidationDatabase) {
52+
this.schemasDatabaseClient = schemasDatabaseClient;
5253
this.tdeValidationDatabase = tdeValidationDatabase;
5354
}
5455

5556
@Override
5657
public DocumentFile processDocumentFile(DocumentFile documentFile) {
5758
String uri = documentFile.getUri();
58-
String extension = documentFile.getFileExtension();
59-
if (extension != null) {
60-
extension = extension.toLowerCase();
61-
}
62-
63-
boolean isTdeTemplate = ("tdej".equals(extension) || "tdex".equals(extension)) || (uri != null && uri.startsWith("/tde"));
64-
if (isTdeTemplate) {
59+
String filename = documentFile.getFile() != null ? documentFile.getFile().getName() : null;
60+
boolean isTdeUri = (uri != null && uri.startsWith("/tde"));
61+
boolean isJsonTde = (isTdeUri && FilenameUtil.endsWithExtension(filename, ".json"))
62+
|| FilenameUtil.endsWithExtension(filename, ".tdej");
63+
boolean isXmlTde = (isTdeUri && FilenameUtil.endsWithExtension(filename, ".xml"))
64+
|| FilenameUtil.endsWithExtension(filename, ".tdex");
65+
66+
// We have a test suggesting that a TDE may not be JSON or XML; that doesn't seem likely, but it also does not
67+
// appear to cause any issues.
68+
if (isTdeUri || isJsonTde || isXmlTde) {
6569
documentFile.getDocumentMetadata().withCollections(TdeUtil.TDE_COLLECTION);
6670
validateTdeTemplate(documentFile);
67-
}
68-
69-
if ("tdej".equals(extension) || "json".equals(extension)) {
70-
documentFile.setFormat(Format.JSON);
71-
} else if ("tdex".equals(extension) || "xml".equals(extension)) {
72-
documentFile.setFormat(Format.XML);
71+
if (isJsonTde) {
72+
documentFile.setFormat(Format.JSON);
73+
} else if (isXmlTde) {
74+
documentFile.setFormat(Format.XML);
75+
}
7376
}
7477

7578
return documentFile;
@@ -79,14 +82,14 @@ private boolean isTemplateBatchInsertSupported() {
7982
if (this.templateBatchInsertSupported == null) {
8083
// Memoize this to avoid repeated calls; the result will always be the same unless the databaseClient is
8184
// modified, in which case templateBatchInsertSupported is set to null
82-
this.templateBatchInsertSupported = TdeUtil.templateBatchInsertSupported(databaseClient);
85+
this.templateBatchInsertSupported = TdeUtil.templateBatchInsertSupported(schemasDatabaseClient);
8386
}
8487
return this.templateBatchInsertSupported;
8588
}
8689

8790
protected void validateTdeTemplate(DocumentFile documentFile) {
8891
final File file = documentFile.getFile();
89-
if (databaseClient == null) {
92+
if (schemasDatabaseClient == null) {
9093
logger.info("No DatabaseClient provided for TDE validation, so will not validate TDE templates");
9194
} else if (tdeValidationDatabase == null) {
9295
logger.info("No TDE validation database specified, so will not validate TDE templates");
@@ -127,7 +130,7 @@ protected ServerEvaluationCall buildJavascriptCall(DocumentFile documentFile, St
127130
"\nreturn tde.validate([xdmp.toJSON(template)], ['%s'])}, {database: xdmp.database('%s')})",
128131
documentFile.getUri(), tdeValidationDatabase
129132
));
130-
return databaseClient.newServerEval().javascript(script.toString())
133+
return schemasDatabaseClient.newServerEval().javascript(script.toString())
131134
.addVariable("template", new StringHandle(fileContent).withFormat(Format.JSON));
132135
}
133136

@@ -139,22 +142,42 @@ protected ServerEvaluationCall buildXqueryCall(DocumentFile documentFile, String
139142
"\ntde:validate($template, '%s')}, <options xmlns='xdmp:eval'><database>{xdmp:database('%s')}</database></options>)",
140143
documentFile.getUri(), tdeValidationDatabase
141144
));
142-
return databaseClient.newServerEval().xquery(script.toString()).addVariable("template", new StringHandle(fileContent).withFormat(Format.XML));
145+
return schemasDatabaseClient.newServerEval().xquery(script.toString()).addVariable("template", new StringHandle(fileContent).withFormat(Format.XML));
143146
}
144147

148+
/**
149+
* @return
150+
* @deprecated since 4.6.0, will be removed in 5.0.0
151+
*/
152+
@Deprecated
145153
public DatabaseClient getDatabaseClient() {
146-
return databaseClient;
154+
return schemasDatabaseClient;
147155
}
148156

157+
/**
158+
* @param databaseClient
159+
* @deprecated since 4.6.0, will be removed in 5.0.0
160+
*/
161+
@Deprecated
149162
public void setDatabaseClient(DatabaseClient databaseClient) {
150-
this.databaseClient = databaseClient;
163+
this.schemasDatabaseClient = databaseClient;
151164
this.templateBatchInsertSupported = null;
152165
}
153166

167+
/**
168+
* @return
169+
* @deprecated since 4.6.0, will be removed in 5.0.0
170+
*/
171+
@Deprecated
154172
public String getTdeValidationDatabase() {
155173
return tdeValidationDatabase;
156174
}
157175

176+
/**
177+
* @param tdeValidationDatabase
178+
* @deprecated since 4.6.0, will be removed in 5.0.0
179+
*/
180+
@Deprecated
158181
public void setTdeValidationDatabase(String tdeValidationDatabase) {
159182
this.tdeValidationDatabase = tdeValidationDatabase;
160183
}

src/test/java/com/marklogic/client/ext/schemasloader/impl/AbstractSchemasTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public abstract class AbstractSchemasTest extends AbstractIntegrationTest {
2626
*/
2727
@BeforeEach
2828
public void setup() {
29-
client = newClient("Schemas");
29+
client = newClient("ml-javaclient-util-test-schemas");
3030
client.newServerEval().xquery("cts:uris((), (), cts:true-query()) ! xdmp:document-delete(.)").eval();
3131
}
3232

src/test/java/com/marklogic/client/ext/schemasloader/impl/GenerateQbvTest.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.marklogic.client.ext.file.DocumentFile;
44
import com.marklogic.client.ext.helper.ClientHelper;
55
import com.marklogic.client.io.DocumentMetadataHandle;
6+
import org.junit.jupiter.api.BeforeEach;
67
import org.junit.jupiter.api.Test;
78

89
import java.nio.file.Path;
@@ -14,9 +15,15 @@
1415

1516
public class GenerateQbvTest extends AbstractSchemasTest {
1617

18+
private DefaultSchemasLoader loader;
19+
20+
@BeforeEach
21+
void beforeEach() {
22+
loader = new DefaultSchemasLoader(client, CONTENT_DATABASE);
23+
}
24+
1725
@Test
1826
public void test() {
19-
DefaultSchemasLoader loader = new DefaultSchemasLoader(client, "Documents");
2027
Path path = Paths.get("src", "test", "resources", "qbv-schemas");
2128
List<DocumentFile> files = loader.loadSchemas(path.toString());
2229
assertEquals(3, files.size(),
@@ -52,7 +59,6 @@ public void test() {
5259

5360
@Test
5461
public void loadBadOptic() {
55-
DefaultSchemasLoader loader = new DefaultSchemasLoader(client, "Documents");
5662
Path path = Paths.get("src", "test", "resources", "qbv-bad-schemas");
5763
RuntimeException ex = assertThrows(RuntimeException.class, () -> loader.loadSchemas(path.toString()));
5864
assertTrue(ex.getMessage().contains("Query-Based View generation failed for file:"), "Unexpected message: " + ex.getMessage());
@@ -62,7 +68,6 @@ public void loadBadOptic() {
6268

6369
@Test
6470
public void schemaViewDoesNotExist() {
65-
DefaultSchemasLoader loader = new DefaultSchemasLoader(client, "Documents");
6671
Path path = Paths.get("src", "test", "resources", "qbv-no-tde-schemas");
6772
RuntimeException ex = assertThrows(RuntimeException.class, () -> loader.loadSchemas(path.toString()));
6873
assertTrue(ex.getMessage().contains("Query-Based View generation failed for file:"), "Unexpected message: " + ex.getMessage());
@@ -72,7 +77,6 @@ public void schemaViewDoesNotExist() {
7277

7378
@Test
7479
public void emptyDirectories() {
75-
DefaultSchemasLoader loader = new DefaultSchemasLoader(client, "Documents");
7680
Path path = Paths.get("src", "test", "resources", "qbv-empty-schemas");
7781
List<DocumentFile> files = loader.loadSchemas(path.toString());
7882
assertEquals(0, files.size());

src/test/java/com/marklogic/client/ext/schemasloader/impl/LoadSchemasTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import java.nio.file.Paths;
2626
import java.util.List;
2727

28-
import static org.junit.jupiter.api.Assertions.*;
28+
import static org.junit.jupiter.api.Assertions.assertEquals;
29+
import static org.junit.jupiter.api.Assertions.assertThrows;
30+
import static org.junit.jupiter.api.Assertions.assertTrue;
2931

3032
public class LoadSchemasTest extends AbstractSchemasTest {
3133

@@ -45,6 +47,9 @@ public void test() {
4547
assertTrue(uris.contains("/child/child.tdej"));
4648
assertTrue(uris.contains("/child/grandchild/grandchild.tdex"));
4749
assertTrue(uris.contains("/parent.tdex"));
50+
51+
// This assertion seems a little off - a TDE should be either a JSON or XML file. This doesn't seem to cause
52+
// any problems, but it also doesn't seem to make sense.
4853
assertTrue(uris.contains("/tde/ruleset.txt"));
4954
}
5055

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"database-name": "%%DATABASE%%",
3+
"schema-database": "%%SCHEMAS_DATABASE%%"
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"database-name": "%%SCHEMAS_DATABASE%%"
3+
}

0 commit comments

Comments
 (0)