Java tutorial
/** * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 . * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ package org.jboss.loom.utils.as7; import org.jboss.loom.ex.CliBatchException; import org.jboss.loom.conf.AS7Config; import org.jboss.loom.ex.MigrationException; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.controller.client.OperationBuilder; import org.jboss.as.controller.client.helpers.ClientConstants; import org.jboss.dmr.ModelNode; import java.io.Closeable; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.Set; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.jboss.dmr.ModelType; import org.jboss.loom.spi.ann.Property; /** * * @author Ondrej Zizka, ozizka at redhat.com */ public class AS7CliUtils { private final static String OP_KEY_PREFIX = "Operation step-"; public static void removeResourceIfExists(ModelNode loggerCmd, ModelControllerClient aS7Client) throws IOException, CliBatchException { // Check if exists. if (!exists(loggerCmd, aS7Client)) return; // Remove. ModelNode res = aS7Client.execute(createRemoveCommandForResource(loggerCmd)); throwIfFailure(res); } /** * Queries the AS 7 if given resource exists. */ public static boolean exists(final ModelNode resource, ModelControllerClient client) throws IOException { ModelNode query = new ModelNode(); // Read operation. query.get(ClientConstants.OP).set(ClientConstants.READ_RESOURCE_OPERATION); // Copy the address. query.get(ClientConstants.OP_ADDR).set(resource.get(ClientConstants.OP_ADDR)); //try{ ModelNode res = client.execute(query); return wasSuccess(res); //} //// This handling should rather be done in whatever provides the client. //catch( IOException ex ){ throw ex; } //finally { client.close(); } } /** Parses the command string into ModalNode and calls the sister method. */ public static boolean exists(String command, ModelControllerClient client) throws IOException { return exists(parseCommand(command, false), client); } public static ModelNode createRemoveCommandForResource(ModelNode resource) { // Copy the address. ModelNode query = new ModelNode(); query.get(ClientConstants.OP_ADDR).set(resource.get(ClientConstants.OP_ADDR)); // Remove operation. query.get(ClientConstants.OP).set(ClientConstants.REMOVE_OPERATION); return query; } /** * Executes CLI request. */ public static void executeRequest(ModelNode request, AS7Config as7config) throws IOException, CliBatchException { ModelControllerClient client = null; try { client = ModelControllerClient.Factory.create(as7config.getHost(), as7config.getManagementPort()); final ModelNode response = client.execute(new OperationBuilder(request).build()); throwIfFailure(response); } catch (IOException ex) { // Specific problem on Roman's PC. Need to connect two times. //final ModelNode response = client.execute(new OperationBuilder(request).build()); //throwIfFailure( response ); throw ex; } finally { safeClose(client); } } /** * Executes CLI request. */ public static ModelNode executeRequest(String cmd, ModelControllerClient mcc) throws IOException { ModelNode node = parseCommand(cmd, true); return mcc.execute(node); } /** * Safely closes closeable resource (a CLI connection in our case). */ public static void safeClose(final Closeable closeable) { if (closeable != null) try { closeable.close(); } catch (IOException e) { //throw new MigrationException("Closing failed: " + e.getMessage(), e); } } /** * If the result is an error, throw an exception. */ private static void throwIfFailure(final ModelNode node) throws CliBatchException { if (wasSuccess(node)) return; final String msg; if (node.hasDefined(ClientConstants.FAILURE_DESCRIPTION)) { if (node.hasDefined(ClientConstants.OP)) { msg = String.format("Operation '%s' at address '%s' failed: %s", node.get(ClientConstants.OP), node.get(ClientConstants.OP_ADDR), node.get(ClientConstants.FAILURE_DESCRIPTION)); } else { msg = String.format("Operation failed: %s", node.get(ClientConstants.FAILURE_DESCRIPTION)); } } else { msg = String.format("Operation failed: %s", node); } throw new CliBatchException(msg, node); } public static boolean wasSuccess(ModelNode node) { return ClientConstants.SUCCESS.equals(node.get(ClientConstants.OUTCOME).asString()); } /** * Parses the index of operation which failed. * * "failure-description" => * {"JBAS014653: Composite operation failed and was rolled back. Steps that failed:" => { * "Operation step-12" => "JBAS014803: Duplicate resource [ (\"subsystem\" => \"security\"), (\"security-domain\" => \"other\") ]" }} * * @deprecated Use extractFailedOperationNode(). */ public static Integer parseFailedOperationIndex(final ModelNode node) throws MigrationException { if (ClientConstants.SUCCESS.equals(node.get(ClientConstants.OUTCOME).asString())) return 0; if (!node.hasDefined(ClientConstants.FAILURE_DESCRIPTION)) return null; ModelNode failDesc = node.get(ClientConstants.FAILURE_DESCRIPTION); String key = failDesc.keys().iterator().next(); // "JBAS014653: Composite operation failed and was rolled back. Steps that failed:" => ... ModelNode compositeFailDesc = failDesc.get(key); // { "Operation step-1" => "JBAS014803: Duplicate resource ... Set<String> keys = compositeFailDesc.keys(); String opKey = keys.iterator().next(); // "Operation step-XX" if (!opKey.startsWith(OP_KEY_PREFIX)) return null; String opIndex = StringUtils.substring(opKey, OP_KEY_PREFIX.length()); return Integer.parseInt(opIndex); } /** * @returns A ModelNode with two properties: "failedOpIndex" and "failureDesc". */ public static BatchFailure extractFailedOperationNode(final ModelNode node) throws MigrationException { if (ClientConstants.SUCCESS.equals(node.get(ClientConstants.OUTCOME).asString())) return null; if (!node.hasDefined(ClientConstants.FAILURE_DESCRIPTION)) return null; ModelNode failDesc = node.get(ClientConstants.FAILURE_DESCRIPTION); if (failDesc.getType() != ModelType.OBJECT) return null; String key = failDesc.keys().iterator().next(); // "JBAS014653: Composite operation failed and was rolled back. Steps that failed:" => ... ModelNode compositeFailDesc = failDesc.get(key); // { "Operation step-1" => "JBAS014803: Duplicate resource ... Set<String> keys = compositeFailDesc.keys(); String opKey = keys.iterator().next(); // "Operation step-XX" if (!opKey.startsWith(OP_KEY_PREFIX)) return null; String opIndex = StringUtils.substring(opKey, OP_KEY_PREFIX.length()); return new BatchFailure(Integer.parseInt(opIndex), compositeFailDesc.get(opKey).toString()); } /** * Copies properties using reflection. * @param handler From this object. * @param builder Append to this CLI builder. * @param A list of properties, as a space separated string. */ public static void copyProperties(Object source, CliApiCommandBuilder builder, String props) { String[] parts = StringUtils.split(props); for (String prop : parts) { try { Method method = source.getClass().getMethod(Property.Utils.convertPropToMethodName(prop)); if (String.class != method.getReturnType()) continue; String val = (String) method.invoke(source); builder.addPropertyIfSet(prop, val); } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | IllegalArgumentException ex) { throw new RuntimeException(ex); } } } /** * Joins the given list into a string of quoted values joined with ", ". * @param col * @return */ public static String joinQuoted(Collection<String> col) { if (col.isEmpty()) return ""; StringBuilder sb = new StringBuilder(); for (String item : col) sb.append(",\"").append(item).append('"'); String str = sb.toString(); str = str.replaceFirst(",", ""); return str; } /** * Parse CLI command into a ModelNode - /foo=a/bar=b/:operation(param=value,...) . * * TODO: Support nested params. */ public static ModelNode parseCommand(String command) { return parseCommand(command, true); } public static ModelNode parseCommand(String command, boolean needOp) { String[] parts = StringUtils.split(command, ':'); if (needOp && parts.length < 2) throw new IllegalArgumentException("Missing CLI command operation: " + command); String addr = parts[0]; ModelNode query = new ModelNode(); // Addr String[] partsAddr = StringUtils.split(addr, '/'); for (String segment : partsAddr) { String[] partsSegment = StringUtils.split(segment, "=", 2); if (partsSegment.length != 2) throw new IllegalArgumentException("Wrong addr segment format - need '=': " + command); query.get(ClientConstants.OP_ADDR).add(partsSegment[0], partsSegment[1]); } // No op? if (parts.length < 2) return query; // Op String[] partsOp = StringUtils.split(parts[1], '('); String opName = partsOp[0]; query.get(ClientConstants.OP).set(opName); // Op args if (partsOp.length > 1) { String args = StringUtils.removeEnd(partsOp[1], ")"); for (String arg : args.split(",")) { String[] partsArg = arg.split("=", 2); query.get(partsArg[0]).set(unquote(partsArg[1])); } } return query; }// parseCommand() /** * Changes "foo\"bar" to foo"bar. * Is tolerant - doesn't check if the quotes are really present. */ public static String unquote(String string) { string = StringUtils.removeStart(string, "\""); string = StringUtils.removeEnd(string, "\""); return StringEscapeUtils.unescapeJava(string); } /** * Formats Model node to the form of CLI script command - /foo=a/bar=b/:operation(param=value,...) . */ public static String formatCommand(ModelNode command) { if (!command.has(ClientConstants.OP)) throw new IllegalArgumentException("'" + ClientConstants.OP + "' not defined."); if (command.get(ClientConstants.OP).getType() != ModelType.STRING) throw new IllegalArgumentException("'" + ClientConstants.OP + "' must be a string."); if (!command.has(ClientConstants.OP_ADDR)) throw new IllegalArgumentException("'" + ClientConstants.OP_ADDR + "' not defined."); if (command.get(ClientConstants.OP_ADDR).getType() != ModelType.LIST) throw new IllegalArgumentException("'" + ClientConstants.OP_ADDR + "' must be a list."); // Operation. String op = command.get(ClientConstants.OP).asString(); // Address ModelNode addr = command.get(ClientConstants.OP_ADDR); StringBuilder sb = new StringBuilder(); for (int i = 0;; i++) { if (!addr.has(i)) break; ModelNode segment = addr.get(i); String key = segment.keys().iterator().next(); sb.append('/').append(key).append('=').append(segment.get(key).asString()); } sb.append(':').append(op); // Params. boolean hasParams = false; Set<String> keys = command.keys(); for (String key : keys) { switch (key) { case ClientConstants.OP: case ClientConstants.OP_ADDR: continue; } sb.append(hasParams ? ',' : '('); hasParams = true; sb.append(key).append('=').append(command.get(key)); } if (hasParams) sb.append(')'); return sb.toString(); } /** /path=jboss.server.base.dir/:read-attribute(name=path,include-defaults=true) { "outcome" => "success", "result" => "/home/ondra/work/AS/Migration/AS-7.1.3/standalone" } */ public static String queryServerPath(String path, ModelControllerClient client) throws MigrationException { ModelNode query = new ModelNode(); query.get(ClientConstants.OP).set(ClientConstants.READ_ATTRIBUTE_OPERATION); query.get(ClientConstants.OP_ADDR).add("path", path); query.get("name").set("path"); ModelNode response; try { response = client.execute(query); throwIfFailure(response); } catch (IOException | CliBatchException ex) { throw new MigrationException("Failed querying for AS 7 directory.", ex); } ModelNode result = response.get(ClientConstants.RESULT); if (result.getType() == ModelType.UNDEFINED) return null; return result.asString(); } /** * Actually it returns the base dir, i.e. the dir containing bin/, standalone/ etc. */ public static String queryServerHomeDir(ModelControllerClient client) throws MigrationException { return queryServerPath("jboss.home.dir", client); } /** * E.g. $AS/standalone (full path). */ public static String queryServerBaseDir(ModelControllerClient client) throws MigrationException { return queryServerPath("jboss.server.base.dir", client); } public static String queryServerConfigDir(ModelControllerClient client) throws MigrationException { return queryServerPath("jboss.server.config.dir", client); } /** * Escape CLI address element - the parts between / and = in /foo=bar/baz=moo . */ public static String escapeAddressElement(String element) { element = element.replace(":", "\\:"); element = element.replace("/", "\\/"); element = element.replace("=", "\\="); element = element.replace(" ", "\\ "); return element; } /** * Converts "some-property-name" to "getSomePropertyName()". * * @deprecated Use @Property.Utils.convertPropToMethodName(). */ public static String formatGetterName(String prop) { return Property.Utils.convertPropToMethodName(prop); } /** * Returns the name of JDBC driver which uses given module. * /subsystem=datasources/:read-attribute(name=installed-drivers) { "outcome" => "success", "result" => [{ "driver-name" => "h2", "deployment-name" => undefined, "driver-module-name" => "com.h2database.h2", "module-slot" => "main", "driver-datasource-class-name" => "", "driver-xa-datasource-class-name" => "org.h2.jdbcx.JdbcDataSource", "driver-class-name" => "org.h2.Driver", "driver-major-version" => 1, "driver-minor-version" => 3, "jdbc-compliant" => true }] } */ public static String findJdbcDriverUsingModule(String driverModuleName, ModelControllerClient as7Client) throws IOException { ModelNode query = parseCommand("/subsystem=datasources/:read-attribute(name=installed-drivers)"); ModelNode driversNode = as7Client.execute(query); driversNode = driversNode.get("result"); for (ModelNode modelNode : driversNode.asList()) { if (modelNode.get("driver-module-name").asString().equals(driverModuleName)) return modelNode.get("driver-name").asString(); } return null; } }// class