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 */
017 package org.apache.activemq.console.command;
018
019 import java.io.BufferedReader;
020 import java.io.File;
021 import java.io.FileInputStream;
022 import java.io.FileNotFoundException;
023 import java.io.FileOutputStream;
024 import java.io.IOException;
025 import java.io.InputStreamReader;
026 import java.nio.ByteBuffer;
027 import java.nio.channels.FileChannel;
028 import java.util.List;
029
030 import javax.xml.parsers.DocumentBuilder;
031 import javax.xml.parsers.DocumentBuilderFactory;
032 import javax.xml.parsers.ParserConfigurationException;
033 import javax.xml.transform.Result;
034 import javax.xml.transform.Source;
035 import javax.xml.transform.Transformer;
036 import javax.xml.transform.TransformerException;
037 import javax.xml.transform.TransformerFactory;
038 import javax.xml.transform.dom.DOMSource;
039 import javax.xml.transform.stream.StreamResult;
040 import javax.xml.xpath.XPath;
041 import javax.xml.xpath.XPathConstants;
042 import javax.xml.xpath.XPathExpressionException;
043 import javax.xml.xpath.XPathFactory;
044
045 import org.w3c.dom.Attr;
046 import org.w3c.dom.Element;
047 import org.xml.sax.SAXException;
048
049 public 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 }