001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.console.command; 018 019import java.io.BufferedReader; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FileNotFoundException; 023import java.io.FileOutputStream; 024import java.io.IOException; 025import java.io.InputStreamReader; 026import java.nio.ByteBuffer; 027import java.nio.channels.FileChannel; 028import java.util.List; 029 030import javax.xml.parsers.DocumentBuilder; 031import javax.xml.parsers.DocumentBuilderFactory; 032import javax.xml.parsers.ParserConfigurationException; 033import javax.xml.transform.Result; 034import javax.xml.transform.Source; 035import javax.xml.transform.Transformer; 036import javax.xml.transform.TransformerException; 037import javax.xml.transform.TransformerFactory; 038import javax.xml.transform.dom.DOMSource; 039import javax.xml.transform.stream.StreamResult; 040import javax.xml.xpath.XPath; 041import javax.xml.xpath.XPathConstants; 042import javax.xml.xpath.XPathExpressionException; 043import javax.xml.xpath.XPathFactory; 044 045import org.w3c.dom.Attr; 046import org.w3c.dom.Element; 047import org.xml.sax.SAXException; 048 049public class CreateCommand extends AbstractCommand { 050 051 protected final String[] helpFile = new String[] { 052 "Task Usage: Main create path/to/brokerA [create-options]", 053 "Description: Creates a runnable broker instance in the specified path.", 054 "", 055 "List Options:", 056 " --amqconf <file path> Path to ActiveMQ conf file that will be used in the broker instance. Default is: conf/activemq.xml", 057 " --version Display the version information.", 058 " -h,-?,--help Display the create broker help information.", 059 "" 060 }; 061 062 protected final String DEFAULT_TARGET_ACTIVEMQ_CONF = "conf/activemq.xml"; // default activemq conf to create in the new broker instance 063 protected final String DEFAULT_BROKERNAME_XPATH = "/beans/broker/@brokerName"; // default broker name xpath to change the broker name 064 065 protected final String[] BASE_SUB_DIRS = { "bin", "conf" }; // default sub directories that will be created 066 protected final String BROKER_NAME_REGEX = "[$][{]brokerName[}]"; // use to replace broker name property holders 067 068 protected String amqConf = "conf/activemq.xml"; // default conf if no conf is specified via --amqconf 069 070 // default files to copy from activemq home to the new broker instance 071 protected String[][] fileCopyMap = { 072 { "conf/log4j.properties", "conf/log4j.properties" }, 073 { "conf/broker.ks", "conf/broker.ks" }, 074 { "conf/broker.ts", "conf/broker.ts" }, 075 { "conf/camel.xml", "conf/camel.xml" }, 076 { "conf/jetty.xml", "conf/jetty.xml" }, 077 { "conf/jetty-realm.properties", "conf/jetty-realm.properties" }, 078 { "conf/credentials.properties", "conf/credentials.properties" } 079 }; 080 081 // default files to create 082 protected String[][] fileWriteMap = { 083 { "winActivemq", "bin/${brokerName}.bat" }, 084 { "unixActivemq", "bin/${brokerName}" } 085 }; 086 087 088 protected String brokerName; 089 protected File amqHome; 090 protected File targetAmqBase; 091 092 protected void runTask(List<String> tokens) throws Exception { 093 context.print("Running create broker task..."); 094 amqHome = new File(System.getProperty("activemq.home")); 095 for (String token : tokens) { 096 097 targetAmqBase = new File(token); 098 brokerName = targetAmqBase.getName(); 099 100 101 if (targetAmqBase.exists()) { 102 BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); 103 String resp; 104 while (true) { 105 context.print("Target directory (" + targetAmqBase.getCanonicalPath() + ") already exists. Overwrite (y/n): "); 106 resp = console.readLine(); 107 if (resp.equalsIgnoreCase("y") || resp.equalsIgnoreCase("yes")) { 108 break; 109 } else if (resp.equalsIgnoreCase("n") || resp.equalsIgnoreCase("no")) { 110 return; 111 } 112 } 113 } 114 115 context.print("Creating directory: " + targetAmqBase.getCanonicalPath()); 116 targetAmqBase.mkdirs(); 117 createSubDirs(targetAmqBase, BASE_SUB_DIRS); 118 writeFileMapping(targetAmqBase, fileWriteMap); 119 copyActivemqConf(amqHome, targetAmqBase, amqConf); 120 copyFileMapping(amqHome, targetAmqBase, fileCopyMap); 121 } 122 } 123 124 /** 125 * Handle the --amqconf options. 126 * 127 * @param token - option token to handle 128 * @param tokens - succeeding command arguments 129 * @throws Exception 130 */ 131 protected void handleOption(String token, List<String> tokens) throws Exception { 132 if (token.startsWith("--amqconf")) { 133 // If no amqconf specified, or next token is a new option 134 if (tokens.isEmpty() || tokens.get(0).startsWith("-")) { 135 context.printException(new IllegalArgumentException("Attributes to amqconf not specified")); 136 return; 137 } 138 139 amqConf = tokens.remove(0); 140 } else { 141 // Let super class handle unknown option 142 super.handleOption(token, tokens); 143 } 144 } 145 146 protected void createSubDirs(File target, String[] subDirs) throws IOException { 147 File subDirFile; 148 for (String subDir : BASE_SUB_DIRS) { 149 subDirFile = new File(target, subDir); 150 context.print("Creating directory: " + subDirFile.getCanonicalPath()); 151 subDirFile.mkdirs(); 152 } 153 } 154 155 protected void writeFileMapping(File targetBase, String[][] fileWriteMapping) throws IOException { 156 for (String[] fileWrite : fileWriteMapping) { 157 File dest = new File(targetBase, resolveParam(BROKER_NAME_REGEX, brokerName, fileWrite[1])); 158 context.print("Creating new file: " + dest.getCanonicalPath()); 159 writeFile(fileWrite[0], dest); 160 } 161 } 162 163 protected void copyFileMapping(File srcBase, File targetBase, String[][] fileMapping) throws IOException { 164 for (String[] fileMap : fileMapping) { 165 File src = new File(srcBase, fileMap[0]); 166 File dest = new File(targetBase, resolveParam(BROKER_NAME_REGEX, brokerName, fileMap[1])); 167 context.print("Copying from: " + src.getCanonicalPath() + "\n to: " + dest.getCanonicalPath()); 168 copyFile(src, dest); 169 } 170 } 171 172 protected void copyActivemqConf(File srcBase, File targetBase, String activemqConf) throws IOException, ParserConfigurationException, SAXException, TransformerException, XPathExpressionException { 173 File src = new File(srcBase, activemqConf); 174 175 if (!src.exists()) { 176 throw new FileNotFoundException("File: " + src.getCanonicalPath() + " not found."); 177 } 178 179 File dest = new File(targetBase, DEFAULT_TARGET_ACTIVEMQ_CONF); 180 context.print("Copying from: " + src.getCanonicalPath() + "\n to: " + dest.getCanonicalPath()); 181 182 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 183 Element docElem = builder.parse(src).getDocumentElement(); 184 185 XPath xpath = XPathFactory.newInstance().newXPath(); 186 Attr brokerNameAttr = (Attr) xpath.evaluate(DEFAULT_BROKERNAME_XPATH, docElem, XPathConstants.NODE); 187 brokerNameAttr.setValue(brokerName); 188 189 writeToFile(new DOMSource(docElem), dest); 190 } 191 192 protected void printHelp() { 193 context.printHelp(helpFile); 194 } 195 196 // write the default files to create (i.e. script files) 197 private void writeFile(String typeName, File dest) throws IOException { 198 String data; 199 if (typeName.equals("winActivemq")) { 200 data = winActivemqData; 201 data = resolveParam("[$][{]activemq.home[}]", amqHome.getCanonicalPath().replaceAll("[\\\\]", "/"), data); 202 data = resolveParam("[$][{]activemq.base[}]", targetAmqBase.getCanonicalPath().replaceAll("[\\\\]", "/"), data); 203 } else if (typeName.equals("unixActivemq")) { 204 data = unixActivemqData; 205 data = resolveParam("[$][{]activemq.home[}]", amqHome.getCanonicalPath().replaceAll("[\\\\]", "/"), data); 206 data = resolveParam("[$][{]activemq.base[}]", targetAmqBase.getCanonicalPath().replaceAll("[\\\\]", "/"), data); 207 } else { 208 throw new IllegalStateException("Unknown file type: " + typeName); 209 } 210 211 ByteBuffer buf = ByteBuffer.allocate(data.length()); 212 buf.put(data.getBytes()); 213 buf.flip(); 214 215 FileChannel destinationChannel = new FileOutputStream(dest).getChannel(); 216 destinationChannel.write(buf); 217 destinationChannel.close(); 218 219 // Set file permissions available for Java 6.0 only 220// dest.setExecutable(true); 221// dest.setReadable(true); 222// dest.setWritable(true); 223 } 224 225 // utlity method to write an xml source to file 226 private void writeToFile(Source src, File file) throws TransformerException { 227 TransformerFactory tFactory = TransformerFactory.newInstance(); 228 Transformer fileTransformer = tFactory.newTransformer(); 229 230 Result res = new StreamResult(file); 231 fileTransformer.transform(src, res); 232 } 233 234 // utility method to copy one file to another 235 private void copyFile(File from, File dest) throws IOException { 236 if (!from.exists()) { 237 return; 238 } 239 FileChannel sourceChannel = new FileInputStream(from).getChannel(); 240 FileChannel destinationChannel = new FileOutputStream(dest).getChannel(); 241 sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel); 242 sourceChannel.close(); 243 destinationChannel.close(); 244 } 245 246 // replace a property place holder (paramName) with the paramValue 247 private String resolveParam(String paramName, String paramValue, String target) { 248 return target.replaceAll(paramName, paramValue); 249 } 250 251 // Embedded windows script data 252 private static final String winActivemqData = 253 "@echo off\n" 254 + "set ACTIVEMQ_HOME=\"${activemq.home}\"\n" 255 + "set ACTIVEMQ_BASE=\"${activemq.base}\"\n" 256 + "\n" 257 + "set PARAM=%1\n" 258 + ":getParam\n" 259 + "shift\n" 260 + "if \"%1\"==\"\" goto end\n" 261 + "set PARAM=%PARAM% %1\n" 262 + "goto getParam\n" 263 + ":end\n" 264 + "\n" 265 + "%ACTIVEMQ_HOME%/bin/activemq %PARAM%"; 266 267 268 // Embedded unix script data 269 private static final String unixActivemqData = "## Figure out the ACTIVEMQ_BASE from the directory this script was run from\n" 270 + "PRG=\"$0\"\n" 271 + "progname=`basename \"$0\"`\n" 272 + "saveddir=`pwd`\n" 273 + "# need this for relative symlinks\n" 274 + "dirname_prg=`dirname \"$PRG\"`\n" 275 + "cd \"$dirname_prg\"\n" 276 + "while [ -h \"$PRG\" ] ; do\n" 277 + " ls=`ls -ld \"$PRG\"`\n" 278 + " link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n" 279 + " if expr \"$link\" : '.*/.*' > /dev/null; then\n" 280 + " PRG=\"$link\"\n" 281 + " else\n" 282 + " PRG=`dirname \"$PRG\"`\"/$link\"\n" 283 + " fi\n" 284 + "done\n" 285 + "ACTIVEMQ_BASE=`dirname \"$PRG\"`/..\n" 286 + "cd \"$saveddir\"\n" 287 + "\n" 288 + "ACTIVEMQ_BASE=`cd \"$ACTIVEMQ_BASE\" && pwd`\n\n" 289 + "export ACTIVEMQ_HOME=${activemq.home}\n" 290 + "export ACTIVEMQ_BASE=$ACTIVEMQ_BASE\n\n" 291 + "${ACTIVEMQ_HOME}/bin/activemq \"$*\""; 292 293}