Return-Path: Delivered-To: apmail-incubator-geronimo-cvs-archive@www.apache.org Received: (qmail 37814 invoked from network); 4 Sep 2003 05:27:32 -0000 Received: from daedalus.apache.org (HELO mail.apache.org) (208.185.179.12) by minotaur-2.apache.org with SMTP; 4 Sep 2003 05:27:32 -0000 Received: (qmail 61104 invoked by uid 500); 4 Sep 2003 05:26:02 -0000 Delivered-To: apmail-incubator-geronimo-cvs-archive@incubator.apache.org Received: (qmail 61059 invoked by uid 500); 4 Sep 2003 05:26:01 -0000 Mailing-List: contact geronimo-cvs-help@incubator.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: list-post: Reply-To: geronimo-dev@incubator.apache.org Delivered-To: mailing list geronimo-cvs@incubator.apache.org Received: (qmail 60985 invoked from network); 4 Sep 2003 05:26:00 -0000 Received: from unknown (HELO minotaur.apache.org) (209.237.227.194) by daedalus.apache.org with SMTP; 4 Sep 2003 05:26:00 -0000 Received: (qmail 37158 invoked by uid 1716); 4 Sep 2003 05:26:19 -0000 Date: 4 Sep 2003 05:26:19 -0000 Message-ID: <20030904052619.37157.qmail@minotaur.apache.org> From: jboynes@apache.org To: incubator-geronimo-cvs@apache.org Subject: cvs commit: incubator-geronimo/modules/core/src/java/org/apache/geronimo/console/cli DConfigBeanConfigurator.java Deployer.java X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N jboynes 2003/09/03 22:26:19 Added: modules/core/src/java/org/apache/geronimo/console/cli DConfigBeanConfigurator.java Deployer.java Log: GERONIMO-10 patch (v5) from Aaron Mulder Not sure on these and their relationship to twiddle - is this duplicate functionality? Revision Changes Path 1.1 incubator-geronimo/modules/core/src/java/org/apache/geronimo/console/cli/DConfigBeanConfigurator.java Index: DConfigBeanConfigurator.java =================================================================== /* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" and * "Apache Geronimo" must not be used to endorse or promote products * derived from this software without prior written permission. For * written permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * "Apache Geronimo", nor may "Apache" appear in their name, without * prior written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * ==================================================================== */ package org.apache.geronimo.console.cli; import java.beans.*; import java.io.*; import java.util.*; import java.lang.reflect.InvocationTargetException; import javax.enterprise.deploy.spi.DConfigBean; import javax.enterprise.deploy.spi.DConfigBeanRoot; import javax.enterprise.deploy.spi.exceptions.ConfigurationException; import javax.enterprise.deploy.model.DDBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.geronimo.common.propertyeditor.PropertyEditors; /** * Knows how to configure a DConfigBean at the command line. The editing process * is a series of reads and writes to the provided input and output streams, * which basically presents information and a prompt to the user, gathers their * input, and repeats. They can navigate through a tree of DConfigBeans and * Java Beans, adding, removing, and editing properies on beans where * appropriate. *

* Note: it might make sense to break this class up eventually. Particularly if * we want to allow the user to navigate between arbitrary DDBeans (standard DD) * and their matching DConfigBeans (server-specific DD). Right now they can only * edit one tree at a time, either the whole DDBean tree, or the whole * DConfigBean tree. *

* @version $Revision: 1.1 $ $Date: 2003/09/04 05:26:19 $ */ public class DConfigBeanConfigurator { private final static Log log = LogFactory.getLog(DConfigBeanConfigurator.class); private PrintWriter out; private BufferedReader in; private Stack beans = new Stack(); /** * Creates a new instance, based on the supplied config bean root and * input and output streams. */ public DConfigBeanConfigurator(DConfigBeanRoot bean, PrintWriter out, BufferedReader in) { this.out = out; this.in = in; beans.push(bean); } /** * Begins the process of configuring the DConfigBean tree. When this method * returns, the user has finished editing the DConfigBeans (or a fatal error * caused the editing to abort). * * @return true if the editing completed normally * (false if there was a fatal error). */ public boolean configure() { try { initialize(); return true; } catch(IntrospectionException e) { log.error("Unable to introspect a JavaBean", e); } catch(IOException e) { log.error("Unable to gather input from user", e); } catch(InvocationTargetException e) { log.error("Unable to read or write a JavaBean property", e.getTargetException()); } catch(IllegalAccessException e) { log.error("Unable to read or write a JavaBean property", e); } catch(ConfigurationException e) { log.error("Unable to generate a child DConfigBean", e); } catch(InstantiationException e) { log.error("Unable to generate a child bean", e); } return false; } /** * The main logic loop for the editing process. This starts an endless loop, * where each iteration prints information on the current bean and prompts * the user for an action. A stack is maintained of the current beans from * root (bottom of the stack) to the current leaf node (top of the stack). * The main options offered here are to move to a parent or child bean (if * available) or to edit a property on the current bean. */ private void initialize() throws IntrospectionException, IOException, InvocationTargetException, IllegalAccessException, ConfigurationException, InstantiationException { boolean forward = true; BeanInfo info; PropertyDescriptor[] properties = new PropertyDescriptor[0]; PropertyDescriptor[] readOnly = new PropertyDescriptor[0]; PropertyDescriptor[] childProps = new PropertyDescriptor[0]; Map childTypes = new HashMap(); while(true) { out.println("\n\n"); Object bean = beans.peek(); int count; // Load top-level info info = Introspector.getBeanInfo(bean.getClass()); String indent = printLocation(); // Load children childTypes.clear(); if(bean instanceof DConfigBean) { DConfigBean dcb = (DConfigBean) bean; String[] xpaths = dcb.getXpaths(); for(int i=0; i 0) { out.println("Properties for "+getFullName(bean)+":"); } for(int i = 0; i < properties.length; i++) { PropertyDescriptor property = properties[i]; out.println(" "+(++count)+": "+property.getDisplayName()+" ("+property.getReadMethod().invoke(bean, new Object[0])+")"); } out.flush(); // Auto-navigate if(properties.length == 0 && childTypes.size() == 1 && childProps.length == 0) { DConfigBean[] children = (DConfigBean[])childTypes.values().iterator().next(); if(children.length == 1) { if(forward) { out.println("Nothing interesting to do here. Moving on."); beans.push(children[0]); continue; } else if(beans.size() > 1) { out.println("Nothing interesting to do here. Moving on."); beans.pop(); continue; } } } else if(properties.length == 0 && childTypes.size() == 0 && childProps.length == 1) { if(!(childProps[0] instanceof IndexedPropertyDescriptor)) { if(forward) { out.println("Nothing interesting to do here. Moving on."); beans.push(childProps[0].getReadMethod().invoke(bean, new Object[0])); continue; } else if(beans.size() > 1) { out.println("Nothing interesting to do here. Moving on."); beans.pop(); continue; } } } if(properties.length > 0) { out.println(); } // Show navigation options out.print("Action ("); boolean first = true; if(properties.length > 0) { if(!first) {out.print(" / ");} out.print("Edit [P]roperty"); first = false; } if(childTypes.size() > 0 || childProps.length > 0) { if(!first) {out.print(" / ");} out.print("Move [D]own"); first = false; } if(beans.size() > 1) { if(!first) {out.print(" / ");} out.print("Move [U]p"); first = false; } if(!first) {out.print(" / ");} out.print("[Q]uit"); first = false; out.print("): "); out.flush(); String choice = in.readLine().trim().toLowerCase(); if(choice.equals("u")) { forward = false; beans.pop(); continue; } else if(choice.equals("d")) { forward = true; if(childTypes.size() == 0 && childProps.length == 0) { log.warn("No children available here."); continue; } else { selectChildBean(childTypes, bean, childProps); continue; } } else if(choice.equals("q")) { return; } else if(choice.equals("p")) { if(properties.length == 0) { log.warn("No editable properties available here."); continue; } else { editProperty(bean, properties); continue; } } else if(isNumber(choice)) { int value = Integer.parseInt(choice); if(value > 0 && value <= properties.length) { editProperty(bean, properties[value-1]); continue; } } log.error("I don't know how to do that (yet)"); } } /** * The user wants to edit a property. This method figures out which one (of * the properties available for the bean). */ private void editProperty(Object bean, PropertyDescriptor[] properties) throws IOException, InvocationTargetException, IllegalAccessException { if(properties.length == 1) { editProperty(bean, properties[0]); return; } String choice = null; while(true) { out.print("Edit which property (1-"+properties.length+")? "); out.flush(); choice = in.readLine(); try { int value = Integer.parseInt(choice); if(value > 0 && value <= properties.length) { editProperty(bean, properties[value-1]); return; } } catch(NumberFormatException e) {} } } /** * Manages the editing of a single property. */ private void editProperty(final Object bean, final PropertyDescriptor property) throws InvocationTargetException, IllegalAccessException, IOException { final PropertyEditor pe = PropertyEditors.findEditor(property.getPropertyType()); pe.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { try { property.getWriteMethod().invoke(bean, new Object[]{pe.getValue()}); pe.removePropertyChangeListener(this); } catch(IllegalAccessException e) { log.error("Not allowed to set property", e); } catch(IllegalArgumentException e) { log.error("Invalid value for property", e); } catch(InvocationTargetException e) { log.error("Exception occured while setting property", e.getTargetException()); } } }); out.println("\nEditing Property "+property.getDisplayName()); Object value = property.getReadMethod().invoke(bean, new Object[0]); if(value == null) { value = pe.getJavaInitializationString(); } out.println(" Old value is: '"+value+"'"); out.println(" Specify a new value. Enter nothing to keep the current value.\n" + " Type (empty) for an empty string or (null) for a null."); out.print("New Value: "); out.flush(); String choice = in.readLine(); if(choice.equals("")) { return; } else if(choice.equals("(null)")) { choice = null; } else if(choice.equals("(empty)")) { choice = ""; } pe.setAsText(choice); } /** * The user wants to move to a child bean. This method figures out which * one. It may be a child DConfigBean or a child property where we don't * have a property editor for that property type so we treat the whole * thing as a child bean. */ private void selectChildBean(Map types, Object bean, PropertyDescriptor[] props) throws IOException, InvocationTargetException, IllegalAccessException, InstantiationException { DConfigBean[] cbs = null; PropertyDescriptor prop = null; int count; String choice; if(types.size()+props.length > 1) { count = 0; out.println("\nAvailable Children:"); for(Iterator iterator = types.keySet().iterator(); iterator.hasNext();) { String name = (String) iterator.next(); out.println(" ["+(++count)+"] "+name); } for(int i = 0; i < props.length; i++) { out.println(" ["+(++count)+"] "+props[i].getDisplayName()); } while(true) { out.print("Select child type (1-"+(types.size()+props.length)+"): "); out.flush(); choice = in.readLine(); try { int value = Integer.parseInt(choice); if(value > 0 && value <= types.size()) { count = 0; String key = null; for(Iterator iterator = types.keySet().iterator(); iterator.hasNext() && count++ < value;) { key = (String) iterator.next(); } cbs = (DConfigBean[]) types.get(key); if(cbs != null) { break; } } else if(value > types.size() && value <= (types.size()+props.length)) { prop = props[value-types.size()-1]; break; } } catch(NumberFormatException e) {} } } else { if(types.size() == 1) { cbs = (DConfigBean[])types.values().iterator().next(); } else if(props.length == 1) { prop = props[0]; } else { log.error("You've confused me. Please try again."); } } if(cbs != null) { selectChildDConfigBean(cbs); } else if(prop != null) { selectChildProperty(bean, prop); } } /** * It turns out the user wants navigate to a child property (where we don't * have an editor for the property type, so we treat it as a child bean). * If the is a plain property, this method will just go there. If it's an * indexed property, this method presents CRUD options. */ private void selectChildProperty(Object bean, PropertyDescriptor prop) throws InvocationTargetException, IllegalAccessException, IOException, InstantiationException { //todo: consider handling indexed properties that are themselves arrays? if(!(prop instanceof IndexedPropertyDescriptor)) { beans.push(prop.getReadMethod().invoke(bean, new Object[0])); return; } String choice; Object[] values; while(true) { out.println("\nEditing list of "+prop.getDisplayName()); values = (Object[]) prop.getReadMethod().invoke(bean, new Object[0]); if(values.length == 0) { out.println(" (list is currently empty)"); } for(int i = 0; i < values.length; i++) { out.println(" "+(i+1)+": "+values[i]); } out.print("Action ([C]reate entry"); if(values.length > 0) { out.print(" / [D]elete entry / edit entry [1"+(values.length > 1 ? "-"+values.length : "")+"]"); } out.print(" / [B]ack): "); out.flush(); choice = in.readLine().trim().toLowerCase(); if(choice.equals("c")) { Object[] newv = (Object[])java.lang.reflect.Array.newInstance(values.getClass().getComponentType(), values.length+1); System.arraycopy(values, 0, newv, 0, values.length); newv[values.length] = values.getClass().getComponentType().newInstance(); prop.getWriteMethod().invoke(bean, new Object[]{newv}); continue; } else if(choice.equals("b")) { return; } else if(isNumber(choice)) { int number = Integer.parseInt(choice); if(number > 0 && number <= values.length) { beans.push(values[number-1]); return; } } else { log.warn("I didn't understand that"); } } } /** * Checks whether a value entered by the user is composed entirely of digits. */ private boolean isNumber(String choice) { for(int i=0; i 0; } /** * It turns out the user wants to edit a child DConfigBean. So far, we just * know what type of child bean they want (e.g. "one of the resource * references"). This method figures out which specific instance of that * they want to edit (identify a specific resource reference). */ private void selectChildDConfigBean(DConfigBean[] cbs) throws IOException { String choice; if(cbs.length == 1) { beans.push(cbs[0]); return; } out.println("\nAvailable Children:"); for(int i = 0; i < cbs.length; i++) { out.println(" ["+(i+1)+"] "+cbs[i]); } while(true) { out.print("Select child (1-"+cbs.length+"): "); out.flush(); choice = in.readLine(); try { int value = Integer.parseInt(choice); if(value > 0 && value <= cbs.length) { beans.push(cbs[value-1]); break; } } catch(NumberFormatException e) {} } } /** * Displays the user's current position in the stack of beans. This * method shows everything down to the current position. The caller * must add on the children of the current node. * * @return The String full of spaces representating the indentation * for any children of the last bean displayed. */ private String printLocation() throws IntrospectionException { out.println(" ---------- Editing Server-Specific DD ---------- "); String here = ""; int count = 0; for(Iterator iterator = beans.iterator(); iterator.hasNext();) { ++count; Object temp = iterator.next(); if(!here.equals("")) { out.print(here); out.print("+ "); } if(count == beans.size()) {out.print("[[[ ");} out.print(getFullName(temp)); if(count == beans.size()) {out.print(" ]]]");} here = here + " "; out.println(); } return here; } /** * Gets the name of a class of beans (e.g. "Resource Reference") * followed by the description of the specific instances (e.g. * jdbc/SomeDatabase). */ private String getFullName(Object bean) throws IntrospectionException { String name = bean.toString(); if(name.length() > 40 || name.indexOf("@") > 0) {//todo: check whether toString has been overridden name = ""; } else { name = " ("+name+")"; } return Introspector.getBeanInfo(bean.getClass()).getBeanDescriptor().getDisplayName()+name; } /** * Gets the sub-list of the supplied properties that are readable, writable, * have a property editor, and are not on the list to specifically exclude. */ private PropertyDescriptor[] getNormalProperties(PropertyDescriptor[] descriptors) { List list = new ArrayList(descriptors.length); for(int i = 0; i < descriptors.length; i++) { PropertyDescriptor descriptor = descriptors[i]; if(isInvisible(descriptor) || descriptor.getReadMethod() == null || descriptor.getWriteMethod() == null || PropertyEditors.findEditor(descriptor.getPropertyType()) == null) { continue; } list.add(descriptors[i]); } return (PropertyDescriptor[]) list.toArray(new PropertyDescriptor[list.size()]); } /** * Gets the sub-list of the supplied properties that are readable, not * writable, and are not on the list to specifically exclude. */ private PropertyDescriptor[] getReadOnly(PropertyDescriptor[] descriptors) { List list = new ArrayList(descriptors.length); for(int i = 0; i < descriptors.length; i++) { PropertyDescriptor descriptor = descriptors[i]; if(isInvisible(descriptor) || descriptor.getWriteMethod() != null || descriptor.getReadMethod() == null) { continue; } list.add(descriptors[i]); } return (PropertyDescriptor[]) list.toArray(new PropertyDescriptor[list.size()]); } /** * Gets the sub-list of the supplied properties that are readable, writeable, * and have no property editor. These will be treated as child properties, * so their properties will in turn be presented for editing. */ private PropertyDescriptor[] getChildProperties(PropertyDescriptor[] descriptors) { List list = new ArrayList(descriptors.length); for(int i = 0; i < descriptors.length; i++) { PropertyDescriptor descriptor = descriptors[i]; if(isInvisible(descriptor) || descriptor.getWriteMethod() == null || descriptor.getReadMethod() == null || PropertyEditors.findEditor(descriptor.getPropertyType()) != null) { continue; } list.add(descriptors[i]); } return (PropertyDescriptor[]) list.toArray(new PropertyDescriptor[list.size()]); } /** * Checks whether a property is one of the ones we want to specifically * ignore/suppress. */ private boolean isInvisible(PropertyDescriptor descriptor) { return descriptor.getName().equals("class") || descriptor.getName().equals("DDBean") || descriptor.getName().equals("xpaths"); } } 1.1 incubator-geronimo/modules/core/src/java/org/apache/geronimo/console/cli/Deployer.java Index: Deployer.java =================================================================== /* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" and * "Apache Geronimo" must not be used to endorse or promote products * derived from this software without prior written permission. For * written permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * "Apache Geronimo", nor may "Apache" appear in their name, without * prior written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * ==================================================================== */ package org.apache.geronimo.console.cli; import java.util.jar.JarFile; import java.io.*; import java.net.URLClassLoader; import java.net.URL; import java.net.MalformedURLException; import javax.enterprise.deploy.spi.*; import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException; import javax.enterprise.deploy.spi.exceptions.InvalidModuleException; import javax.enterprise.deploy.spi.exceptions.ConfigurationException; import javax.enterprise.deploy.shared.factories.DeploymentFactoryManager; import javax.enterprise.deploy.model.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.geronimo.enterprise.deploy.tool.EjbDeployableObject; /** * Initializes a command-line JSR-88 deployer. * * @version $Revision: 1.1 $ $Date: 2003/09/04 05:26:19 $ */ public class Deployer { private static final Log log = LogFactory.getLog(Deployer.class); static { try { Class.forName("org.apache.geronimo.enterprise.deploy.provider.GeronimoDeploymentFactory"); } catch(ClassNotFoundException e) { log.error("Unable to load Geronimo JSR-88 implementation"); } } private DeploymentManager deployer; private DeployableObject standardModule; private DeploymentConfiguration serverModule; private PrintWriter out; private BufferedReader in; private EjbJarInfo jarInfo; private File saveDir = new File(System.getProperty("user.dir")); /** * Creates a new instance using System.out and System.in to interact with * the user. The user will need ot begin by selecting an EJB JAR file * to work with. */ public Deployer() throws IllegalStateException, IllegalArgumentException { this(new PrintWriter(new OutputStreamWriter(System.out), true), new BufferedReader(new InputStreamReader(System.in))); } /** * Creates a new instance for the provided EJB JAR file using System.out * and System.in to interact with the user. */ public Deployer(File jarFile) throws IllegalStateException, IllegalArgumentException { this(jarFile, new PrintWriter(new OutputStreamWriter(System.out), true), new BufferedReader(new InputStreamReader(System.in))); } /** * Creates a new instance using the provided input/output streams to * interact with the user. The user will need ot begin by selecting an EJB * JAR file to work with. */ public Deployer(PrintWriter out, Reader in) throws IllegalStateException, IllegalArgumentException { this.out = out; this.in = in instanceof BufferedReader ? (BufferedReader)in : new BufferedReader(in); if(!connect()) { throw new IllegalStateException("Unable to connect to Deployment service"); } } /** * Creates a new instance for the provided EJB JAR file and input/output * streams. */ public Deployer(File jarFile, PrintWriter out, Reader in) throws IllegalStateException, IllegalArgumentException { this.out = out; this.in = in instanceof BufferedReader ? (BufferedReader)in : new BufferedReader(in); try { jarInfo = new EjbJarInfo(); jarInfo.file = jarFile; jarInfo.jarFile = new JarFile(jarFile); } catch(IOException e) { throw new IllegalArgumentException(jarFile+" is not a valid JAR file!"); } if(!connect() || !initializeEjbJar()) { throw new IllegalStateException("Unable to connect to Deployment service or prepare deployment information"); } } /** * Enters the deployment user interface. When this method returns, the * user has finished their deployment activities. */ public void run() { workWithoutModule(); deployer.release(); } /** * Prompts the user to enter a Deployer URL and then gets a DeploymentManager * for that URL. * * @return true if the connection was successful. */ private boolean connect() { out.println("\n\nEnter the deployer URL. Leave blank for the default URL 'deployer:geronimo:'"); out.print("URL: "); out.flush(); try { String url = in.readLine(); if(url.equals("")) { url = "deployer:geronimo:"; } deployer = DeploymentFactoryManager.getInstance().getDisconnectedDeploymentManager(url); } catch(DeploymentManagerCreationException e) { log.error("Can't create deployment manager",e); return false; } catch(IOException e) { log.error("Unable to read user input", e); return false; } return true; } /** * Loads the deployment descriptor information from the specific EJB JAR * file. * * @return true if the deployment information was loaded * successfully. */ private boolean initializeEjbJar() { try { ClassLoader loader = new URLClassLoader(new URL[]{jarInfo.file.toURL()}, ClassLoader.getSystemClassLoader()); standardModule = new EjbDeployableObject(jarInfo.jarFile, loader); } catch(MalformedURLException e) { out.println("ERROR: "+jarInfo.file+" is not a valid JAR file!"); return false; } try { serverModule = deployer.createConfiguration(standardModule); } catch(InvalidModuleException e) { out.println("ERROR: Unable to initialize a Geronimo DD for EJB JAR "+jarInfo.file); return false; } jarInfo.ejbJar = standardModule.getDDBeanRoot(); jarInfo.editingEjbJar = true; try { jarInfo.ejbJarConfig = serverModule.getDConfigBeanRoot(jarInfo.ejbJar); initializeDConfigBean(jarInfo.ejbJarConfig); } catch(ConfigurationException e) { log.error("Unable to initialize server-specific deployment information", e); return false; } return true; } /** * Presents a user interface to let the user take high-level deployment * actions. This lets them do the things you do without reference to a * particular EJB JAR. */ private void workWithoutModule() { while(true) { if(jarInfo != null) { workWithEjbJar(); continue; } out.println("\n\nNo J2EE module is currently selected."); out.println(" -- Select one or more servers or clusters to work with"); // DM.getTargets() out.println(" -- Start non-running modules on the selected servers/clusters"); out.println(" -- Stop running modules on the selected servers/clusters"); out.println(" -- Undeploy modules from the selected servers/clusters"); out.println(" -- View modules on the selected servers/clusters"); out.println(" 6) Select an EJB JAR to configure, deploy, or redeploy"); //todo: change text when other modules are supported out.println(" 7) Disconnect from any servers."); String choice; while(true) { out.print("Action ([6-7] or [Q]uit): "); out.flush(); try { choice = in.readLine().trim().toLowerCase(); } catch(IOException e) { log.error("Unable to read user input", e); return; } if(choice.equals("6")) { selectModule(); break; } else if(choice.equals("7")) { deployer.release(); out.println("Released any server resources and disconnected."); break; } else if(choice.equals("q")) { return; } } } } /** * Prompts the user to select a J2EE module to work with. * * Currently handles EJB JAR modules only. */ private void selectModule() { out.println("\nCurrent directory is "+saveDir); out.println("Select an EJB JAR file to load."); String choice; File file; while(true) { out.print("File Name: "); out.flush(); try { choice = in.readLine().trim(); } catch(IOException e) { log.error("Unable to read user input", e); return; } file = new File(saveDir, choice); if(!file.canRead() || file.isDirectory()) { out.println("ERROR: cannot read from this file. Please try again."); continue; } saveDir = file.getParentFile(); break; } try { jarInfo = new EjbJarInfo(); jarInfo.file = file; jarInfo.jarFile = new JarFile(jarInfo.file); } catch(IOException e) { out.println("ERROR: "+file+" is not a valid JAR file!"); jarInfo = null; return; } if(!initializeEjbJar()) { jarInfo = null; return; } } /** * Presents a user interface for a user to work with an EJB JAR. */ private void workWithEjbJar() { while(true) { out.println("\n\nLoaded an EJB JAR. Working with the ejb-jar.xml deployment descriptor."); out.println(" -- Edit the standard EJB deployment descriptor (ejb-jar.xml)"); out.println(" 2) Edit the corresponding server-specific deployment information"); out.println(" 3) Load a saved set of server-specific deployment information"); out.println(" -- Save the current set of server-specific deployment information"); out.println(" -- Edit web services deployment information"); out.println(" -- Deploy or redeploy the JAR into the application server"); out.println(" 7) Select a new EJB JAR to work with"); //todo: adjust text when other modules are accepted out.println(" 8) Manage existing deployments in the server"); String choice; while(true) { out.print("Action ([2-3,7,8] or [Q]uit): "); out.flush(); try { choice = in.readLine().trim().toLowerCase(); } catch(IOException e) { log.error("Unable to read user input", e); return; } if(choice.equals("2")) { editServerSpecificDD(); break; } else if(choice.equals("3")) { loadServerSpecificDD(); break; } else if(choice.equals("4")) { saveServerSpecificDD(); break; } else if(choice.equals("7")) { selectModule(); if(jarInfo != null) { break; } else { return; } } else if(choice.equals("8")) { //todo: prompt to save if modifications were made jarInfo = null; return; } else if(choice.equals("q")) { jarInfo = null; return; } } } } /** * Loads the server-specific deployment information from a file on disk. * Note that in JSR-88, server-specific DDs are not saved in the * JAR/EAR/whatever. */ private void loadServerSpecificDD() { out.println("\nCurrent directory is "+saveDir); out.println("Select a file name. The server-specific deployment information for the "); out.println((jarInfo.editingEjbJar ? "ejb-jar.xml" : "Web Services DD")+" will be loaded from the file you specify."); String choice; while(true) { out.print("File Name: "); out.flush(); try { choice = in.readLine().trim(); } catch(IOException e) { log.error("Unable to read user input", e); return; } File file = new File(saveDir, choice); if(!file.canRead() || file.isDirectory()) { out.println("ERROR: cannot read from this file. Please try again."); continue; } saveDir = file.getParentFile(); try { BufferedInputStream fin = new BufferedInputStream(new FileInputStream(file)); DConfigBeanRoot root = serverModule.restoreDConfigBean(fin, jarInfo.editingEjbJar ? jarInfo.ejbJar : jarInfo.webServices); fin.close(); if(jarInfo.editingEjbJar) { jarInfo.ejbJarConfig = root; } else { jarInfo.webServicesConfig = root; } out.println("Deployment information loaded from "+file.getName()); return; } catch(IOException e) { log.error("Unable to read from file", e); return; } catch(ConfigurationException e) { out.println("ERROR: "+e.getMessage()); return; } } } /** * Saves the server-specific deployment information to a file on disk. * Note that in JSR-88, server-specific DDs are not saved in the * JAR/EAR/whatever. */ private void saveServerSpecificDD() { out.println("\nCurrent directory is "+saveDir); out.println("Select a file name. The server-specific deployment information for the "); out.println((jarInfo.editingEjbJar ? "ejb-jar.xml" : "Web Services DD")+" will be saved to the file you specify."); String choice; try { while(true) { out.print("File Name: "); out.flush(); choice = in.readLine().trim(); File file = new File(saveDir, choice); if((file.exists() && !file.canWrite()) || (!file.exists() && !file.getParentFile().canWrite()) || file.isDirectory()) { out.println("ERROR: cannot write to this file. Please try again."); continue; } if(file.exists()) { out.print("File already exists. Overwrite (Y/N)? "); out.flush(); choice = in.readLine().trim().toLowerCase(); if(choice.equals("n")) { // todo: makre sure they entered y or n continue; } } saveDir = file.getParentFile(); try { BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(file)); serverModule.saveDConfigBean(fout, jarInfo.editingEjbJar ? jarInfo.ejbJarConfig : jarInfo.webServicesConfig); fout.close(); out.println("Deployment information saved to "+file.getName()); return; } catch(IOException e) { log.error("Unable to write to file", e); return; } catch(ConfigurationException e) { out.println("ERROR: "+e.getMessage()); return; } } } catch(IOException e) { log.error("Unable to read user input", e); return; } } /** * Marches recursively through the DConfigBean tree to initialize * DConfigBeans for all the interesting DDBeans. Once this is done, and * DDBean changes need to be relayed to the DConfigBeans that listn on them. */ private void initializeDConfigBean(DConfigBean dcb) throws ConfigurationException { String[] xpaths = dcb.getXpaths(); for(int i=0; i