package com.bagri.tools.vvm.service;
import javax.management.*;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularData;
import com.bagri.tools.vvm.model.*;
import java.util.*;
import java.util.logging.Logger;
public class ClusterServiceProvider implements ClusterManagementService, SchemaManagementService {
private static final Logger LOGGER = Logger.getLogger(ClusterServiceProvider.class.getName());
private MBeanServerConnection connection;
private Map<String, DocumentManagementService> docMgrs = new HashMap<>();
public ClusterServiceProvider(MBeanServerConnection connection) {
this.connection = connection;
}
public void close() {
this.connection = null;
}
@Override
public Node getNode(ObjectName on) throws ServiceException {
try {
String name = (String) connection.getAttribute(on, "Name");
String[] deployedSchemas = new String[0];
CompositeData optionsCd = null;
try {
deployedSchemas = (String[]) connection.getAttribute(on, "DeployedSchemas");
if (null == deployedSchemas) {
deployedSchemas = new String[0];
}
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getNode.schemas", e);
}
try {
optionsCd = (CompositeData) connection.getAttribute(on, "Options");
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getNode.options", e);
}
Node node = new Node(on, name);
node.setNodeOptions(convertCompositeToNodeOptions(optionsCd));
node.setDeployedSchemas(Arrays.asList(deployedSchemas));
return node;
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getNode", e);
throw new ServiceException(e);
}
}
@Override
public List<Node> getNodes() throws ServiceException {
try {
Set<ObjectInstance> instances = connection.queryMBeans(new ObjectName("com.bagri.db:type=Node,name=*"), null);
List<Node> nodes = new ArrayList<Node>();
for (ObjectInstance instance : instances) {
Node node = getNode(instance.getObjectName());
nodes.add(node);
}
return nodes;
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getNodes", e);
throw new ServiceException(e);
}
}
@Override
public void saveNode(Node node) throws ServiceException {
try {
// TODO: What da hell, refactor it!!!. Define equals for NodeOption or inherit NodeOptions from Properties
List<NodeOption> existing = convertCompositeToNodeOptions((CompositeData) connection.getAttribute(node.getObjectName(), "Options"));
List<String> toDelete = new ArrayList<String>();
for (NodeOption option : existing) {
String key = option.getOptionName();
boolean found = false;
for (NodeOption newOption : node.getNodeOptions()) {
if (key.equals(newOption.getOptionName())) {
found = true;
break;
}
}
if (!found) {
toDelete.add(key);
}
}
for (String key : toDelete) {
connection.invoke(node.getObjectName()
, "removeOption"
, new Object[] {key}
, new String[] {String.class.getName()});
}
for (NodeOption option : node.getNodeOptions()) {
connection.invoke(node.getObjectName()
, "setOption", new Object[] {option.getOptionName()
, option.getOptionValue()}
, new String[]{String.class.getName()
, String.class.getName()});
}
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "saveNode", e);
throw new ServiceException(e);
}
}
@Override
public void addNode(Node node) throws ServiceException {
String optionsStr = convertOptionsToString(node.getNodeOptions());
try {
connection.invoke(new ObjectName("com.bagri.db:type=Management,name=ClusterManagement")
, "addNode"
, new Object[] {node.getName(), optionsStr}
, new String[] {String.class.getName(), String.class.getName()});
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "addNode", e);
throw new ServiceException(e);
}
}
@Override
public void deleteNode(Node node) throws ServiceException {
try {
connection.invoke(new ObjectName("com.bagri.db:type=Management,name=ClusterManagement")
, "deleteNode"
, new Object[] {node.getName()}
, new String[] {String.class.getName()});
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "deleteNode", e);
throw new ServiceException(e);
}
}
@Override
public DocumentManagementService getDocumentManagement(String schemaName) {
DocumentManagementService docMgr = docMgrs.get(schemaName);
if (docMgr == null) {
docMgr = new DocumentServiceProvider(connection, schemaName);
docMgrs.put(schemaName, docMgr);
}
return docMgr;
}
@Override
public List<Schema> getSchemas() throws ServiceException {
try {
Set<ObjectInstance> instances = connection.queryMBeans(new ObjectName("com.bagri.db:type=Schema,name=*"), null);
List<Schema> schemas = new ArrayList<Schema>();
for (ObjectInstance instance : instances) {
Schema schema = extractSchema(instance);
schemas.add(schema);
}
return schemas;
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getSchemas", e);
throw new ServiceException(e);
}
}
@Override
public Properties getDefaultProperties() throws ServiceException {
try {
CompositeData cd = (CompositeData) connection.invoke(new ObjectName("com.bagri.db:type=Management,name=SchemaManagement")
, "getDefaultProperties"
, null
, null);
return convertCompositeToProperties(cd);
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getDefaultProperties", e);
throw new ServiceException(e);
}
}
@Override
public void setDefaultProperty(Property property) throws ServiceException {
try {
connection.invoke(new ObjectName("com.bagri.db:type=Management,name=SchemaManagement")
, "setDefaultProperty"
, new Object[] {property.getPropertyName(), property.getPropertyValue()}
, new String[] {String.class.getName(), String.class.getName()});
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "setDefaultProperty", e);
throw new ServiceException(e);
}
}
@Override
public void addSchema(Schema schema) throws ServiceException {
try {
connection.invoke(new ObjectName("com.bagri.db:type=Management,name=SchemaManagement")
, "createSchema"
, new Object[] {schema.getSchemaName(), schema.getDescription(), convertPropertiesToString(schema.getProperties())}
, new String[] {String.class.getName(), String.class.getName(), String.class.getName()});
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "addSchema", e);
throw new ServiceException(e);
}
}
@Override
public Schema getSchema(String schemaName) throws ServiceException {
try {
ObjectInstance oi = connection.getObjectInstance(new ObjectName("com.bagri.db:type=Schema,name=" + schemaName));
if (null != oi) {
return extractSchema(oi);
}
return null;
} catch (InstanceNotFoundException e) {
return null;
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getSchema", e);
throw new ServiceException(e);
}
}
@Override
public Schema getSchema(ObjectName objectName) throws ServiceException {
try {
ObjectInstance oi = connection.getObjectInstance(objectName);
if (null != oi) {
return extractSchema(oi);
}
return null;
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getSchema", e);
throw new ServiceException(e);
}
}
@Override
public void saveSchema(Schema schema) throws ServiceException {
try {
if (null != schema.getDescription()) {
connection.invoke(schema.getObjectName()
, "setDescription"
, new Object[] {schema.getDescription()}
, new String[] {String.class.getName()});
}
if (schema.isActive()) {
connection.invoke(schema.getObjectName()
, "activateSchema"
, null
, null);
} else {
connection.invoke(schema.getObjectName()
, "deactivateSchema"
, null
, null);
}
Properties existing = convertCompositeToProperties((CompositeData) connection.invoke(schema.getObjectName(), "getProperties", null, null));
existing.keySet().removeAll(schema.getProperties().keySet());
Set<Object> toDelete = existing.keySet();
for (Object key : toDelete) {
connection.invoke(schema.getObjectName()
, "removeProperty"
, new Object[] {key.toString()}
, new String[] {String.class.getName()});
}
for (Object key : schema.getProperties().keySet()) {
connection.invoke(schema.getObjectName()
, "setProperty"
, new Object[] {key.toString(), schema.getProperties().getProperty(key.toString())}
, new String[]{String.class.getName(), String.class.getName()});
}
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "saveSchema", e);
throw new ServiceException(e);
}
}
@Override
public void deleteSchema(Schema schema) throws ServiceException {
try {
connection.invoke(new ObjectName("com.bagri.db:type=Management,name=SchemaManagement")
, "destroySchema"
, new Object[] {schema.getSchemaName()}
, new String[] {String.class.getName()});
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "deleteSchema", e);
throw new ServiceException(e);
}
}
@Override
public void cancelQuery(String schemaName) throws ServiceException {
try {
Object vars = connection.invoke(getSchemaObjectName("QueryManagement", schemaName)
, "cancelQuery"
, null
, null
);
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "cancelQuery", e);
throw new ServiceException(e);
}
}
@Override
public List<String> parseQuery(String schemaName, String query, Properties props) throws ServiceException {
try {
Object vars = connection.invoke(getSchemaObjectName("QueryManagement", schemaName)
, "parseQuery"
, new Object[] {query, props}
, new String[] {String.class.getName(), Properties.class.getName()}
);
return Arrays.asList((String[]) vars);
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "parseQuery", e);
throw new ServiceException(e);
}
}
@Override
public Object runQuery(String schemaName, boolean direct, String query, Map<String, Object> params, Properties props) throws ServiceException {
try {
CompositeData bindings = null;
if (params != null) {
bindings = mapToComposite("param", "desc", params);
}
Object res = connection.invoke(getSchemaObjectName("QueryManagement", schemaName)
, "runPreparedQuery"
, new Object[] {query, direct, bindings, props}
, new String[] {String.class.getName(), boolean.class.getName(), CompositeData.class.getName(), Properties.class.getName()}
);
return res;
} catch (Throwable e) {
LOGGER.throwing(this.getClass().getName(), "runQuery", e);
throw new ServiceException(e);
}
}
@Override
public Properties getQueryProperties(String schemaName) throws ServiceException {
Properties props = new Properties();
try {
ObjectName on = getSchemaObjectName("QueryManagement", schemaName);
Object value = connection.getAttribute(on, "FetchSize");
props.setProperty("bdb.client.fetchSize", value.toString());
value = connection.getAttribute(on, "QueryTimeout");
props.setProperty("xqj.schema.queryTimeout", value.toString());
return props;
} catch (Exception ex) {
LOGGER.throwing(this.getClass().getName(), "getQueryProperties", ex);
throw new ServiceException(ex);
}
}
@Override
public long[] getSchemaVolumeStatistics(String schemaName) throws ServiceException {
try {
Object res = connection.getAttribute(getSchemaObjectName("DocumentManagement", schemaName), "TotalCounts");
CompositeData cd = (CompositeData) res;
return new long[] {(Integer) cd.get("Number of documents"), (Integer) cd.get("Number of elements"), (Long) cd.get("Consumed size")};
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getSchemaVolumeStatistics", e);
throw new ServiceException(e);
}
}
@Override
public long[] getSchemaTransactionStatistics(String schemaName) throws ServiceException {
try {
Object res = connection.getAttribute(getSchemaObjectName("TransactionManagement", schemaName), "TxStatistics");
CompositeData cd = (CompositeData) res;
return new long[] {(Long) cd.get("Started"), (Long) cd.get("In Progress"), (Long) cd.get("Commited"), (Long) cd.get("Rolled Back")};
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getSchemaTransactionStatistics", e);
throw new ServiceException(e);
}
}
@Override
public TabularData getSchemaPartitionStatistics(String schemaName) throws ServiceException {
int flag = 0;
try {
TabularData result = (TabularData) connection.invoke(new ObjectName("com.bagri.db:type=Schema,name=" + schemaName),
"getPartitionStatistics", new Object[] {flag}, new String[] {int.class.getName()});
//LOGGER.info("getPartitionStatistics; got results: " + result.size());
return result;
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getSchemaPartitionStatistics", e);
throw new ServiceException(e);
}
}
@Override
public List<String> getWorkingHosts(String schemaName) throws ServiceException {
try {
Object res = connection.getAttribute(new ObjectName("com.bagri.db:type=Schema,name=" + schemaName), "ActiveNodes");
return Arrays.asList((String[]) res);
} catch (Exception e) {
LOGGER.throwing(this.getClass().getName(), "getWorkingHosts", e);
throw new ServiceException(e);
}
}
private ObjectName getSchemaObjectName(String kind, String schemaName) throws MalformedObjectNameException {
return new ObjectName("com.bagri.db:type=Schema,kind=" + kind + ",name=" + schemaName);
}
private Schema extractSchema(ObjectInstance oi) {
ObjectName on = oi.getObjectName();
String name = null;
String description = null;
String dataFormat = null;
String state = null;
boolean active = false;
boolean persistent = false;
int version = -1;
String[] registeredTypes = new String[0];
CompositeData propertiesCd = null;
try {
name = (String) connection.getAttribute(on, "Name");
} catch (Exception e) {/* Ignore it for now */}
try {
description = (String) connection.getAttribute(on, "Description");
} catch (Exception e) {/* Ignore it for now */}
try {
dataFormat = (String) connection.getAttribute(on, "DataFormat");
} catch (Exception e) {/* Ignore it for now */}
try {
state = (String) connection.getAttribute(on, "State");
} catch (Exception e) {/* Ignore it for now */}
try {
active = (Boolean) connection.getAttribute(on, "Active");
} catch (Exception e) {/* Ignore it for now */}
try {
persistent = (Boolean) connection.getAttribute(on, "Persistent");
} catch (Exception e) {/* Ignore it for now */}
try {
version = (Integer) connection.getAttribute(on, "Version");
} catch (Exception e) {/* Ignore it for now */}
try {
registeredTypes = (String[]) connection.getAttribute(on, "RegisteredTypes");
} catch (Exception e) {/* Ignore it for now */}
try {
propertiesCd = (CompositeData) connection.getAttribute(on, "Properties");
} catch (Exception e) {/* Ignore it for now */}
Schema schema = new Schema(name);
schema.setObjectName(on);
schema.setDescription(description);
schema.setDataFormat(dataFormat);
schema.setState(state);
schema.setActive(active);
schema.setPersistent(persistent);
schema.setVersion(version);
schema.setRegisteredTypes(registeredTypes);
schema.setProperties(convertCompositeToProperties(propertiesCd));
return schema;
}
private List<NodeOption> convertCompositeToNodeOptions(CompositeData cd) {
List<NodeOption> options = new ArrayList<NodeOption>();
if (null == cd) {
return options;
}
Set<String> keys = cd.getCompositeType().keySet();
for (String key : keys) {
String value = (String) cd.get(key);
NodeOption option = new NodeOption(key, value);
options.add(option);
}
return options;
}
private String convertOptionsToString(List<NodeOption> options) {
String result = "";
for (NodeOption o : options) {
result += o.getOptionName() + "=" + o.getOptionValue() + ";";
}
return result;
}
private String convertPropertiesToString(Properties properties) {
String result = "";
for (Object key : properties.keySet()) {
result += key + "=" + properties.get(key) + ";";
}
return result;
}
private Properties convertCompositeToProperties(CompositeData cd) {
Properties properties = new Properties();
if (null == cd) {
return properties;
}
Set<String> keys = cd.getCompositeType().keySet();
for (String key : keys) {
String value = (String) cd.get(key);
properties.put(key, value);
}
return properties;
}
private CompositeData mapToComposite(String name, String desc, Map<String, Object> map) {
if (map != null && !map.isEmpty()) {
try {
String[] names = new String[map.size()];
OpenType[] types = new OpenType[map.size()];
int idx = 0;
for (Map.Entry<String, Object> e : map.entrySet()) {
names[idx] = e.getKey();
types[idx] = getOpenType(e.getValue());
idx++;
}
CompositeType type = new CompositeType(name, desc, names, names, types);
return new CompositeDataSupport(type, map);
} catch (OpenDataException ex) {
//logger.warn("statsToComposite. error: {}", ex.getMessage());
}
}
return null;
}
private static OpenType getOpenType(Object value) {
if (value == null) {
return SimpleType.VOID;
}
String name = value.getClass().getName();
//if (OpenType.ALLOWED_CLASSNAMES_LIST.contains(name)) {
if ("java.lang.Long".equals(name)) {
return SimpleType.LONG;
}
if ("java.lang.Integer".equals(name)) {
return SimpleType.INTEGER;
}
if ("java.lang.String".equals(name)) {
return SimpleType.STRING;
}
if ("java.lang.Double".equals(name)) {
return SimpleType.DOUBLE;
}
if ("java.lang.Float".equals(name)) {
return SimpleType.FLOAT;
}
if ("java.math.BigDecimal".equals(name)) {
return SimpleType.BIGDECIMAL;
}
if ("java.math.BigInteger".equals(name)) {
return SimpleType.BIGINTEGER;
}
if ("java.lang.Boolean".equals(name)) {
return SimpleType.BOOLEAN;
}
if ("java.lang.Byte".equals(name)) {
return SimpleType.BYTE;
}
if ("java.lang.Character".equals(name)) {
return SimpleType.CHARACTER;
}
if ("java.util.Date".equals(name)) {
return SimpleType.DATE;
}
if ("java.lang.Short".equals(name)) {
return SimpleType.SHORT;
}
//"javax.management.ObjectName",
//CompositeData.class.getName(),
//TabularData.class.getName()
//}
return null; // is it allowed ??
}
}