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;
}
}