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 [EMAIL PROTECTED]
   *
   * 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 [EMAIL PROTECTED]
   *
   * 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 [EMAIL PROTECTED] 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;
      }
  }
  
  
  

Reply via email to