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

Commit ffcc45d

Browse files
committed
#346 Can now deploy databases via CMA
Doesn't yet support sub-databases though
1 parent 9934d7b commit ffcc45d

20 files changed

+249
-117
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
group=com.marklogic
22
javadocsDir=../gh-pages-marklogic-java/javadocs
3-
version=3.14.0
3+
version=3.14.346
44
mlJavaclientUtilVersion=3.12.0
55
mlJunitVersion=3.1.0
66

src/main/java/com/marklogic/appdeployer/AppConfig.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public class AppConfig {
7474
private boolean catchUndeployExceptions = false;
7575

7676
private boolean deployAmpsWithCma = false;
77+
private boolean deployDatabasesWithCma = false;
7778
private boolean deployForestsWithCma = false;
7879
private boolean deployPrivilegesWithCma = false;
7980
private boolean mergeResources = true;
@@ -1396,4 +1397,12 @@ public DatabaseClient.ConnectionType getAppServicesConnectionType() {
13961397
public void setAppServicesConnectionType(DatabaseClient.ConnectionType appServicesConnectionType) {
13971398
this.appServicesConnectionType = appServicesConnectionType;
13981399
}
1400+
1401+
public boolean isDeployDatabasesWithCma() {
1402+
return deployDatabasesWithCma;
1403+
}
1404+
1405+
public void setDeployDatabasesWithCma(boolean deployDatabasesWithCma) {
1406+
this.deployDatabasesWithCma = deployDatabasesWithCma;
1407+
}
13991408
}

src/main/java/com/marklogic/appdeployer/DefaultAppConfigFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ public void initialize() {
7676
config.setDeployAmpsWithCma(Boolean.parseBoolean(prop));
7777
});
7878

79+
propertyConsumerMap.put("mlDeployDatabasesWithCma", (config, prop) -> {
80+
logger.info("Deploy databases and forests" + cmaMessage + prop);
81+
config.setDeployDatabasesWithCma(Boolean.parseBoolean(prop));
82+
});
83+
7984
propertyConsumerMap.put("mlDeployForestsWithCma", (config, prop) -> {
8085
logger.info("Deploy forests" + cmaMessage + prop);
8186
config.setDeployForestsWithCma(Boolean.parseBoolean(prop));

src/main/java/com/marklogic/appdeployer/command/AbstractCommand.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ protected String copyFileToString(File f, CommandContext context) {
157157
* @return
158158
*/
159159
protected SaveReceipt saveResource(ResourceManager mgr, CommandContext context, File resourceFile) {
160-
String payload = readResourceFromFile(mgr, context, resourceFile);
160+
String payload = readResourceFromFile(context, resourceFile);
161161

162162
if (payload != null && resourceMergingIsSupported(context)) {
163163
try {
@@ -344,9 +344,9 @@ protected BiPredicate<ResourceReference, ResourceReference> getBiPredicateForMer
344344
* @param f
345345
* @return
346346
*/
347-
protected String readResourceFromFile(ResourceManager mgr, CommandContext context, File f) {
347+
protected String readResourceFromFile(CommandContext context, File f) {
348348
String payload = copyFileToString(f, context);
349-
return adjustPayloadBeforeSavingResource(mgr, context, f, payload);
349+
return adjustPayloadBeforeSavingResource(context, f, payload);
350350
}
351351

352352
/**
@@ -386,13 +386,12 @@ protected void afterResourceSaved(ResourceManager mgr, CommandContext context, R
386386
* <p>
387387
* A subclass can return null from this method to indicate that the resource should not be saved.
388388
*
389-
* @param mgr
390389
* @param context
391390
* @param f
392391
* @param payload
393392
* @return
394393
*/
395-
protected String adjustPayloadBeforeSavingResource(ResourceManager mgr, CommandContext context, File f, String payload) {
394+
protected String adjustPayloadBeforeSavingResource(CommandContext context, File f, String payload) {
396395
String[] props = context.getAppConfig().getExcludeProperties();
397396
if (props != null && props.length > 0) {
398397
logger.info(format("Excluding properties %s from payload", Arrays.asList(props).toString()));

src/main/java/com/marklogic/appdeployer/command/AbstractResourceCommand.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
import com.marklogic.mgmt.mapper.DefaultResourceMapper;
1111
import com.marklogic.mgmt.mapper.ResourceMapper;
1212
import com.marklogic.mgmt.resource.ResourceManager;
13+
import com.marklogic.mgmt.util.ObjectMapperFactory;
1314

1415
import java.io.File;
16+
import java.io.IOException;
1517
import java.util.ArrayList;
1618
import java.util.List;
1719

@@ -152,15 +154,21 @@ protected boolean useCmaForDeployingResources(CommandContext context) {
152154
* @param resourceDir
153155
*/
154156
protected void deployResourcesViaCma(CommandContext context, File resourceDir) {
155-
ResourceMapper resourceMapper = new DefaultResourceMapper(new API(context.getManageClient()));
156157
Configuration config = new Configuration();
157158
for (File f : listFilesInDirectory(resourceDir, context)) {
158159
if (logger.isInfoEnabled()) {
159160
logger.info("Processing file: " + f.getAbsolutePath());
160161
}
161-
String payload = readResourceFromFile(null, context, f);
162+
String payload = readResourceFromFile(context, f);
162163
if (payload != null && payload.trim().length() > 0) {
163-
((SupportsCmaCommand) this).addResourceToConfiguration(payload, resourceMapper, config);
164+
payload = convertXmlPayloadToJsonIfNecessary(context, payload);
165+
ObjectNode objectNode;
166+
try {
167+
objectNode = (ObjectNode)ObjectMapperFactory.getObjectMapper().readTree(payload);
168+
} catch (IOException e) {
169+
throw new RuntimeException("Unable to read JSON, cause: " + e.getMessage(), e);
170+
}
171+
((SupportsCmaCommand) this).addResourceToConfiguration(objectNode, config);
164172
}
165173
}
166174
new Configurations(config).submit(context.getManageClient());
@@ -173,10 +181,8 @@ protected void deployResourcesViaCma(CommandContext context, File resourceDir) {
173181
*/
174182
protected void saveMergedResourcesViaCma(CommandContext context, List<ResourceReference> mergedReferences) {
175183
Configuration config = new Configuration();
176-
ResourceMapper resourceMapper = new DefaultResourceMapper(new API(context.getManageClient()));
177184
for (ResourceReference reference : mergedReferences) {
178-
String payload = reference.getObjectNode().toString();
179-
((SupportsCmaCommand)this).addResourceToConfiguration(payload, resourceMapper, config);
185+
((SupportsCmaCommand)this).addResourceToConfiguration(reference.getObjectNode(), config);
180186
}
181187
new Configurations(config).submit(context.getManageClient());
182188
}
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.marklogic.appdeployer.command;
22

3+
import com.fasterxml.jackson.databind.node.ObjectNode;
34
import com.marklogic.mgmt.api.configuration.Configuration;
4-
import com.marklogic.mgmt.mapper.ResourceMapper;
55

66
/**
77
* Commands that wish to optimize the deployment of resources in a single resource directory should implement this
@@ -13,11 +13,10 @@ public interface SupportsCmaCommand {
1313
boolean cmaShouldBeUsed(CommandContext context);
1414

1515
/**
16-
* @param payload the contents of the resource file, with all tokens replaced
17-
* @param resourceMapper the implementer is likely to use this to convert the payload into a subclass of Resource
18-
* so that it doesn't matter if the payload is JSON or XML
19-
* @param configuration the CMA Configuration object that the payload should be added to
16+
* @param resource the contents of the resource file, with all tokens replaced
17+
* @param configuration the CMA Configuration object that the payload should be added to
2018
*/
21-
void addResourceToConfiguration(String payload, ResourceMapper resourceMapper, Configuration configuration);
19+
void addResourceToConfiguration(ObjectNode resource, Configuration configuration);
20+
2221

2322
}

src/main/java/com/marklogic/appdeployer/command/appservers/UpdateRestApiServersCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public void execute(CommandContext context) {
4242
if (appConfig.isTestPortSet()) {
4343
String payload = copyFileToString(f);
4444
payload = payloadTokenReplacer.replaceTokens(payload, appConfig, true);
45-
payload = adjustPayloadBeforeSavingResource(mgr, context, f, payload);
45+
payload = adjustPayloadBeforeSavingResource(context, f, payload);
4646
mgr.save(payload);
4747
}
4848
}

src/main/java/com/marklogic/appdeployer/command/cma/DeployConfigurationsCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void execute(CommandContext context) {
2929
if (logger.isInfoEnabled()) {
3030
logger.info("Processing file: " + f.getAbsolutePath());
3131
}
32-
String payload = readResourceFromFile(null, context, f);
32+
String payload = readResourceFromFile(context, f);
3333
SaveReceipt receipt = mgr.save(payload);
3434
afterResourceSaved(null, context, new ResourceReference(f, null), receipt);
3535
}

src/main/java/com/marklogic/appdeployer/command/databases/DeployDatabaseCommand.java

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -123,29 +123,53 @@ public Integer getUndoSortOrder() {
123123

124124
@Override
125125
public void execute(CommandContext context) {
126-
String payload = buildPayload(context);
126+
String payload = buildPayloadForSaving(context);
127127
if (payload != null) {
128-
DatabaseManager dbMgr = new DatabaseManager(context.getManageClient());
129-
databaseName = dbMgr.getResourceId(payload);
128+
DatabaseManager dbMgr = new DatabaseManager(context.getManageClient());
129+
databaseName = dbMgr.getResourceId(payload);
130130

131-
payload = adjustPayloadBeforeSavingResource(dbMgr, context, null, payload);
132131
dbMgr.save(payload);
133132

134-
if (shouldCreateForests(context, payload)) {
135-
deployForestsCommand = buildDeployForestsCommand(databaseName, context);
133+
DeployForestsCommand tempCommand = buildDeployForestsCommand(databaseName, context);
134+
if (tempCommand != null) {
135+
this.deployForestsCommand = tempCommand;
136136
if (postponeForestCreation) {
137137
logger.info("Postponing creation of forests for database: " + databaseName);
138138
} else {
139139
deployForestsCommand.execute(context);
140140
}
141141
}
142142

143-
if (!isSubDatabase()) {
144-
new DeploySubDatabasesCommand(databaseName, deployDatabaseCommandFactory).execute(context);
145-
}
143+
deploySubDatabases(this.databaseName, context);
146144
}
147145
}
148146

147+
/**
148+
* If this is not a sub-database, then deploy sub-databases if any have been configured for this database.
149+
*
150+
* @param context
151+
*/
152+
public void deploySubDatabases(String dbName, CommandContext context) {
153+
if (!isSubDatabase()) {
154+
new DeploySubDatabasesCommand(dbName, deployDatabaseCommandFactory).execute(context);
155+
}
156+
}
157+
158+
/**
159+
* Performs all work necessary to construct a payload that can either be saved immediately or included in a CMA
160+
* configuration.
161+
*
162+
* @param context
163+
* @return
164+
*/
165+
public String buildPayloadForSaving(CommandContext context) {
166+
String payload = buildPayload(context);
167+
if (payload != null) {
168+
payload = adjustPayloadBeforeSavingResource(context, null, payload);
169+
}
170+
return payload;
171+
}
172+
149173
/**
150174
* When deleting databases, resource files are not yet merged together first. This should only mean that some
151175
* unnecessary delete calls are made.
@@ -267,11 +291,11 @@ else if (databaseFilename != null) {
267291
* name is extracted from the payload via a PayloadParser. This check can be disabled by setting
268292
* checkForCustomForests to false.
269293
*
294+
* @param databaseName
270295
* @param context
271-
* @param payload
272296
* @return
273297
*/
274-
protected boolean shouldCreateForests(CommandContext context, String payload) {
298+
protected boolean shouldCreateForests(String databaseName, CommandContext context) {
275299
if (!context.getAppConfig().isCreateForests()) {
276300
if (logger.isInfoEnabled()) {
277301
logger.info("Forest creation is disabled, so not creating any forests");
@@ -280,11 +304,9 @@ protected boolean shouldCreateForests(CommandContext context, String payload) {
280304
}
281305

282306
if (isCheckForCustomForests()) {
283-
PayloadParser parser = new PayloadParser();
284-
String dbName = parser.getPayloadFieldValue(payload, "database-name");
285-
boolean customForestsDontExist = !customForestsExist(context, dbName);
307+
boolean customForestsDontExist = !customForestsExist(context, databaseName);
286308
if (!customForestsDontExist && logger.isInfoEnabled()) {
287-
logger.info("Found custom forests for database " + dbName + ", so not creating default forests");
309+
logger.info("Found custom forests for database " + databaseName + ", so not creating default forests");
288310
}
289311
return customForestsDontExist;
290312
}
@@ -315,14 +337,17 @@ protected boolean customForestsExist(CommandContext context, String dbName) {
315337
*
316338
* @param databaseName
317339
* @param context
318-
* @return
340+
* @return will return null if it's determined that no forests should be created for the database
319341
*/
320342
public DeployForestsCommand buildDeployForestsCommand(String databaseName, CommandContext context) {
321-
DeployForestsCommand c = new DeployForestsCommand(databaseName);
322-
c.setForestsPerHost(getForestsPerHost());
323-
c.setCreateForestsOnEachHost(createForestsOnEachHost);
324-
c.setForestFilename(forestFilename);
325-
return c;
343+
if (shouldCreateForests(databaseName, context)) {
344+
DeployForestsCommand c = new DeployForestsCommand(databaseName);
345+
c.setForestsPerHost(getForestsPerHost());
346+
c.setCreateForestsOnEachHost(createForestsOnEachHost);
347+
c.setForestFilename(forestFilename);
348+
return c;
349+
}
350+
return null;
326351
}
327352

328353
protected String buildDefaultDatabasePayload(CommandContext context) {

src/main/java/com/marklogic/appdeployer/command/databases/DeployOtherDatabasesCommand.java

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.marklogic.appdeployer.command.AbstractUndoableCommand;
77
import com.marklogic.appdeployer.command.CommandContext;
88
import com.marklogic.appdeployer.command.SortOrderConstants;
9+
import com.marklogic.appdeployer.command.SupportsCmaCommand;
910
import com.marklogic.appdeployer.command.forests.DeployForestsCommand;
1011
import com.marklogic.mgmt.PayloadParser;
1112
import com.marklogic.mgmt.api.API;
@@ -87,20 +88,27 @@ public void execute(CommandContext context) {
8788
logger.info("Not sorting databases by dependencies, will sort them by their filenames instead");
8889
}
8990

90-
databasePlans.forEach(databasePlan -> {
91-
databasePlan.getDeployDatabaseCommand().execute(context);
92-
});
91+
if (context.getAppConfig().isDeployDatabasesWithCma()) {
92+
deployDatabasesAndForestsViaCma(context, databasePlans);
93+
}
9394

94-
// Either create forests in one bulk CMA request, or via a command per database
95-
if (context.getAppConfig().isDeployForestsWithCma()) {
96-
deployAllForestsInSingleCmaRequest(context, databasePlans);
97-
} else {
95+
else {
96+
// Otherwise, create each database one at a time, which also handles sub-databases
9897
databasePlans.forEach(databasePlan -> {
99-
DeployForestsCommand dfc = databasePlan.getDeployDatabaseCommand().getDeployForestsCommand();
100-
if (dfc != null) {
101-
dfc.execute(context);
102-
}
98+
databasePlan.getDeployDatabaseCommand().execute(context);
10399
});
100+
101+
// Either create forests in one bulk CMA request, or via a command per database
102+
if (context.getAppConfig().isDeployForestsWithCma()) {
103+
deployAllForestsInSingleCmaRequest(context, databasePlans);
104+
} else {
105+
databasePlans.forEach(databasePlan -> {
106+
DeployForestsCommand dfc = databasePlan.getDeployDatabaseCommand().getDeployForestsCommand();
107+
if (dfc != null) {
108+
dfc.execute(context);
109+
}
110+
});
111+
}
104112
}
105113
}
106114

@@ -340,6 +348,42 @@ protected void buildDeployDatabaseCommands(CommandContext context, List<Database
340348
});
341349
}
342350

351+
/**
352+
* As of 3.15.0, if databases are to be deployed via CMA, then their forests will also be deployed via CMA,
353+
* regardless of the setting on the AppConfig instance.
354+
*
355+
* Also as of 3.15.0, sub-databases and their forests are never deployed by CMA. Will support this in a future
356+
* release.
357+
*
358+
* @param context
359+
* @param databasePlans
360+
*/
361+
protected void deployDatabasesAndForestsViaCma(CommandContext context, List<DatabasePlan> databasePlans) {
362+
Configuration dbConfig = new Configuration();
363+
// Forests must be included in a separate configuration object
364+
Configuration forestConfig = new Configuration();
365+
366+
databasePlans.forEach(plan -> {
367+
final DeployDatabaseCommand deployDatabaseCommand = plan.getDeployDatabaseCommand();
368+
String payload = deployDatabaseCommand.buildPayloadForSaving(context);
369+
payload = convertXmlPayloadToJsonIfNecessary(context, payload);
370+
dbConfig.addDatabase(payload);
371+
372+
DeployForestsCommand deployForestsCommand = deployDatabaseCommand.buildDeployForestsCommand(plan.getDatabaseName(), context);
373+
if (deployForestsCommand != null) {
374+
deployForestsCommand.buildForests(context, false).forEach(forest -> forestConfig.addForest(forest.toObjectNode()));
375+
}
376+
});
377+
378+
new Configurations(dbConfig, forestConfig).submit(context.getManageClient());
379+
380+
// Now account for sub-databases, but not with CMA
381+
databasePlans.forEach(plan -> {
382+
final DeployDatabaseCommand deployDatabaseCommand = plan.getDeployDatabaseCommand();
383+
deployDatabaseCommand.deploySubDatabases(plan.getDatabaseName(), context);
384+
});
385+
}
386+
343387
/**
344388
* Each DatabasePlan is expected to have constructed a DeployForestCommand, but not executed it. Each
345389
* DeployForestCommand can then be used to build a list of forests. All of those forests can be combined into a
@@ -350,15 +394,15 @@ protected void buildDeployDatabaseCommands(CommandContext context, List<Database
350394
*/
351395
protected void deployAllForestsInSingleCmaRequest(CommandContext context, List<DatabasePlan> databasePlans) {
352396
List<Forest> allForests = new ArrayList<>();
353-
for (DatabasePlan reference : databasePlans) {
354-
DeployForestsCommand deployForestsCommand = reference.getDeployDatabaseCommand().getDeployForestsCommand();
355-
if (deployForestsCommand != null) {
356-
allForests.addAll(deployForestsCommand.buildForests(context, false));
397+
databasePlans.forEach(plan -> {
398+
DeployForestsCommand dfc = plan.getDeployDatabaseCommand().getDeployForestsCommand();
399+
if (dfc != null) {
400+
allForests.addAll(dfc.buildForests(context, false));
357401
}
358-
}
402+
});
359403
if (!allForests.isEmpty()) {
360404
Configuration config = new Configuration();
361-
config.setForests(allForests);
405+
allForests.forEach(forest -> config.addForest(forest.toObjectNode()));
362406
new Configurations(config).submit(context.getManageClient());
363407
}
364408
}

0 commit comments

Comments
 (0)