You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by jb...@apache.org on 2003/09/04 07:26:19 UTC

cvs commit: incubator-geronimo/modules/core/src/java/org/apache/geronimo/console/cli DConfigBeanConfigurator.java Deployer.java

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
   * <http://www.apache.org/>.
   *
   * ====================================================================
   */
  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.
   * <p>
   * 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.
   * </p>
   * @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 <code>true</code> if the editing completed normally
       *        (<code>false</code> 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<xpaths.length; i++) {
                      DDBean[] ddbs = dcb.getDDBean().getChildBean(xpaths[i]);
                      if(ddbs.length != 0) {
                          DConfigBean[] list = new DConfigBean[ddbs.length];
                          for(int j = 0; j < ddbs.length; j++) {
                              list[j] = dcb.getDConfigBean(ddbs[j]);
                          }
                          childTypes.put(Introspector.getBeanInfo(list[0].getClass()).getBeanDescriptor().getDisplayName(), list);
                      }
                  }
                  for(Iterator iterator = childTypes.keySet().iterator(); iterator.hasNext();) {
                      String s = (String)iterator.next();
                      int number = ((DConfigBean[])childTypes.get(s)).length;
                      out.println(indent+"+ "+s+" ("+number+" entr"+(number == 1 ? "y" : "ies")+")");
                  }
              }
              childProps = getChildProperties(info.getPropertyDescriptors());
              for(int i = 0; i < childProps.length; i++) {
                  PropertyDescriptor prop = childProps[i];
                  if(prop instanceof IndexedPropertyDescriptor) {
                      int number = ((Object[])prop.getReadMethod().invoke(bean, new Object[0])).length;
                      out.println(indent+"+ "+prop.getDisplayName()+" ("+number+" entr"+(number == 1 ? "y" : "ies")+")");
                  } else {
                      out.println(indent+"+ "+prop.getDisplayName()+" (child property)");
                  }
              }
              out.println();
  
              // Load properties todo: handle properties of type bean but not DConfigBean and indexed properties
              count = 0;
              properties = getNormalProperties(info.getPropertyDescriptors());
              readOnly = getReadOnly(info.getPropertyDescriptors());
              for(int i = 0; i < readOnly.length; i++) {
                  PropertyDescriptor property = readOnly[i];
                  out.println(property.getDisplayName()+": "+property.getReadMethod().invoke(bean, new Object[0]));
              }
              if(properties.length > 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<choice.length(); i++) {
              if(!Character.isDigit(choice.charAt(i))) {
                  return false;
              }
          }
          return choice.length() > 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
   * <http://www.apache.org/>.
   *
   * ====================================================================
   */
  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 <tt>true</tt> 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 <tt>true</tt> 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<xpaths.length; i++) {
              DDBean[] ddbs = dcb.getDDBean().getChildBean(xpaths[i]);
              for(int j = 0; j < ddbs.length; j++) {
                  initializeDConfigBean(dcb.getDConfigBean(ddbs[j]));
              }
          }
      }
  
      /**
       * Hands over control to {@link DConfigBeanConfigurator} to let the user edit
       * the server-specific deployment information.
       */
      private void editServerSpecificDD() {
          new DConfigBeanConfigurator(jarInfo.ejbJarConfig, out, in).configure();
      }
  
      /**
       * Holds all the relevent data for an EJB JAR.
       */
      private static class EjbJarInfo {
          public File file;
          public JarFile jarFile;
          public DDBeanRoot ejbJar;
          public DConfigBeanRoot ejbJarConfig;
          public DDBeanRoot webServices;
          public DConfigBeanRoot webServicesConfig;
          public boolean editingEjbJar;
      }
  }
  
  
  

Re: cvs commit: incubator-geronimo/modules/core/src/java/org/apache/geronimo/console/cli DConfigBeanConfigurator.java Deployer.java

Posted by Dain Sundstrom <da...@coredevelopers.net>.
On Thursday, September 4, 2003, at 06:24 AM, Jason Dillon wrote:

>>> I looked at this and almost committed but noticed that it only 
>>> handled
>>> jars and was not very happy about that.  But I guess it is just the
>>> start.  The deploy commands should be able to deploy anything which 
>>> is
>>> deployable IMO.
>>
>> 	Umm, you opted not to commit the patch because it wasn't yet a
>> fully-functional product?  Further, instead of making a comment 
>> describing
>> the enhancements you felt were required in order for the patch to be
>> acceptable, you simply ignored the patch for two weeks?  Surely you 
>> jest!
>
> No I passed it on to Jeremy as he asked to review.

Actually it was my fault.  I was supposed to review it an dropped the 
ball.  Is it in now?

-dain


Re: cvs commit: incubator-geronimo/modules/core/src/java/org/apache/geronimo/console/cli DConfigBeanConfigurator.java Deployer.java

Posted by Jason Dillon <ja...@coredevelopers.net>.
>> My opinion is that these are Twiddle bits and do not belong here.
>> Especially not in a console/cli package.
>
> 	I put it in there because Dain suggested that package in a message
> on the 19th (though now that I look, he did recommend a separate module
> from core).  If you want it somewhere else, that's fine, but where?

I would drop them in command for now.


>> I looked at this and almost committed but noticed that it only handled
>> jars and was not very happy about that.  But I guess it is just the
>> start.  The deploy commands should be able to deploy anything which is
>> deployable IMO.
>
> 	Umm, you opted not to commit the patch because it wasn't yet a
> fully-functional product?  Further, instead of making a comment 
> describing
> the enhancements you felt were required in order for the patch to be
> acceptable, you simply ignored the patch for two weeks?  Surely you 
> jest!

No I passed it on to Jeremy as he asked to review.


> 	In any case, there's a bit of wiring that needs to happen to
> support each module type.  Right now only the EJB JAR code is in place 
> in
> JSR-88, so we could offer more options in the deploy client, but it 
> would
> just fail when the commands were passed on to JSR-88, and I thought 
> I'd be
> honest up front instead.  If you look at the code, there are todos 
> next to
> each EJB JAR line saying something along the lines of "change this once
> JSR-88 supports the other module types."

Thats fine.

--jason


Re: cvs commit: incubator-geronimo/modules/core/src/java/org/apache/geronimo/console/cli DConfigBeanConfigurator.java Deployer.java

Posted by Aaron Mulder <am...@alumni.princeton.edu>.
On Thu, 4 Sep 2003, Jason Dillon wrote:
> My opinion is that these are Twiddle bits and do not belong here.   
> Especially not in a console/cli package.

	I put it in there because Dain suggested that package in a message 
on the 19th (though now that I look, he did recommend a separate module 
from core).  If you want it somewhere else, that's fine, but where?

> I looked at this and almost committed but noticed that it only handled  
> jars and was not very happy about that.  But I guess it is just the  
> start.  The deploy commands should be able to deploy anything which is  
> deployable IMO.

	Umm, you opted not to commit the patch because it wasn't yet a
fully-functional product?  Further, instead of making a comment describing
the enhancements you felt were required in order for the patch to be
acceptable, you simply ignored the patch for two weeks?  Surely you jest!

	In any case, there's a bit of wiring that needs to happen to
support each module type.  Right now only the EJB JAR code is in place in
JSR-88, so we could offer more options in the deploy client, but it would
just fail when the commands were passed on to JSR-88, and I thought I'd be
honest up front instead.  If you look at the code, there are todos next to
each EJB JAR line saying something along the lines of "change this once
JSR-88 supports the other module types."

Aaron


Date: Tue, 19 Aug 2003 09:36:38 -0500
From: Dain Sundstrom <da...@coredevelopers.net>
Reply-To: geronimo-dev@incubator.apache.org
To: geronimo-dev@incubator.apache.org
Subject: Re: [core] why org.apache.geronimo.enterprise.deploy.provider
    package?

I agree that the product provider stuff is the stuff should be separate
from the tools.  Our deployment system should fully support the product
provider stuff plus a bunch of add-on features, so putting it all in a
single deployment package seems right to me.

For the tools, I think we should have a tools module with packages
something like this:

org.apache.console.web
org.apache.console.cli
org.apache.console.gui
org.apache.console.ant

Each of these packages would support management tasks and deployment
task (I classify deployment as just another management task).

-dain


Re: cvs commit: incubator-geronimo/modules/core/src/java/org/apache/geronimo/console/cli DConfigBeanConfigurator.java Deployer.java

Posted by Jason Dillon <ja...@coredevelopers.net>.
My opinion is that these are Twiddle bits and do not belong here.   
Especially not in a console/cli package.

I looked at this and almost committed but noticed that it only handled  
jars and was not very happy about that.  But I guess it is just the  
start.  The deploy commands should be able to deploy anything which is  
deployable IMO.

--jason


On Thursday, September 4, 2003, at 12:26  PM, jboynes@apache.org wrote:

> 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
>    * <http://www.apache.org/>.
>    *
>    *  
> ====================================================================
>    */
>   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.
>    * <p>
>    * 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.
>    * </p>
>    * @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 <code>true</code> if the editing completed normally
>        *        (<code>false</code> 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<xpaths.length; i++) {
>                       DDBean[] ddbs =  
> dcb.getDDBean().getChildBean(xpaths[i]);
>                       if(ddbs.length != 0) {
>                           DConfigBean[] list = new  
> DConfigBean[ddbs.length];
>                           for(int j = 0; j < ddbs.length; j++) {
>                               list[j] = dcb.getDConfigBean(ddbs[j]);
>                           }
>                            
> childTypes.put(Introspector.getBeanInfo(list[0].getClass()).getBeanDesc 
> riptor().getDisplayName(), list);
>                       }
>                   }
>                   for(Iterator iterator =  
> childTypes.keySet().iterator(); iterator.hasNext();) {
>                       String s = (String)iterator.next();
>                       int number =  
> ((DConfigBean[])childTypes.get(s)).length;
>                       out.println(indent+"+ "+s+" ("+number+"  
> entr"+(number == 1 ? "y" : "ies")+")");
>                   }
>               }
>               childProps =  
> getChildProperties(info.getPropertyDescriptors());
>               for(int i = 0; i < childProps.length; i++) {
>                   PropertyDescriptor prop = childProps[i];
>                   if(prop instanceof IndexedPropertyDescriptor) {
>                       int number =  
> ((Object[])prop.getReadMethod().invoke(bean, new Object[0])).length;
>                       out.println(indent+"+ "+prop.getDisplayName()+"  
> ("+number+" entr"+(number == 1 ? "y" : "ies")+")");
>                   } else {
>                       out.println(indent+"+ "+prop.getDisplayName()+"  
> (child property)");
>                   }
>               }
>               out.println();
>
>               // Load properties todo: handle properties of type bean  
> but not DConfigBean and indexed properties
>               count = 0;
>               properties =  
> getNormalProperties(info.getPropertyDescriptors());
>               readOnly = getReadOnly(info.getPropertyDescriptors());
>               for(int i = 0; i < readOnly.length; i++) {
>                   PropertyDescriptor property = readOnly[i];
>                   out.println(property.getDisplayName()+":  
> "+property.getReadMethod().invoke(bean, new Object[0]));
>               }
>               if(properties.length > 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().getComp 
> onentType(), 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<choice.length(); i++) {
>               if(!Character.isDigit(choice.charAt(i))) {
>                   return false;
>               }
>           }
>           return choice.length() > 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().getDispla 
> yName()+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
>    * <http://www.apache.org/>.
>    *
>    *  
> ====================================================================
>    */
>   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.DeploymentManagerCreationExcepti 
> on;
>   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.GeronimoD 
> eploymentFactory");
>           } 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 <tt>true</tt> 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 <tt>true</tt> 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<xpaths.length; i++) {
>               DDBean[] ddbs = dcb.getDDBean().getChildBean(xpaths[i]);
>               for(int j = 0; j < ddbs.length; j++) {
>                   initializeDConfigBean(dcb.getDConfigBean(ddbs[j]));
>               }
>           }
>       }
>
>       /**
>        * Hands over control to {@link DConfigBeanConfigurator} to let  
> the user edit
>        * the server-specific deployment information.
>        */
>       private void editServerSpecificDD() {
>           new DConfigBeanConfigurator(jarInfo.ejbJarConfig, out,  
> in).configure();
>       }
>
>       /**
>        * Holds all the relevent data for an EJB JAR.
>        */
>       private static class EjbJarInfo {
>           public File file;
>           public JarFile jarFile;
>           public DDBeanRoot ejbJar;
>           public DConfigBeanRoot ejbJarConfig;
>           public DDBeanRoot webServices;
>           public DConfigBeanRoot webServicesConfig;
>           public boolean editingEjbJar;
>       }
>   }
>
>
>
>