Java tutorial
/* * Copyright (C) 2007 Unicon, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this distribution. It is also available here: * http://www.fsf.org/licensing/licenses/gpl.html * * As a special exception to the terms and conditions of version * 2 of the GPL, you may redistribute this Program in connection * with Free/Libre and Open Source Software ("FLOSS") applications * as described in the GPL FLOSS exception. You should have received * a copy of the text describing the FLOSS exception along with this * distribution. */ package net.unicon.academus.apps.messaging; import java.io.FileInputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.sql.DataSource; import net.unicon.academus.apps.ConfigHelper; import net.unicon.alchemist.access.Identity; import net.unicon.alchemist.access.IdentityType; import net.unicon.alchemist.access.Principal; import net.unicon.alchemist.rdbms.SimpleDataSource; import net.unicon.civis.ICivisFactory; import net.unicon.mercury.DraftMessage; import net.unicon.mercury.EntityType; import net.unicon.mercury.IMessageFactory; import net.unicon.mercury.IRecipient; import net.unicon.mercury.MercuryException; import net.unicon.mercury.XmlFormatException; import org.dom4j.Attribute; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * Command line Batch Message Importer. * * @see #usage() */ public class BatchImporter { private static final String DEFAULT_CONFIG = "/config/messaging-portlet.xml"; /** * Parse command line options into a hashmap of name-value pairs. * Arguments with values are considered those provided as '-myarg=myvalue', * split at the '='. * * Non-value arguments will have {@link Boolean#TRUE Boolean.TRUE} as their * paired value. * * Any arguments that do not begin with a '-' character will be considered * non-arguments, and be stored in the special key 'nonarg' as a List. * * If no arguments are provided, or an argument named '-help' or '-?' is * provided, this method will call {@link #usage()} and exit. */ public static Map getopt(String[] args) { Map rslt = new HashMap(); rslt.put("nonarg", new ArrayList()); for (int i = 0; i < args.length; i++) { if (args[i].startsWith("-")) { if (args[i].indexOf('=') != -1) { String[] tmp = args[i].substring(1).split("=", 2); rslt.put(tmp[0], tmp[1]); } else { rslt.put(args[i].substring(1), Boolean.TRUE); } } else { ((List) rslt.get("nonarg")).add(args[i]); } } if (args.length == 0 || rslt.containsKey("help") || rslt.containsKey("?")) usage(); return rslt; } public static void warn(String msg) { System.err.println(msg); } public static void error(String msg) { System.err.println(msg); System.exit(1); } /** * Command usage. * * Usage for net.unicon.academus.apps.messaging.BatchImporter:<br> * java -cp <classpath> net.unicon.academus.apps.messaging.BatchImporter [arguments] <input files> * * <br>Arguments include: * <ul> * <li>-config=<configfile> [Optional; Default * "/config/messaging-portlet.xml" (from classpath)] Specify the location * of the MessagingPortlet configuration file. * * <li>-messagecenter=<messagecenter> [Optional; Default * "notifications"] Specify the message-center to be used. * * <li>-user=<username> [Optional; Default "admin"] Specify the * username to use as the sender. * * <li>-groups=<groups> [Optional] Specify the groups that the user * needs to be a part of. Comma delimited. * * <li>-altrecips=<altrecips> [Optional] Specify a comma-delimited * list of recipients to use in place of those specified in the import * file. * </ul> * * <p>In order to utilize database-oriented message centers, a DataSource must * be created. This can be done by providing the following properties (using the * -Dpropname=propvalue syntax for the 'java' command):</p> * <ul> * <li>simpledatasource.driver - JDBC Driver class * <li>simpledatasource.url - Database URL * <li>simpledatasource.user - Database username * <li>simpledatasource.pass - Database password * </ul> * * <p>Alternatively, you can specify the property 'simpledatasource.propsfile' * to point to a file containing the above properties.</p> * * At least one input file must be specified. */ public static void usage() { System.out.println("Usage for " + BatchImporter.class.getName() + ":"); System.out .println("\tjava -cp <classpath> " + BatchImporter.class.getName() + " [arguments] <input files>"); System.out.println(); System.out.println("Arguments include: "); System.out.println("\t-config=<configfile>\t\t[Optional; Default \"" + DEFAULT_CONFIG + "\"]"); System.out.println("\t\t\t\t\tSpecify the location of the MessagingPortlet configuration file."); System.out.println(); System.out.println("\t-messagecenter=<messagecenter>\t[Optional; Default \"notifications\"]"); System.out.println("\t\t\t\t\tSpecify the message-center to be used."); System.out.println(); System.out.println("\t-user=<username>\t\t[Optional; Default \"admin\"]"); System.out.println("\t\t\t\t\tSpecify the username to use as the sender."); System.out.println(); System.out.println("\t-groups=<groups>\t\t[Optional]"); System.out.println("\t\t\t\t\tSpecify the groups that the user needs to be a part of. Comma delimited."); System.out.println(); System.out.println("\t-altrecips=<altrecips>\t\t[Optional]"); System.out.println( "\t\t\t\t\tSpecify a comma-delimited list of recipients to use in place of those specified in the import file."); System.out.println(); System.out.println(); System.out.println("In order to utilize database-oriented message " + "centers, a DataSource must be created. This can be done by " + "providing the following properties (using the " + "-Dpropname=propvalue syntax for the 'java' command):"); System.out.println("\tsimpledatasource.driver - JDBC Driver class"); System.out.println("\tsimpledatasource.url - Database URL"); System.out.println("\tsimpledatasource.user - Database username"); System.out.println("\tsimpledatasource.pass - Database password"); System.out.println("Alternatively, you can specify the property " + "'simpledatasource.propsfile' to point to a file containing " + "the above properties."); System.out.println(); System.out.println("At least one input file must be specified."); System.exit(2); } public static void main(String[] args) throws Exception { Map argmap = getopt(args); List largs = (List) argmap.get("nonarg"); if (largs.isEmpty()) { warn("No input file(s) given."); usage(); } Map mcenters = null; String configFile = (String) argmap.get("config"); if (configFile == null) { URL u = BatchImporter.class.getResource(DEFAULT_CONFIG); if (u == null) { warn("No configuration file specified, and unable to load the default.\n" + "Please specify one using the '-config=<myconfig>' argument."); usage(); } else { configFile = u.toString(); } } // Message Center lookup String mcenter = (String) argmap.get("messagecenter"); if (mcenter == null) { mcenter = "notifications"; } MessageCenter mc = configure(configFile, mcenter); if (mc == null) error("Unable to locate a message-center with the id: " + mcenter); System.out.println("Using message-center: " + mc.getLabel()); // Civis factory parsing //ICivisFactory[] factories = configureCivis(configFile); ICivisFactory[] factories = null; // Access Broker related. List identities = new ArrayList(); // What user do we need to be? String username = (String) argmap.get("user"); if (username == null) username = "admin"; identities.add(new Identity(username, IdentityType.USER)); System.out.println("Using username: " + username); // Figure out what groups were requested to be a part of String tmp = (String) argmap.get("groups"); if (tmp != null) { String[] groups = tmp.split("[,;:\t]"); for (int i = 0; i < groups.length; i++) { identities.add(new Identity(groups[i], IdentityType.GROUP)); } } // Always add Everyone identities.add(new Identity("Everyone", IdentityType.GROUP)); // Look up the user's message factory Principal princ = new Principal((Identity[]) identities.toArray(new Identity[0])); FactoryInfo finfo = mc.createFactory(princ); if (finfo == null) error("Unable to authorize user '" + username + "' for message-center '" + mc.getLabel() + "'."); IMessageFactory fact = finfo.getFactory(); if (fact == null) error("Unable to authorize user '" + username + "' for message-center '" + mc.getLabel() + "'."); DraftMessage[] drafts = null; System.out.println(); System.out.println("Parsing input files..."); System.out.println(); try { drafts = parseFiles(largs, factories); } catch (Exception e) { warn("Failed to parse input files. See stack trace for further information."); e.printStackTrace(System.err); System.exit(1); } System.out.println("Done. " + drafts.length + " message(s) parsed."); tmp = (String) argmap.get("altrecips"); if (tmp != null) { String[] altRecips = tmp.split("[,;:\t]"); System.out.println(); System.out.println("Altering recipients list..."); alterRecipients(drafts, "To", altRecips); System.out.println("Done. " + drafts.length + " message(s) altered."); } System.out.println(); System.out.println("Sending imported messages..."); if (sendMessages(fact, drafts) == false) error("Failed to send imported messages. Please correct any errors ant try again."); System.out.println("Done. Batch import completed for " + drafts.length + " message(s)."); } public static DraftMessage[] parseFiles(List largs, ICivisFactory[] factories) throws Exception { Iterator it = largs.iterator(); List rslt = new ArrayList(); boolean hasError = false; while (it.hasNext()) { String file = (String) it.next(); System.out.print("Parsing file: " + file + " "); FileInputStream fin = new FileInputStream(file); DraftMessage[] drafts = null; try { drafts = ImportExportHelper.parseInputStream(fin, factories); } catch (XmlFormatException e) { System.out.println(); warn("Unable to parse input file '" + file + "': " + e.getMessage()); e.printStackTrace(System.err); hasError = true; } if (drafts == null || drafts.length == 0) { System.out.println(); warn("No valid messages found in file: " + file); hasError = true; } else { rslt.addAll(Arrays.asList(drafts)); System.out.println(" -- " + drafts.length + " message(s)"); } } System.out.println(); if (hasError) { error("Unable to successfully parse at least one input file. Please " + "check the output and correct any errors before trying " + "again."); } else if (rslt.isEmpty()) { error("No valid messages found in any input file(s).\n" + "Please provide input files containing messages."); } return (DraftMessage[]) rslt.toArray(new DraftMessage[0]); } public static void alterRecipients(DraftMessage[] drafts, String recipType, String[] altRecips) throws Exception { if (altRecips == null || altRecips.length == 0) error("No alternate recipients specified."); for (int i = 0; i < drafts.length; i++) { drafts[i].removeRecipients(); for (int j = 0; j < altRecips.length; j++) drafts[i].addRecipient(recipType, altRecips[j], altRecips[j], EntityType.USER); } } public static boolean sendMessages(IMessageFactory fact, DraftMessage[] drafts) { List errors = new ArrayList(); boolean hasError = false; // Validate the messages first. for (int i = 0; i < drafts.length; i++) { IRecipient[] recips = drafts[i].getRecipients(); String subject = drafts[i].getSubject(); String body = drafts[i].getBody(); if (recips == null || recips.length == 0) { errors.add("No recipients provided."); } if (subject == null || subject.trim().equals("")) { errors.add("No subject provided."); } if (body == null || body.trim().equals("")) { errors.add("No message body provided."); } if (!errors.isEmpty()) { Iterator it = errors.iterator(); warn("The following errors occurred while validating message " + i + ":"); while (it.hasNext()) { warn("\t" + (String) it.next()); } errors.clear(); hasError = true; } } if (!hasError) { try { for (int i = 0; i < drafts.length; i++) { System.out.println("[" + i + "] Sending message: " + drafts[i].getSubject()); fact.sendMessage(drafts[i]); } } catch (MercuryException e) { warn("Unable to send message(s): " + e.getMessage()); e.printStackTrace(System.err); hasError = true; } } return !hasError; } public static MessageCenter configure(String configFile, String mcenter) throws Exception { Element configElement = (Element) (new SAXReader()).read(configFile).selectSingleNode("messaging"); configElement = ConfigHelper.handle(configElement, false); // Bootstrap datasources with SimpleDataSource... List bsList = configElement.selectNodes("//*[@needsDataSource='true']"); if (!bsList.isEmpty()) { try { DataSource ds = new SimpleDataSource(); for (Iterator it = bsList.iterator(); it.hasNext();) { Element e = (Element) it.next(); Attribute impl = e.attribute("impl"); if (impl == null) throw new IllegalArgumentException("Elements with the 'needsDataSource' attribute " + " must have an 'impl' attribute."); Class.forName(impl.getValue()).getDeclaredMethod("bootstrap", new Class[] { DataSource.class }) .invoke(null, new Object[] { ds }); } } catch (Throwable e) { e.printStackTrace(System.err); error("Failed the DataSource bootstrapping. Please correct any errors."); } } // Handle any global configuration for the message factories. List mList = configElement.selectNodes("message-factory"); for (Iterator mIt = mList.iterator(); mIt.hasNext();) { Element e = (Element) mIt.next(); Attribute impl = e.attribute("impl"); if (impl == null) throw new IllegalArgumentException("Element <message-factory> must have an 'impl'" + " attribute."); try { Class.forName(impl.getValue()).getDeclaredMethod("parse", new Class[] { Element.class }) .invoke(null, new Object[] { e }); } catch (Throwable t) { throw new RuntimeException("Failed to parse the <message-factory> element.", t); } } Element e = (Element) configElement.selectSingleNode("message-center[@id='" + mcenter + "']"); return new MessageCenter(e); } /* This is not used because the only civis implementation currently requires the full portal to operate. private static ICivisFactory[] configureCivis(String configFile) throws Exception{ Element configElement = (Element)(new SAXReader()) .read(configFile) .selectSingleNode("messaging"); List facs = new ArrayList(); Iterator it = configElement.selectNodes("civis").iterator(); Element el = null; while(it.hasNext()){ el = (Element)it.next(); if (el != null) { Attribute impl = el.attribute("impl"); if (impl == null) throw new IllegalArgumentException( "Element <civis> must have an 'impl'" + " attribute."); try { facs.add((ICivisFactory)Class.forName(impl.getValue()) .getDeclaredMethod("parse", new Class[] { Element.class }) .invoke(null, new Object[] { el })); } catch (Throwable t) { throw new RuntimeException( "Failed to parse the <civis> element.", t); } } } return (ICivisFactory[])(facs.toArray(new ICivisFactory[0])); } */ }