moved unload from shared to enterprise
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
package com.inteligr8.alfresco.asie.enterprise.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.index.shard.ShardState;
|
||||
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
import org.springframework.extensions.webscripts.WebScriptResponse;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import com.inteligr8.alfresco.asie.Constants;
|
||||
import com.inteligr8.alfresco.asie.api.CoreAdminApi;
|
||||
import com.inteligr8.alfresco.asie.model.NodeParameterSet;
|
||||
import com.inteligr8.alfresco.asie.rest.AbstractAsieNodeWebScript;
|
||||
import com.inteligr8.alfresco.asie.service.ShardBackupService;
|
||||
import com.inteligr8.alfresco.asie.spi.ShardStateService;
|
||||
import com.inteligr8.solr.model.CoreMetadata;
|
||||
import com.inteligr8.solr.model.core.StatusRequest;
|
||||
import com.inteligr8.solr.model.core.StatusResponse;
|
||||
import com.inteligr8.solr.model.core.UnloadRequest;
|
||||
|
||||
public abstract class AbstractUnregisterNodeWebScript<T extends NodeParameterSet> extends AbstractAsieNodeWebScript {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Autowired
|
||||
@Qualifier(Constants.QUALIFIER_ASIE)
|
||||
private AttributeService attrService;
|
||||
|
||||
@Autowired
|
||||
private ShardBackupService sbs;
|
||||
|
||||
@Autowired
|
||||
private ShardStateService sss;
|
||||
|
||||
protected abstract T createParameters(WebScriptRequest req, String nodeHostname, int nodePort);
|
||||
|
||||
@Override
|
||||
protected void execute(WebScriptRequest req, WebScriptResponse res, String nodeHostname, int nodePort)
|
||||
throws IOException {
|
||||
T params = this.createParameters(req, nodeHostname, nodePort);
|
||||
|
||||
final List<Pair<Serializable[], ShardState>> matchingCores = new LinkedList<>();
|
||||
|
||||
AttributeQueryCallback callback = new AttributeQueryCallback() {
|
||||
@Override
|
||||
public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) {
|
||||
ShardState shardState = (ShardState) value;
|
||||
if (!matches(params, shardState))
|
||||
return true;
|
||||
|
||||
matchingCores.add(Pair.of(keys, shardState));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
this.sss.iterate(callback);
|
||||
|
||||
Serializable[] keys = new String[] {
|
||||
Constants.ATTR_ASIE,
|
||||
Constants.ATTR_UNLOADED,
|
||||
nodeHostname + ":" + nodePort
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> cores = (Map<String, String>) this.attrService.getAttribute(keys);
|
||||
if (cores == null)
|
||||
cores = new HashMap<>();
|
||||
try {
|
||||
for (Pair<Serializable[], ShardState> matchingCore : matchingCores) {
|
||||
ShardState shardNode = matchingCore.getValue();
|
||||
String core = shardNode.getPropertyBag().get("coreName");
|
||||
|
||||
try {
|
||||
StatusResponse status = this.getCoreStatus(nodeHostname, nodePort, core);
|
||||
if (status == null) {
|
||||
this.logger.warn("Registered host/core status could not be retrieved: {}:{}/solr/{}", nodeHostname, nodePort, core);
|
||||
} else {
|
||||
CoreMetadata coreMetadata = status.getStatus().getCores().get(core);
|
||||
if (coreMetadata == null || coreMetadata.getName() == null) {
|
||||
this.logger.warn("Registered core does not actually exist on the node host; could be a DNS issue: {}:{}/solr/{}", nodeHostname, nodePort, core);
|
||||
} else {
|
||||
this.unloadCore(nodeHostname, nodePort, core);
|
||||
cores.put(core, coreMetadata.getInstancePath());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.sss.remove(matchingCore.getKey());
|
||||
this.sbs.forget(shardNode);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// FIXME maybe a separate tx?
|
||||
this.attrService.setAttribute((Serializable) cores, keys);
|
||||
}
|
||||
|
||||
res.setStatus(HttpStatus.OK.value());
|
||||
}
|
||||
|
||||
protected boolean matches(T params, ShardState shardState) {
|
||||
if (!params.getHostname().equalsIgnoreCase(shardState.getShardInstance().getHostName())) {
|
||||
InetAddress nodeAddress = params.getAddress();
|
||||
if (nodeAddress == null)
|
||||
return false;
|
||||
InetAddress shardNodeAddress = resolve(shardState.getShardInstance().getHostName());
|
||||
if (!nodeAddress.equals(shardNodeAddress))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (params.getPort() != shardState.getShardInstance().getPort())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private InetAddress resolve(String hostname) {
|
||||
try {
|
||||
return InetAddress.getByName(hostname);
|
||||
} catch (UnknownHostException uhe) {
|
||||
// suppress
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected StatusResponse getCoreStatus(String nodeHostname, int nodePort, String core) {
|
||||
this.logger.debug("Retrieving status for core {} on ASIE node: {}", core, nodeHostname);
|
||||
CoreAdminApi api = this.createApi(nodeHostname, nodePort);
|
||||
return api.getStatus(new StatusRequest().withCore(core));
|
||||
}
|
||||
|
||||
protected void unloadCore(String nodeHostname, int nodePort, String core) {
|
||||
this.logger.info("Unloading core {} on ASIE node: {}", core, nodeHostname);
|
||||
CoreAdminApi api = this.createApi(nodeHostname, nodePort);
|
||||
api.unload(new UnloadRequest().withCore(core));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,136 @@
|
||||
package com.inteligr8.alfresco.asie.enterprise.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.extensions.webscripts.WebScriptException;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
import org.springframework.extensions.webscripts.WebScriptResponse;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.inteligr8.alfresco.asie.Constants;
|
||||
import com.inteligr8.alfresco.asie.api.CoreAdminApi;
|
||||
import com.inteligr8.alfresco.asie.rest.AbstractAsieNodeWebScript;
|
||||
import com.inteligr8.solr.model.ExceptionResponse;
|
||||
import com.inteligr8.solr.model.core.CreateRequest;
|
||||
import com.inteligr8.solr.model.core.ReloadRequest;
|
||||
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.InternalServerErrorException;
|
||||
|
||||
@Component(value = "webscript.com.inteligr8.alfresco.asie.nodeShard.post")
|
||||
public class ReloadNodeShardWebScript extends AbstractAsieNodeWebScript {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
private final Pattern shardRangePattern = Pattern.compile("([0-9]+)-([0-9]+)");
|
||||
|
||||
@Autowired
|
||||
@Qualifier(Constants.QUALIFIER_ASIE)
|
||||
private AttributeService attrService;
|
||||
|
||||
@Override
|
||||
protected void execute(WebScriptRequest req, WebScriptResponse res, final String nodeHostname, final int nodePort)
|
||||
throws IOException {
|
||||
String shardCore = this.getRequiredPathParameter(req, "shardCore");
|
||||
int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class);
|
||||
String coreName = shardCore + "-" + shardId;
|
||||
|
||||
Serializable[] keys = new String[] {
|
||||
Constants.ATTR_ASIE,
|
||||
Constants.ATTR_UNLOADED,
|
||||
nodeHostname + ":" + nodePort
|
||||
};
|
||||
|
||||
Map<String, String> cores = this.fetchUnloadedCores(keys);
|
||||
if (!cores.containsKey(coreName)) {
|
||||
cores.putAll(this.fetchOtherCores(req));
|
||||
}
|
||||
|
||||
boolean changed = false;
|
||||
String coreInstancePath = cores.get(coreName);
|
||||
if (coreInstancePath == null)
|
||||
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The specified node/shard could not be found or formulated");
|
||||
|
||||
this.logger.info("Reloading core {} on ASIE node: {}", coreName, nodeHostname);
|
||||
CoreAdminApi api = this.createApi(nodeHostname, nodePort);
|
||||
try {
|
||||
api.create(new CreateRequest()
|
||||
.withCore(coreName)
|
||||
.withConfigDirectory(coreInstancePath));
|
||||
} catch (BadRequestException bre) {
|
||||
this.logger.warn("Core {} does not exist on ASIE node: {}; forgetting it", coreName, nodeHostname);
|
||||
cores.remove(coreName);
|
||||
changed = true;
|
||||
} catch (InternalServerErrorException isee) {
|
||||
ExceptionResponse response = isee.getResponse().readEntity(ExceptionResponse.class);
|
||||
if (response.getError() == null || response.getError().getMessage() == null || !response.getError().getMessage().endsWith(" already exists."))
|
||||
throw isee;
|
||||
|
||||
this.logger.warn("Core {} was already loaded on ASIE node: {}; reloading ...", coreName, nodeHostname);
|
||||
api.reload(new ReloadRequest()
|
||||
.withCore(coreName));
|
||||
}
|
||||
|
||||
if (changed)
|
||||
this.attrService.setAttribute((Serializable) cores, keys);
|
||||
|
||||
res.setStatus(HttpStatus.OK.value());
|
||||
}
|
||||
|
||||
private Map<String, String> fetchUnloadedCores(Serializable[] keys) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> cores = (Map<String, String>) this.attrService.getAttribute(keys);
|
||||
if (cores == null)
|
||||
cores = new HashMap<String, String>();
|
||||
return cores;
|
||||
}
|
||||
|
||||
private Map<String, String> fetchOtherCores(WebScriptRequest req) {
|
||||
String coreName = this.getOptionalQueryParameter(req, "coreName");
|
||||
if (coreName == null)
|
||||
coreName = this.getOptionalQueryParameter(req, "core");
|
||||
String shardRange = this.getOptionalQueryParameter(req, "shardRange");
|
||||
Short shardCount = this.getOptionalQueryParameter(req, "shardCount", Short.class);
|
||||
if (coreName == null || shardRange == null || shardCount == null)
|
||||
return Collections.emptyMap();
|
||||
|
||||
String template = this.getOptionalQueryParameter(req, "template");
|
||||
if (template == null)
|
||||
template = "rerank";
|
||||
Short nodeId = this.getOptionalQueryParameter(req, "nodeId", Short.class);
|
||||
if (nodeId == null)
|
||||
nodeId = 1;
|
||||
Short nodeCount = this.getOptionalQueryParameter(req, "nodeCount", Short.class);
|
||||
if (nodeCount == null)
|
||||
nodeCount = 2;
|
||||
|
||||
String baseConfigDirectory = template + "--" + coreName + "--shards--" + shardCount + "-x-1--node--" + nodeId + "-of-" + nodeCount;
|
||||
|
||||
Matcher matcher = this.shardRangePattern.matcher(shardRange);
|
||||
if (!matcher.find())
|
||||
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The 'shardRange' query parameter value is not formatted as expected");
|
||||
|
||||
Map<String, String> cores = new HashMap<>();
|
||||
|
||||
short startShardId = Short.parseShort(matcher.group(1));
|
||||
short endShardId = Short.parseShort(matcher.group(2));
|
||||
for (short shardId = startShardId; shardId <= endShardId; shardId++) {
|
||||
String shardConfigDirectory = baseConfigDirectory + "/" + coreName + "-" + shardId;
|
||||
cores.put(coreName, shardConfigDirectory);
|
||||
}
|
||||
|
||||
return cores;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
package com.inteligr8.alfresco.asie.enterprise.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.extensions.webscripts.WebScriptException;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
import org.springframework.extensions.webscripts.WebScriptResponse;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.inteligr8.alfresco.asie.Constants;
|
||||
import com.inteligr8.alfresco.asie.api.CoreAdminApi;
|
||||
import com.inteligr8.alfresco.asie.rest.AbstractAsieNodeWebScript;
|
||||
import com.inteligr8.solr.model.ExceptionResponse;
|
||||
import com.inteligr8.solr.model.core.CreateRequest;
|
||||
import com.inteligr8.solr.model.core.ReloadRequest;
|
||||
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.InternalServerErrorException;
|
||||
|
||||
@Component(value = "webscript.com.inteligr8.alfresco.asie.node.post")
|
||||
public class ReloadNodeWebScript extends AbstractAsieNodeWebScript {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
private final Pattern shardRangePattern = Pattern.compile("([0-9]+)-([0-9]+)");
|
||||
|
||||
@Autowired
|
||||
@Qualifier(Constants.QUALIFIER_ASIE)
|
||||
private AttributeService attrService;
|
||||
|
||||
@Override
|
||||
protected void execute(WebScriptRequest req, WebScriptResponse res, final String nodeHostname, final int nodePort)
|
||||
throws IOException {
|
||||
Serializable[] keys = new String[] {
|
||||
Constants.ATTR_ASIE,
|
||||
Constants.ATTR_UNLOADED,
|
||||
nodeHostname + ":" + nodePort
|
||||
};
|
||||
|
||||
Map<String, String> cores = this.fetchUnloadedCores(keys);
|
||||
cores.putAll(this.fetchOtherCores(req));
|
||||
if (cores.isEmpty())
|
||||
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The specified node was not found or formulated");
|
||||
|
||||
boolean changed = false;
|
||||
|
||||
Iterator<Entry<String, String>> i = cores.entrySet().iterator();
|
||||
while (i.hasNext()) {
|
||||
Entry<String, String> core = i.next();
|
||||
String coreName = core.getKey();
|
||||
String coreInstancePath = core.getValue();
|
||||
|
||||
this.logger.info("Reloading core {} on ASIE node: {}", coreName, nodeHostname);
|
||||
CoreAdminApi api = this.createApi(nodeHostname, nodePort);
|
||||
try {
|
||||
api.create(new CreateRequest()
|
||||
.withCore(coreName)
|
||||
.withConfigDirectory(coreInstancePath));
|
||||
} catch (BadRequestException bre) {
|
||||
this.logger.warn("Core {} does not exist on ASIE node: {}; forgetting it", coreName, nodeHostname);
|
||||
i.remove();
|
||||
changed = true;
|
||||
} catch (InternalServerErrorException isee) {
|
||||
ExceptionResponse response = isee.getResponse().readEntity(ExceptionResponse.class);
|
||||
if (response.getError() == null || response.getError().getMessage() == null || !response.getError().getMessage().endsWith(" already exists."))
|
||||
throw isee;
|
||||
|
||||
this.logger.info("Core {} was already loaded on ASIE node: {}; reloading ...", coreName, nodeHostname);
|
||||
api.reload(new ReloadRequest()
|
||||
.withCore(coreName));
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
this.attrService.setAttribute((Serializable) cores, keys);
|
||||
|
||||
res.setStatus(HttpStatus.OK.value());
|
||||
}
|
||||
|
||||
private Map<String, String> fetchUnloadedCores(Serializable[] keys) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> cores = (Map<String, String>) this.attrService.getAttribute(keys);
|
||||
if (cores == null)
|
||||
cores = new HashMap<String, String>();
|
||||
return cores;
|
||||
}
|
||||
|
||||
private Map<String, String> fetchOtherCores(WebScriptRequest req) {
|
||||
String coreName = this.getOptionalQueryParameter(req, "coreName");
|
||||
if (coreName == null)
|
||||
coreName = this.getOptionalQueryParameter(req, "core");
|
||||
String shardRange = this.getOptionalQueryParameter(req, "shardRange");
|
||||
Short shardCount = this.getOptionalQueryParameter(req, "shardCount", Short.class);
|
||||
if (coreName == null || shardRange == null || shardCount == null)
|
||||
return Collections.emptyMap();
|
||||
|
||||
String template = this.getOptionalQueryParameter(req, "template");
|
||||
if (template == null)
|
||||
template = "rerank";
|
||||
Short nodeId = this.getOptionalQueryParameter(req, "nodeId", Short.class);
|
||||
if (nodeId == null)
|
||||
nodeId = 1;
|
||||
Short nodeCount = this.getOptionalQueryParameter(req, "nodeCount", Short.class);
|
||||
if (nodeCount == null)
|
||||
nodeCount = 2;
|
||||
|
||||
String baseConfigDirectory = template + "--" + coreName + "--shards--" + shardCount + "-x-1--node--" + nodeId + "-of-" + nodeCount;
|
||||
|
||||
Matcher matcher = this.shardRangePattern.matcher(shardRange);
|
||||
if (!matcher.find())
|
||||
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The 'shardRange' query parameter value is not formatted as expected");
|
||||
|
||||
Map<String, String> cores = new HashMap<>();
|
||||
|
||||
short startShardId = Short.parseShort(matcher.group(1));
|
||||
short endShardId = Short.parseShort(matcher.group(2));
|
||||
for (short shardId = startShardId; shardId <= endShardId; shardId++) {
|
||||
String shardConfigDirectory = baseConfigDirectory + "/" + coreName + "-" + shardId;
|
||||
cores.put(coreName, shardConfigDirectory);
|
||||
}
|
||||
|
||||
return cores;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.inteligr8.alfresco.asie.enterprise.rest;
|
||||
|
||||
import org.alfresco.repo.index.shard.ShardState;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.inteligr8.alfresco.asie.model.NodeShardParameterSet;
|
||||
import com.inteligr8.alfresco.asie.model.ShardSet;
|
||||
|
||||
@Component(value = "webscript.com.inteligr8.alfresco.asie.nodeShard.delete")
|
||||
public class UnloadNodeShardWebScript extends AbstractUnregisterNodeWebScript<NodeShardParameterSet> {
|
||||
|
||||
@Override
|
||||
protected NodeShardParameterSet createParameters(WebScriptRequest req, String nodeHostname, int nodePort) {
|
||||
ShardSet shardSet = this.getRequiredPathParameter(req, "shardSet", ShardSet.class);
|
||||
int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class);
|
||||
|
||||
return new NodeShardParameterSet(nodeHostname, nodePort, shardSet, shardId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(NodeShardParameterSet params, ShardState shardState) {
|
||||
if (!params.getShardSet().isFor(shardState))
|
||||
return false;
|
||||
if (params.getShardId() != shardState.getShardInstance().getShard().getInstance())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.inteligr8.alfresco.asie.enterprise.rest;
|
||||
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.inteligr8.alfresco.asie.model.NodeParameterSet;
|
||||
|
||||
@Component(value = "webscript.com.inteligr8.alfresco.asie.node.delete")
|
||||
public class UnloadNodeWebScript extends AbstractUnregisterNodeWebScript<NodeParameterSet> {
|
||||
|
||||
@Override
|
||||
protected NodeParameterSet createParameters(WebScriptRequest req, String nodeHostname, int nodePort) {
|
||||
return new NodeParameterSet(nodeHostname, nodePort);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user