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.cocoon;
018:
019: import java.io.File;
020: import java.util.Arrays;
021:
022: import javax.xml.parsers.DocumentBuilder;
023: import javax.xml.parsers.DocumentBuilderFactory;
024:
025: import org.apache.cocoon.bean.CocoonBean;
026: import org.apache.cocoon.bean.helpers.OutputStreamListener;
027: import org.apache.cocoon.bean.helpers.BeanConfigurator;
028:
029: import org.apache.commons.cli.CommandLine;
030: import org.apache.commons.cli.HelpFormatter;
031: import org.apache.commons.cli.Option;
032: import org.apache.commons.cli.Options;
033: import org.apache.commons.cli.PosixParser;
034: import org.apache.commons.lang.BooleanUtils;
035:
036: import org.w3c.dom.Document;
037:
038: /**
039: * Command line entry point. Parses command line, create Cocoon bean and invokes it
040: * with file destination.
041: *
042: * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
043: * @author <a href="mailto:nicolaken@apache.org">Nicola Ken Barozzi</a>
044: * @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
045: * @author <a href="mailto:uv@upaya.co.uk">Upayavira</a>
046: * @version CVS $Id: Main.java 433543 2006-08-22 06:22:54Z crossley $
047: */
048: public class Main {
049:
050: protected static final String HELP_OPT = "h";
051: protected static final String VERSION_OPT = "v";
052: protected static final String VERBOSE_OPT = "V";
053: protected static final String LOG_KIT_OPT = "k";
054: protected static final String LOGGER_OPT = "l";
055: protected static final String LOG_LEVEL_OPT = "u";
056: protected static final String CONTEXT_DIR_OPT = "c";
057: protected static final String DEST_DIR_OPT = "d";
058: protected static final String WORK_DIR_OPT = "w";
059: protected static final String CONFIG_FILE_OPT = "C";
060: protected static final String BROKEN_LINK_FILE_OPT = "b";
061: protected static final String URI_FILE_OPT = "f";
062: protected static final String XCONF_OPT = "x";
063: protected static final String AGENT_OPT = "a";
064: protected static final String ACCEPT_OPT = "p";
065: protected static final String FOLLOW_LINKS_OPT = "r";
066: protected static final String PRECOMPILE_ONLY_OPT = "P";
067: protected static final String CONFIRM_EXTENSIONS_OPT = "e";
068: protected static final String LOAD_CLASS_OPT = "L";
069: protected static final String DEFAULT_FILENAME_OPT = "D";
070: protected static final String URI_GROUP_NAME_OPT = "n";
071:
072: protected static final String HELP_LONG = "help";
073: protected static final String VERSION_LONG = "version";
074: protected static final String VERBOSE_LONG = "verbose";
075: protected static final String LOG_KIT_LONG = "logKitconfig";
076: protected static final String LOGGER_LONG = "Logger";
077: protected static final String LOG_LEVEL_LONG = "logLevel";
078: protected static final String CONTEXT_DIR_LONG = "contextDir";
079: protected static final String DEST_DIR_LONG = "destDir";
080: protected static final String WORK_DIR_LONG = "workDir";
081: protected static final String CONFIG_FILE_LONG = "configFile";
082: protected static final String BROKEN_LINK_FILE_LONG = "brokenLinkFile";
083: protected static final String URI_FILE_LONG = "uriFile";
084: protected static final String XCONF_LONG = "xconf";
085: protected static final String AGENT_LONG = "userAgent";
086: protected static final String ACCEPT_LONG = "accept";
087: protected static final String FOLLOW_LINKS_LONG = "followLinks";
088: protected static final String PRECOMPILE_ONLY_LONG = "precompileOnly";
089: protected static final String CONFIRM_EXTENSIONS_LONG = "confirmExtensions";
090: protected static final String LOAD_CLASS_LONG = "loadClass";
091: protected static final String DEFAULT_FILENAME_LONG = "defaultFilename";
092: protected static final String URI_LONG = "uri";
093: protected static final String URI_GROUP_NAME_LONG = "uris";
094:
095: private static Options options;
096: private static OutputStreamListener listener;
097:
098: private static void setOptions() {
099: options = new Options();
100:
101: options.addOption(new Option(HELP_OPT, HELP_LONG, false,
102: "print this message and exit"));
103:
104: options.addOption(new Option(VERSION_OPT, VERSION_LONG, false,
105: "print the version information and exit"));
106:
107: options.addOption(new Option(VERBOSE_OPT, VERBOSE_LONG, false,
108: "enable verbose messages to System.out"));
109:
110: options.addOption(new Option(LOG_KIT_OPT, LOG_KIT_LONG, true,
111: "use given file for LogKit Management configuration"));
112:
113: options
114: .addOption(new Option(LOGGER_OPT, LOGGER_LONG, true,
115: "use given logger category as default logger for the Cocoon engine"));
116:
117: options
118: .addOption(new Option(
119: LOG_LEVEL_OPT,
120: LOG_LEVEL_LONG,
121: true,
122: "choose the minimum log level for logging (DEBUG, INFO, WARN, ERROR, FATAL_ERROR) for startup logging"));
123:
124: options.addOption(new Option(CONTEXT_DIR_OPT, CONTEXT_DIR_LONG,
125: true, "use given dir as context"));
126:
127: options.addOption(new Option(DEST_DIR_OPT, DEST_DIR_LONG, true,
128: "use given dir as destination"));
129:
130: options.addOption(new Option(WORK_DIR_OPT, WORK_DIR_LONG, true,
131: "use given dir as working directory"));
132:
133: options
134: .addOption(new Option(
135: CONFIG_FILE_OPT,
136: CONFIG_FILE_LONG,
137: true,
138: "specify alternate location of the configuration"
139: + " file (default is ${contextDir}/cocoon.xconf)"));
140:
141: options
142: .addOption(new Option(BROKEN_LINK_FILE_OPT,
143: BROKEN_LINK_FILE_LONG, true,
144: "send a list of broken links to a file (one URI per line)"));
145:
146: options
147: .addOption(new Option(URI_FILE_OPT, URI_FILE_LONG,
148: true,
149: "use a text file with uris to process (one URI per line)"));
150:
151: options.addOption(new Option(XCONF_OPT, XCONF_LONG, true,
152: "specify a file containing XML configuration details"
153: + " for the command line interface"));
154:
155: options.addOption(new Option(AGENT_OPT, AGENT_LONG, true,
156: "use given string for user-agent header"));
157:
158: options.addOption(new Option(ACCEPT_OPT, ACCEPT_LONG, true,
159: "use given string for accept header"));
160:
161: options
162: .addOption(new Option(
163: FOLLOW_LINKS_OPT,
164: FOLLOW_LINKS_LONG,
165: true,
166: "process pages linked from starting page or not"
167: + " (boolean argument is expected, default is true)"));
168:
169: options.addOption(new Option(PRECOMPILE_ONLY_OPT,
170: PRECOMPILE_ONLY_LONG, true,
171: "generate java code for xsp and xmap files"));
172:
173: options
174: .addOption(new Option(
175: CONFIRM_EXTENSIONS_OPT,
176: CONFIRM_EXTENSIONS_LONG,
177: true,
178: "confirm that file extensions match mime-type of"
179: + " pages and amend filename accordingly (default"
180: + " is true)"));
181:
182: options
183: .addOption(new Option(
184: LOAD_CLASS_OPT,
185: LOAD_CLASS_LONG,
186: true,
187: "specify a class to be loaded at startup (specifically"
188: + " for use with JDBC). Can be used multiple times"));
189:
190: options.addOption(new Option(DEFAULT_FILENAME_OPT,
191: DEFAULT_FILENAME_LONG, true,
192: "specify a filename to be appended to a URI when the"
193: + " URI refers to a directory"));
194: options.addOption(new Option(URI_GROUP_NAME_OPT,
195: URI_GROUP_NAME_LONG, true,
196: "specify which <uris> element to process in the configuration"
197: + " file specified with the -x parameter"));
198: }
199:
200: /**
201: * The <code>main</code> method.
202: *
203: * @param args a <code>String[]</code> of arguments
204: * @exception Exception if an error occurs
205: */
206: public static void main(String[] args) throws Exception {
207:
208: Main.setOptions();
209: CommandLine line = new PosixParser().parse(options, args);
210: listener = new OutputStreamListener(System.out);
211: CocoonBean cocoon = new CocoonBean();
212: cocoon.addListener(listener);
213:
214: if (line.hasOption(HELP_OPT)) {
215: printUsage();
216: } else if (line.hasOption(VERSION_OPT)) {
217: printVersion();
218: } else {
219: String uriGroup = null;
220: if (line.hasOption(URI_GROUP_NAME_OPT)) {
221: uriGroup = line.getOptionValue(URI_GROUP_NAME_OPT);
222: }
223:
224: String destDir = null;
225: if (line.hasOption(XCONF_OPT)) {
226: // destDir from command line overrides one in xconf file
227: destDir = Main.processXConf(cocoon, line
228: .getOptionValue(XCONF_OPT), destDir, uriGroup);
229: }
230: if (line.hasOption(DEST_DIR_OPT)) {
231: destDir = line.getOptionValue(DEST_DIR_OPT);
232: }
233:
234: if (line.hasOption(VERBOSE_OPT)) {
235: cocoon.setVerbose(true);
236: }
237: if (line.hasOption(PRECOMPILE_ONLY_OPT)) {
238: cocoon.setPrecompileOnly(true);
239: }
240:
241: if (line.hasOption(WORK_DIR_OPT)) {
242: String workDir = line.getOptionValue(WORK_DIR_OPT);
243: if (workDir.length() == 0) {
244: listener
245: .messageGenerated("Careful, you must specify a work dir when using the -w/--workDir argument");
246: System.exit(1);
247: } else {
248: cocoon
249: .setWorkDir(line
250: .getOptionValue(WORK_DIR_OPT));
251: }
252: }
253: if (line.hasOption(CONTEXT_DIR_OPT)) {
254: String contextDir = line
255: .getOptionValue(CONTEXT_DIR_OPT);
256: if (contextDir.length() == 0) {
257: listener
258: .messageGenerated("Careful, you must specify a configuration file when using the -c/--contextDir argument");
259: System.exit(1);
260: } else {
261: cocoon.setContextDir(contextDir);
262: }
263: }
264: if (line.hasOption(CONFIG_FILE_OPT)) {
265: cocoon.setConfigFile(line
266: .getOptionValue(CONFIG_FILE_OPT));
267: }
268: if (line.hasOption(LOG_KIT_OPT)) {
269: cocoon.setLogKit(line.getOptionValue(LOG_KIT_OPT));
270: }
271: if (line.hasOption(LOGGER_OPT)) {
272: cocoon.setLogger(line.getOptionValue(LOGGER_OPT));
273: }
274: if (line.hasOption(LOG_LEVEL_OPT)) {
275: cocoon.setLogLevel(line.getOptionValue(LOG_LEVEL_OPT));
276: }
277: if (line.hasOption(AGENT_OPT)) {
278: cocoon.setAgentOptions(line.getOptionValue(AGENT_OPT));
279: }
280: if (line.hasOption(ACCEPT_OPT)) {
281: cocoon
282: .setAcceptOptions(line
283: .getOptionValue(ACCEPT_OPT));
284: }
285: if (line.hasOption(DEFAULT_FILENAME_OPT)) {
286: cocoon.setDefaultFilename(line
287: .getOptionValue(DEFAULT_FILENAME_OPT));
288: }
289: if (line.hasOption(BROKEN_LINK_FILE_OPT)) {
290: listener.setReportFile(line
291: .getOptionValue(BROKEN_LINK_FILE_OPT));
292: }
293: if (line.hasOption(FOLLOW_LINKS_OPT)) {
294: cocoon.setFollowLinks(BooleanUtils.toBoolean(line
295: .getOptionValue(FOLLOW_LINKS_OPT)));
296: }
297: if (line.hasOption(CONFIRM_EXTENSIONS_OPT)) {
298: cocoon
299: .setConfirmExtensions(BooleanUtils
300: .toBoolean(line.getOptionValue(
301: CONFIRM_EXTENSIONS_OPT, "yes")));
302: }
303: if (line.hasOption(LOAD_CLASS_OPT)) {
304: cocoon.addLoadedClasses(Arrays.asList(line
305: .getOptionValues(LOAD_CLASS_OPT)));
306: }
307: if (line.hasOption(URI_FILE_OPT)) {
308: cocoon.addTargets(BeanConfigurator.processURIFile(line
309: .getOptionValue(URI_FILE_OPT)), destDir);
310: }
311:
312: cocoon.addTargets(line.getArgList(), destDir);
313:
314: listener.messageGenerated(CocoonBean.getProlog());
315:
316: if (cocoon.getTargetCount() == 0
317: && cocoon.isPrecompileOnly()) {
318: listener
319: .messageGenerated("Please, specify at least one starting URI.");
320: System.exit(1);
321: }
322:
323: cocoon.initialize();
324: cocoon.process();
325: cocoon.dispose();
326:
327: listener.complete();
328:
329: int exitCode = (listener.isSuccessful() ? 0 : 1);
330: System.exit(exitCode);
331: }
332: }
333:
334: private static String processXConf(CocoonBean cocoon,
335: String filename, String destDir, String uriGroup) {
336:
337: try {
338: final DocumentBuilder builder = DocumentBuilderFactory
339: .newInstance().newDocumentBuilder();
340: final Document xconf = builder.parse(new File(filename)
341: .toURL().toExternalForm());
342: return BeanConfigurator.configure(xconf, cocoon, destDir,
343: uriGroup, listener);
344: } catch (Exception e) {
345: System.out.println("ERROR: " + e.getMessage());
346: return destDir;
347: }
348: }
349:
350: /**
351: * Print the usage message and exit
352: */
353: private static void printUsage() {
354: HelpFormatter formatter = new HelpFormatter();
355:
356: formatter.printHelp("cocoon cli [options] [targets]",
357: CocoonBean.getProlog(), options,
358: "Note: the context directory defaults to '"
359: + Constants.DEFAULT_CONTEXT_DIR + "'");
360: }
361:
362: /**
363: * Print the version string and exit
364: */
365: private static void printVersion() {
366: System.out.println(Constants.VERSION);
367: }
368: }
|