0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.netbeans.modules.j2ee.sun.share.configbean;
0042:
0043: import java.util.ArrayList;
0044: import java.util.Collection;
0045: import java.util.Collections;
0046: import java.util.HashMap;
0047: import java.util.Iterator;
0048: import java.util.LinkedHashSet;
0049: import java.util.List;
0050: import java.util.Map;
0051: import java.util.ResourceBundle;
0052: import java.util.Set;
0053: import java.util.logging.Level;
0054:
0055: import java.text.MessageFormat;
0056:
0057: import java.beans.PropertyChangeEvent;
0058: import java.beans.PropertyChangeListener;
0059: import java.beans.PropertyChangeSupport;
0060: import java.beans.VetoableChangeListener;
0061: import java.beans.VetoableChangeSupport;
0062:
0063: import javax.enterprise.deploy.spi.DConfigBean;
0064: import javax.enterprise.deploy.model.DDBean;
0065: import javax.enterprise.deploy.model.XpathEvent;
0066: import javax.enterprise.deploy.model.XpathListener;
0067: import javax.enterprise.deploy.spi.exceptions.BeanNotFoundException;
0068: import javax.enterprise.deploy.spi.exceptions.ConfigurationException;
0069: import org.netbeans.modules.j2ee.deployment.devmodules.api.J2eeModule;
0070: import org.netbeans.modules.j2ee.sun.dd.api.ASDDVersion;
0071:
0072: import org.netbeans.modules.j2ee.sun.dd.api.CommonDDBean;
0073: import org.netbeans.modules.j2ee.sun.dd.api.common.WebserviceEndpoint;
0074: import org.netbeans.modules.j2ee.sun.dd.api.web.SunWebApp;
0075:
0076: import org.netbeans.modules.j2ee.sun.share.Constants;
0077: import org.netbeans.modules.j2ee.sun.share.configbean.customizers.common.ValidationSupport;
0078: import org.openide.ErrorManager;
0079:
0080: /** This is the base class for all DConfigBean objects in the SunONE App Server
0081: * JSR88 implementation.
0082: *
0083: * @author Vince Kraemer
0084: * @author Peter Williams
0085: */
0086: public abstract class Base implements Constants, DConfigBean,
0087: XpathListener, DConfigBeanUIFactory {
0088:
0089: /** Resource bundle
0090: */
0091: protected final ResourceBundle bundle = ResourceBundle
0092: .getBundle("org.netbeans.modules.j2ee.sun.share.configbean.Bundle"); // NOI18N
0093:
0094: /** Property event names
0095: */
0096: public static final String DISPLAY_NAME = "displayName"; // NOI18N
0097: public static final String DIRTY_PROPERTY = "dirty"; // NOI18N
0098:
0099: /** Singleton object used as generic old value in property events to force
0100: * them to be sent. (if the new value is "" or null, then using "" or null
0101: * for the old value is problematic - when new & old values match, the event
0102: * is not fired.
0103: */
0104: public static final Object GenericOldValue = new Object();
0105:
0106: private DDBean dDBean;
0107: private Base parent;
0108: private String baseXpath;
0109: private String sunBaseXpath;
0110:
0111: private J2eeModule module;
0112:
0113: /** Name of descriptor element this bean represents, e.g. sun-web-app, servlet-ref, etc. */
0114: protected String descriptorElement;
0115:
0116: /** isValid represents the valid state of the bean:
0117: * null: unknown
0118: * TRUE: bean is valid
0119: * FALSE: bean has at least one invalid field.
0120: */
0121: private Boolean isValid = null;
0122:
0123: /** Validation message database for this bean.
0124: */
0125: private ErrorMessageDB errorMessageDB = null;
0126:
0127: /** Utility field used by bound properties. */
0128: private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
0129: this );
0130:
0131: /** Utility field used by constrained properties. */
0132: private VetoableChangeSupport vetoableChangeSupport = new VetoableChangeSupport(
0133: this );
0134:
0135: private PropertyChangeListener validationListener = new PropertyChangeListener() {
0136: public void propertyChange(PropertyChangeEvent evt) {
0137: if (ErrorMessageDB.VALIDATION_STATE_CHANGED.equals(evt
0138: .getPropertyName())) {
0139: validationStateChanged((Boolean) evt.getNewValue());
0140: }
0141: }
0142: };
0143:
0144: /** identity property to aid in debugging. Displays absolute ID of bean in
0145: * system.
0146: */
0147: private static int identitySource = 0;
0148: private String identity;
0149:
0150: public String getIdentity() {
0151: return identity;
0152: }
0153:
0154: public void setIdentity(String id) {
0155: }
0156:
0157: /** Quickly set this bean to dirty so that Studio will add a SaveCookie
0158: * for us.
0159: */
0160: private int dirtyFlag;
0161:
0162: public void setDirty() {
0163: int oldDirtyFlag = dirtyFlag;
0164: dirtyFlag += 1;
0165: getPCS().firePropertyChange(DIRTY_PROPERTY, oldDirtyFlag,
0166: dirtyFlag);
0167: }
0168:
0169: /** For use by the customizers when they modify a sub property of an element.
0170: */
0171: public void firePropertyChange(String propertyName,
0172: Object oldProperty, Object newProperty) {
0173: getPCS().firePropertyChange(propertyName, oldProperty,
0174: newProperty);
0175: }
0176:
0177: /** Creates a new instance of Base */
0178: protected Base() {
0179: identity = Integer.toString(++identitySource);
0180: }
0181:
0182: /** Since we create DConfigBeans via default constructors, this is the real
0183: * initialization method. Override this method if you need to do extra
0184: * initialization in a derived class but make absolutely sure your first line
0185: * is 'super.init(dDBean, parent)'!!!
0186: *
0187: * @param dDBean DDBean that this DConfigBean is bound to
0188: * @param parent DConfigBean that is the parent of this bean. Will be null
0189: * if this is a DConfigBeanRoot, otherwise, should have a value.
0190: * @throws ConfigurationException
0191: */
0192: protected void init(DDBean dDBean, Base parent)
0193: throws ConfigurationException {
0194: this .dDBean = dDBean;
0195: this .parent = parent;
0196: this .baseXpath = dDBean.getXpath();
0197: this .sunBaseXpath = translateXpath(this .baseXpath);
0198:
0199: // Build validation field list for this bean
0200: // !PW We need a better way to do this. See comment by validationFieldList
0201: // member definition.
0202: updateValidationFieldList();
0203:
0204: dDBean.addXpathListener(dDBean.getXpath(), this );
0205: getMessageDB().addPropertyChangeListener(validationListener);
0206: }
0207:
0208: void init(J2eeModule mod, Base object) {
0209: this .module = mod;
0210: this .parent = parent;
0211: //this.baseXpath = getXpath(mod);
0212: // Build validation field list for this bean
0213: // !PW We need a better way to do this. See comment by validationFieldList
0214: // member definition.
0215: updateValidationFieldList();
0216:
0217: //dDBean.addXpathListener(dDBean.getXpath(), this);
0218: getMessageDB().addPropertyChangeListener(validationListener);
0219: }
0220:
0221: /** Cleanup routine. This is called just before a DConfigBean is removed
0222: * from the tree (and all caches).
0223: */
0224: protected void cleanup() {
0225: // remove listeners
0226: getMessageDB().removePropertyChangeListener(validationListener);
0227: dDBean.removeXpathListener(dDBean.getXpath(), this );
0228:
0229: // clear errorMessageDB
0230: synchronized (this ) {
0231: errorMessageDB = null;
0232: }
0233:
0234: // remove from DConfigBean tree
0235: if (parent != null) {
0236: parent.removeChild(this );
0237: }
0238:
0239: dDBean = null;
0240: parent = null;
0241: }
0242:
0243: protected String getDescriptorElement() {
0244: return descriptorElement;
0245: }
0246:
0247: protected void setDescriptorElement(String element) {
0248: descriptorElement = element;
0249: }
0250:
0251: protected String getComponentName() {
0252: return null;
0253: }
0254:
0255: protected String getAbsoluteXpath(String field) {
0256: // JVM will optimize this to use StringBuffer/StringBuilder
0257: return sunBaseXpath + "/" + field;
0258: }
0259:
0260: protected String translateXpath(String ddXpath) {
0261: return ddXpath;
0262: }
0263:
0264: /* Does this class of DConfigBeans require JNDI names in general. Right now
0265: * this means J2EE 1.3, 1.4 = Yes, JavaEE5 = No.
0266: */
0267: protected boolean requiresJndiName() {
0268: return J2EEVersion.J2EE_1_4
0269: .compareSpecification(getJ2EEModuleVersion()) >= 0;
0270: }
0271:
0272: /** Named child bean data cache to prevent loss of data when named beans haven't
0273: * been loaded yet and a save request comes in.
0274: */
0275: private Map namedBeanCache = new HashMap(11);
0276:
0277: protected void saveNamedBeans(String type, String nameProperty,
0278: CommonDDBean[] data) {
0279: if (data != null && data.length > 0) {
0280: Map dataMap = new HashMap(data.length * 3);
0281: for (int i = 0; i < data.length; i++) {
0282: String beanName = (String) data[i]
0283: .getValue(nameProperty);
0284: if (Utils.notEmpty(beanName)) {
0285: dataMap.put(beanName, data[i]);
0286: }
0287: }
0288:
0289: namedBeanCache.put(type, dataMap);
0290: }
0291: }
0292:
0293: protected CommonDDBean removeNamedBean(String type, String beanName) {
0294: CommonDDBean result = null;
0295: Map dataMap = getNamedBeanMap(type);
0296: if (dataMap != null) {
0297: result = (CommonDDBean) dataMap.remove(beanName);
0298: }
0299:
0300: return result;
0301: }
0302:
0303: protected CommonDDBean removeCachedEndpoint(String hostType,
0304: String hostNameType, String hostName, String endpointType,
0305: String portComponentName) {
0306: // 1. get cache of host type
0307: // 2. iterate cache for hostname
0308: // 3. if found, remove endpoint(s) for portcomponentname from host entry
0309: CommonDDBean removedEndpoint = null;
0310: Map dataMap = getNamedBeanMap(hostType);
0311: if (dataMap != null) {
0312: try {
0313: Iterator entryIter = dataMap.entrySet().iterator();
0314: while (entryIter.hasNext()) {
0315: Map.Entry entry = (Map.Entry) entryIter.next();
0316: CommonDDBean host = (CommonDDBean) entry.getValue();
0317: Object n = host.getValue(hostNameType);
0318: if (n instanceof String
0319: && hostName.equals((String) n)) {
0320: Object[] objs = host.getValues(endpointType);
0321: if (objs != null) {
0322: for (int i = 0; i < objs.length; i++) {
0323: if (objs[i] instanceof WebserviceEndpoint) {
0324: WebserviceEndpoint endpoint = (WebserviceEndpoint) objs[i];
0325: if (portComponentName
0326: .equals(endpoint
0327: .getPortComponentName())) {
0328: host.removeValue(endpointType,
0329: endpoint);
0330: removedEndpoint = endpoint;
0331: }
0332: }
0333: }
0334: }
0335: }
0336: }
0337: } catch (IllegalArgumentException ex) {
0338: // programmer bug if this happens.
0339: ErrorManager.getDefault().notify(
0340: ErrorManager.INFORMATIONAL, ex);
0341: }
0342: }
0343: return removedEndpoint;
0344: }
0345:
0346: protected Map getNamedBeanMap(String type) {
0347: return (Map) namedBeanCache.get(type);
0348: }
0349:
0350: protected void saveAllNamedBeans(CommonDDBean parentBean) {
0351: Iterator iter = getNamedBeanSpecs().iterator();
0352: while (iter.hasNext()) {
0353: NamedBean beanSpec = (NamedBean) iter.next();
0354: try {
0355: Object data = parentBean.getValues(beanSpec.getType());
0356: if (data instanceof CommonDDBean[]) {
0357: saveNamedBeans(beanSpec.getType(), beanSpec
0358: .getPropertyName(), (CommonDDBean[]) data);
0359: } else if (data != null) {
0360: // System.out.println("saveAllNamedBeans: unexpected datatype - " + data.getClass().getSimpleName());
0361: }
0362: } catch (Exception ex) {
0363: // if property does not exist, we'll get a runtime exception from schema2beans.
0364: // System.out.println("saveAllNamedBeans: " + ex.getLocalizedMessage());
0365: }
0366: }
0367: }
0368:
0369: protected void restoreAllNamedBeans(CommonDDBean parentBean,
0370: String version) {
0371: Iterator iter = getNamedBeanSpecs().iterator();
0372: while (iter.hasNext()) {
0373: NamedBean beanSpec = (NamedBean) iter.next();
0374: Map beanMap = getNamedBeanMap(beanSpec.getType());
0375: restoreNamedBeans(beanMap, beanSpec.getType(), parentBean,
0376: version);
0377: }
0378: }
0379:
0380: protected void restoreNamedBeans(Map beanMap,
0381: String parentPropertyName, CommonDDBean parentBean,
0382: String version) {
0383: if (beanMap != null && beanMap.size() > 0) {
0384: for (Iterator beanIter = beanMap.entrySet().iterator(); beanIter
0385: .hasNext();) {
0386: try {
0387: Map.Entry entry = (Map.Entry) beanIter.next();
0388: CommonDDBean bean = (CommonDDBean) entry.getValue();
0389: parentBean.addValue(parentPropertyName, bean
0390: .cloneVersion(version));
0391: } catch (Exception ex) {
0392: // if property does not exist, ignore the runtime exception from schema2beans.
0393: // System.out.println("restoreNamedBeans: " + ex.getLocalizedMessage());
0394: }
0395: }
0396: }
0397: }
0398:
0399: protected void updateNamedBeanCache(String type) {
0400: if (parent != null) {
0401: String name = getComponentName();
0402: if (Utils.notEmpty(name)) {
0403: parent.removeNamedBean(type, name);
0404: }
0405: }
0406: }
0407:
0408: protected Collection getNamedBeanSpecs() {
0409: return Collections.EMPTY_LIST;
0410: }
0411:
0412: private static Collection commonAppBeanSpecs = new ArrayList();
0413:
0414: static {
0415: commonAppBeanSpecs
0416: .add(new NamedBean(
0417: SunWebApp.EJB_REF,
0418: org.netbeans.modules.j2ee.sun.dd.api.common.EjbRef.EJB_REF_NAME));
0419: commonAppBeanSpecs
0420: .add(new NamedBean(
0421: SunWebApp.MESSAGE_DESTINATION_REF,
0422: org.netbeans.modules.j2ee.sun.dd.api.common.MessageDestinationRef.MESSAGE_DESTINATION_REF_NAME));
0423: commonAppBeanSpecs
0424: .add(new NamedBean(
0425: SunWebApp.RESOURCE_ENV_REF,
0426: org.netbeans.modules.j2ee.sun.dd.api.common.ResourceEnvRef.RESOURCE_ENV_REF_NAME));
0427: commonAppBeanSpecs
0428: .add(new NamedBean(
0429: SunWebApp.RESOURCE_REF,
0430: org.netbeans.modules.j2ee.sun.dd.api.common.ResourceRef.RES_REF_NAME));
0431: commonAppBeanSpecs
0432: .add(new NamedBean(
0433: SunWebApp.SERVICE_REF,
0434: org.netbeans.modules.j2ee.sun.dd.api.common.ServiceRef.SERVICE_REF_NAME));
0435: }
0436:
0437: protected static Collection getCommonNamedBeanSpecs() {
0438: return commonAppBeanSpecs;
0439: }
0440:
0441: protected static class NamedBean {
0442: private final String type;
0443: private final String propertyName;
0444:
0445: public NamedBean(final String t, final String pn) {
0446: type = t;
0447: propertyName = pn;
0448: }
0449:
0450: public String getType() {
0451: return type;
0452: }
0453:
0454: public String getPropertyName() {
0455: return propertyName;
0456: }
0457: }
0458:
0459: /** -----------------------------------------------------------------------
0460: * Validation implementation
0461: */
0462:
0463: /** Global access to Rajeshwar's validation manager. This is only used for EJB
0464: * validations but is here so it can be shared between EjbJarRoot and BaseEJb +
0465: * derivatives.
0466: *
0467: * Web, App Clients, and EAR's use a different rule manager.
0468: */
0469: protected static ValidationSupport validationSupport = new ValidationSupport();
0470:
0471: /** Message database, one per DConfigBean.
0472: */
0473: protected final synchronized ErrorMessageDB getMessageDB() {
0474: if (errorMessageDB == null) {
0475: errorMessageDB = ErrorMessageDB.createMessageDB();
0476: }
0477: return errorMessageDB;
0478: }
0479:
0480: /** !PW This member is interesting. It stores the list of fieldId's that this
0481: * bean can validate. The list is built as the various derived classes add
0482: * the fields they control to the list (see ejb's which are quite multi-tiered.)
0483: * In that sense, two objects of the same type (2 ejb-ref's, 2 servlets, etc.)
0484: * have the same list and should be able to use the same list. However, this
0485: * field cannot be static because then it would be shared by all classes,
0486: * irrespective of type. For now, it will be unique per bean, but a better
0487: * way is probably some registry where beans of the same type can share the
0488: * same list.
0489: */
0490: protected List validationFieldList = new ArrayList();
0491:
0492: /** override this method (and call overridden version via super) to add
0493: * fields to the validation field id list.
0494: */
0495: protected void updateValidationFieldList() {
0496: }
0497:
0498: public void validationStateChanged(Boolean newState) {
0499: isValid = newState;
0500: getPCS().firePropertyChange(DISPLAY_NAME, "", getDisplayName());
0501: }
0502:
0503: /** Returns previous result of validateFields() or invokes method if status is
0504: * out of date.
0505: *
0506: * @return true if valid, false otherwise.
0507: */
0508: public boolean isValid() {
0509: if (isValid == null) {
0510: boolean tempValid = validateFields(true);
0511: isValid = Boolean.valueOf(tempValid);
0512: }
0513:
0514: return isValid.booleanValue();
0515: }
0516:
0517: /** Validate the fields managed by this bean. Used by the customizers
0518: * (and possibly incremental deployment.)
0519: *
0520: * @return true or false as to whether bean is valid or not.
0521: */
0522: public boolean validateFields(boolean shortCircuit) {
0523: ErrorMessageDB messageDB = getMessageDB();
0524: boolean result = true;
0525:
0526: messageDB.clearErrors();
0527: for (Iterator iter = validationFieldList.iterator(); iter
0528: .hasNext()
0529: && (result || !shortCircuit);) {
0530: boolean fieldResult = validateField((String) iter.next());
0531: result = result && fieldResult;
0532: }
0533:
0534: isValid = Boolean.valueOf(result);
0535:
0536: return result;
0537: }
0538:
0539: /** Validate a single field managed by this bean. Used by the customizers
0540: * (and possibly incremental deployment.)
0541: *
0542: * @param field Field spec (xpath to this field in DTD, should be defined
0543: * constant in bean class.)
0544: * @return true or false as to whether field is valid or not.
0545: */
0546: public boolean validateField(String fieldId) {
0547: return true;
0548: }
0549:
0550: /** -----------------------------------------------------------------------
0551: * Implementation of XpathListener interface
0552: */
0553: public void fireXpathEvent(XpathEvent xpe) {
0554: // dumpNotification("fireXpathEvent", xpe);
0555: }
0556:
0557: /* ------------------------------------------------------------------------
0558: * Version retrieval methods
0559: */
0560: public J2EEBaseVersion getJ2EEModuleVersion() {
0561: Base parent = getParent();
0562: if (parent != null) {
0563: return getParent().getJ2EEModuleVersion();
0564: } else {
0565: ErrorManager.getDefault().notify(
0566: ErrorManager.INFORMATIONAL,
0567: new IllegalStateException(
0568: "getJ2EEModuleVersion() called on child DConfigBean with null parent: "
0569: + this ));
0570: }
0571: return null;
0572: }
0573:
0574: public ASDDVersion getAppServerVersion() {
0575: Base parent = getParent();
0576: if (parent != null) {
0577: return getParent().getAppServerVersion();
0578: } else {
0579: ErrorManager.getDefault().notify(
0580: ErrorManager.INFORMATIONAL,
0581: new IllegalStateException(
0582: "getAppServerVersion() called on child DConfigBean with null parent: "
0583: + this ));
0584: }
0585: return null;
0586: }
0587:
0588: /* ------------------------------------------------------------------------
0589: * Child bean finder methods
0590: */
0591: protected DDBean getNameDD(String nameXpath)
0592: throws ConfigurationException {
0593: DDBean nameDD = null;
0594:
0595: DDBean[] beans = getDDBean().getChildBean(nameXpath);
0596: if (beans.length == 1) {
0597: // Found the DDBean we want.
0598: nameDD = beans[0];
0599: } else {
0600: Object[] args = new Object[2];
0601: args[0] = getDDBean().getXpath();
0602: args[1] = nameXpath;
0603:
0604: if (beans.length > 1) {
0605: throw Utils.makeCE(
0606: "ERR_DDBeanHasDuplicateRequiredXpaths", args,
0607: null); // NOI18N
0608: } else {
0609: throw Utils.makeCE("ERR_DDBeanMissingRequiredXpath",
0610: args, null); // NOI18N
0611: }
0612: }
0613:
0614: return nameDD;
0615: }
0616:
0617: protected void validateDDBean(DDBean ddBean)
0618: throws ConfigurationException {
0619: // DDBean cannot be null
0620: if (ddBean == null) {
0621: throw Utils.makeCE("ERR_DDBeanIsNull", null, null); // NOI18N
0622: }
0623:
0624: // DDBean's xpath cannot be null
0625: if (ddBean.getXpath() == null) {
0626: throw Utils.makeCE("ERR_DDBeanHasNullXpath", null, null); // NOI18N
0627: }
0628:
0629: // Note: DDBean's text field can be empty (and so presumably can be null).
0630: }
0631:
0632: /* ------------------------------------------------------------------------
0633: * DConfigBean interface methods
0634: */
0635: /** Returns the beans that hold configuration data for particular
0636: * subelements of the application.xml
0637: * @param dDBean DDBean representing an xpath for which we want to attach a DConfigBean.
0638: * @throws ConfigurationException if there is an error creating the sub-bean
0639: * @return The DConfigBean that holds extended configuration data.
0640: */
0641: public DConfigBean getDConfigBean(DDBean dDBean)
0642: throws javax.enterprise.deploy.spi.exceptions.ConfigurationException {
0643: try {
0644: jsr88Logger.entering(Base.class.toString(),
0645: "getDConfigBean", dDBean);
0646:
0647: validateDDBean(dDBean);
0648: Base dcbResult = getDCBInstance(dDBean);
0649:
0650: // !PW If we get a result from the cache, we should verify that the bean
0651: // return is the correct type for the DDBean passed in, in case someone
0652: // is trying to reuse DDBean objects (ala Vince in his test case!)
0653:
0654: if (dcbResult == null) {
0655: dcbResult = getDCBFactoryMgr().createDCB(dDBean, this );
0656:
0657: if (dcbResult != null) {
0658: putDCBInstance(dcbResult);
0659: addChild(dcbResult);
0660:
0661: System.out.println("New DCB for "
0662: + dDBean.getXpath());
0663:
0664: // Lastly, if this bean is a member of a group, return the head
0665: // of the group to the caller.
0666: //
0667: Base groupHead = dcbResult.getDCBHead();
0668: if (groupHead != null) {
0669: dcbResult = groupHead;
0670: }
0671:
0672: // !PW FIXME bug workaround IZ 41214
0673: beanAdded(dcbResult.getDDBean().getXpath());
0674: }
0675: }
0676:
0677: return dcbResult;
0678: } catch (java.lang.AssertionError ex) {
0679: ConfigurationException ce = new ConfigurationException();
0680: ce.initCause(ex);
0681: throw ce;
0682: } catch (RuntimeException ex) {
0683: throw Utils.makeCE("ERR_UnknownConfigException", null, ex); // NOI18N
0684: }
0685: }
0686:
0687: /** !PW FIXME Workaround for broken XpathEvent.BEAN_ADDED not being sent.
0688: * Override this method (see WebAppRoot) to be notified if a child bean
0689: * is created. See IZ 41214
0690: */
0691: protected void beanAdded(String xpath) {
0692: }
0693:
0694: /** !PW FIXME Workaround for broken XpathEvent.BEAN_REMOVED not being sent.
0695: * Override this method (see WebAppRoot) to be notified if a child bean
0696: * is destroyed. See IZ 41214
0697: */
0698: protected void beanRemoved(String xpath) {
0699: }
0700:
0701: /**
0702: * @return
0703: */
0704: public DDBean getDDBean() {
0705: return this .dDBean;
0706: }
0707:
0708: public J2eeModule getModule() {
0709: return module;
0710: }
0711:
0712: /** Xpaths that this bean extends. Each DConfigBean that has children will
0713: * provide an array of xpaths (which, by the way, are happen to be the keys
0714: * in the factory mapping for those children).
0715: * @return The array of xpaths that are interesting to this DConfigBean instance
0716: */
0717: public String[] getXpaths() {
0718: return getDCBFactoryMgr().getFactoryKeys();
0719: }
0720:
0721: /** The DDBean (or one of it's children) that this DConfigBean is bound to
0722: * has changed.
0723: *
0724: * @param xpathEvent
0725: */
0726: public void notifyDDChange(XpathEvent xpathEvent) {
0727: // dumpNotification("notifyDDChange", xpathEvent);
0728: }
0729:
0730: // protected void dumpNotification(String fnName, XpathEvent xpathEvent) {
0731: // String type;
0732: //
0733: // if(xpathEvent.isAddEvent()) {
0734: // type = "BEAN_ADD";
0735: // } else if(xpathEvent.isRemoveEvent()) {
0736: // type = "BEAN_REMOVE";
0737: // } else if(xpathEvent.isChangeEvent()) {
0738: // type = "BEAN_CHANGE";
0739: // } else {
0740: // type = "UNKNOWN TYPE";
0741: // }
0742: //
0743: // System.out.println(fnName + ": XPATHEVENT: " + type +
0744: // ", DCB identity = " + getIdentity() +
0745: // ", DCB type = " + getClass().getName() +
0746: // ", xpath = " + xpathEvent.getBean().getXpath() +
0747: // ", DCB xpath = " + getDDBean().getXpath());
0748: // }
0749:
0750: /** JSR-88: Removes a child DConfigBean from this bean.
0751: * Spec interpretation note: If the dConfigBean parameter refers to a bean
0752: * that has it's own children, those children are also removed, ad infinitum.
0753: *
0754: * @param dConfigBean The child DConfigBean to remove from this DConfigBean
0755: * @throws BeanNotFoundException
0756: */
0757: public void removeDConfigBean(DConfigBean dConfigBean)
0758: throws BeanNotFoundException {
0759: if (dConfigBean != null) {
0760: if (dConfigBean.getDDBean() != null) {
0761: if (((Base) dConfigBean).getParent() == this ) {
0762: // Handle children first.
0763:
0764: // Can't use iterator here or we will possibly get a ConcurrentModificationException
0765: // as the children clean themselves up.
0766: Base beanToRemove = (Base) dConfigBean;
0767: Object children[] = beanToRemove.getChildren()
0768: .toArray();
0769: for (int i = 0; i < children.length; i++) {
0770: try {
0771: beanToRemove
0772: .removeDConfigBean((Base) children[i]);
0773: } catch (BeanNotFoundException ex) {
0774: // This would suggest a corrupt tree or bad code somewhere if it happens.
0775: // Catch & log it and continue cleaning the tree.
0776: ErrorManager.getDefault().notify(
0777: ErrorManager.INFORMATIONAL, ex);
0778: }
0779: }
0780:
0781: DDBean key = dConfigBean.getDDBean();
0782: beanToRemove = removeDCBInstance(key);
0783:
0784: if (beanToRemove != null) {
0785: if (beanToRemove instanceof BaseRoot) {
0786: // remove from root cache as well.
0787: BaseRoot rootBean = (BaseRoot) getConfig()
0788: .getDCBRootCache().remove(key);
0789:
0790: if (rootBean != null) {
0791: assert (rootBean == beanToRemove); // these should be the same, right?
0792: }
0793: } else if (beanToRemove instanceof BaseModuleRef) {
0794: // Clean up patch list - the patch list should be empty, but you never know...
0795: getConfig().getPatchList().remove(key);
0796: }
0797: }
0798:
0799: if (beanToRemove != null) {
0800: // !PW FIXME 1st half - workaround for IZ 41214 (see method comment)
0801: String beanXpath = beanToRemove.getDDBean()
0802: .getXpath();
0803:
0804: System.out.println("DCB removed for "
0805: + beanToRemove.getDDBean().getXpath());
0806:
0807: // cleanup bean before throwing away
0808: beanToRemove.cleanup();
0809: beanToRemove = null;
0810:
0811: // !PW FIXME 2nd half - workaround for IZ 41214 (see method comment)
0812: beanRemoved(beanXpath);
0813: } else {
0814: Object[] args = new Object[2];
0815: args[0] = dConfigBean.getDDBean();
0816: args[1] = key.getXpath();
0817: throw new BeanNotFoundException(
0818: MessageFormat
0819: .format(
0820: bundle
0821: .getString("ERR_DConfigBeanNotFoundOnRemove"),
0822: args));
0823: }
0824: } else {
0825: // The parent of the DConfigBean parameter is not this instance - spec violation.
0826: throw new BeanNotFoundException(
0827: bundle
0828: .getString("ERR_DConfigBeanWrongParentOnRemove"));
0829: }
0830: } else {
0831: // DDBean is null. This could be that this DConfigBean has
0832: // previously been removed.
0833: throw new BeanNotFoundException(
0834: bundle
0835: .getString("ERR_DConfigBeanNotFoundOnRemoveNullDDBean"));
0836: }
0837: } else {
0838: // DConfigBean is null
0839: throw new BeanNotFoundException(
0840: bundle
0841: .getString("ERR_DConfigBeanNotFoundOnRemoveNullDConfigBean"));
0842: }
0843: }
0844:
0845: /**
0846: * @param pCL
0847: */
0848: public void addPropertyChangeListener(PropertyChangeListener pCL) {
0849: propertyChangeSupport.addPropertyChangeListener(pCL);
0850: }
0851:
0852: /**
0853: * @param pCL
0854: */
0855: public void removePropertyChangeListener(PropertyChangeListener pCL) {
0856: propertyChangeSupport.removePropertyChangeListener(pCL);
0857: }
0858:
0859: /**
0860: * @return
0861: */
0862: protected PropertyChangeSupport getPCS() {
0863: return propertyChangeSupport;
0864: }
0865:
0866: /**
0867: * @return
0868: */
0869: protected VetoableChangeSupport getVCS() {
0870: return vetoableChangeSupport;
0871: }
0872:
0873: /** Adds a VetoableChangeListener to the listener list.
0874: * @param l The listener to add.
0875: *
0876: */
0877: public void addVetoableChangeListener(VetoableChangeListener l) {
0878: vetoableChangeSupport.addVetoableChangeListener(l);
0879: }
0880:
0881: /** Removes a VetoableChangeListener from the listener list.
0882: * @param l The listener to remove.
0883: *
0884: */
0885: public void removeVetoableChangeListener(VetoableChangeListener l) {
0886: vetoableChangeSupport.removeVetoableChangeListener(l);
0887: }
0888:
0889: /**
0890: * @return
0891: */
0892: public Base getParent() {
0893: return parent;
0894: }
0895:
0896: /** Retrieve the parser provided by the root DCB in this tree. Failing that,
0897: * return the parser provided by the master root DCB of the entire configuration.
0898: */
0899: protected ConfigParser getParser() {
0900: Base parent = getParent();
0901: if (parent != null) {
0902: return parent.getParser();
0903: }
0904:
0905: SunONEDeploymentConfiguration config = getConfig();
0906: if (config != null) {
0907: BaseRoot dcbRoot = config.getMasterDCBRoot();
0908: if (dcbRoot != null) {
0909: return dcbRoot.getParser();
0910: }
0911: }
0912:
0913: return null;
0914: }
0915:
0916: /** A DConfigBean may represent data that would go into multiple descriptor
0917: * files. A DConfigBean may also expose properties of a super bean. These
0918: * snippets are used to hold the schema2beans object and the name of the file
0919: * that the bean will be part of. They are merged in Base.addToGraphs()
0920: * to produce the deployment plan file.
0921: *
0922: * @return a collection of snippet objects for this bean. Null is not allowed.
0923: */
0924: abstract Collection getSnippets();
0925:
0926: /** Loads the values of the DConfigBean properties from the deployment plan file
0927: * that this bean's DeploymentConfiguration parent read. This method should be
0928: * called by init. It is also called in the restore methods on
0929: * DeploymentConfiguration.
0930: *
0931: * @param config The SunONEDeploymentConfig object that read in the deployment plan file
0932: * @return true if a bean was found and loaded, false otherwise.
0933: */
0934: abstract boolean loadFromPlanFile(
0935: SunONEDeploymentConfiguration config);
0936:
0937: /** This method operates recursively to perform a depth first search of the
0938: * DConfigBean hierarchy, creating the corresponding schema2beans graph as
0939: * it travels. Pieces of the graph can be merged as children of the tracking
0940: * parent, or into other parts of a root found in the graph, or finally, into
0941: * whole new roots that are added to the final map.
0942: *
0943: * @param map A map of file names to schema2beans object graphs
0944: * @param bbCurrent The current tracking parent basebean
0945: * @parem bbKey The map lookup key that matches the current tracking bean passed in
0946: */
0947: public void addToGraphs(Map map, CommonDDBean bbCurrent,
0948: String bbKey) {
0949: jsr88Logger.entering(this .getClass().toString(), "addToGraphs"); // NOI18N
0950:
0951: String uriText = getUriText();
0952: Collection snippets = getSnippets();
0953:
0954: boolean isFirst = true;
0955: CommonDDBean newCurrentBean = null;
0956: String newSnippetKey = "";
0957:
0958: Iterator iter = snippets.iterator();
0959: while (iter.hasNext()) {
0960: try {
0961: CommonDDBean bean = null;
0962: Snippet s = (Snippet) iter.next();
0963:
0964: if (s.hasDDSnippet()) {
0965: String snippetKey = Utils.getFQNKey(uriText, s
0966: .getFileName());
0967: if (snippetKey.compareTo(bbKey) == 0) {
0968: // merge with current basebean
0969: bean = s.mergeIntoRovingDD(bbCurrent);
0970: } else if (map.containsKey(snippetKey)) {
0971: // merge with root -- this option is unlikely to be used it means that the
0972: // current snippet IS represented in existing graph AND the current roving
0973: // bean is NOT in the same tree, so we must merge at root level with the root
0974: // we found in the graph.
0975: //
0976: // !PW This option is now used by WebAppCache, a javabean that represents the
0977: // cache portion of sun-web.xml and is owned/parented by WebAppRoot.
0978: //
0979: try {
0980: bean = s.mergeIntoRootDD((CommonDDBean) map
0981: .get(snippetKey));
0982: } catch (UnsupportedOperationException ex) {
0983: jsr88Logger
0984: .finest("Invalid Snippet: Snippet Class: "
0985: + s.getClass().getName());
0986: CommonDDBean parent = (CommonDDBean) map
0987: .get(snippetKey);
0988: jsr88Logger.finest("Parent Bean: "
0989: + ((parent != null) ? parent
0990: .getClass().getName()
0991: : "(null -- ack!)"));
0992: jsr88Logger.finest("Snippet Key: "
0993: + snippetKey);
0994: jsr88Logger
0995: .finest("Snippet Property Name: "
0996: + s.getPropertyName());
0997: throw ex;
0998: }
0999: } else {
1000: // create new basebean from root and add to graph
1001: bean = s.getDDSnippet();
1002:
1003: // !PW FIXME Cmp SNIPPET is temporarily returning null here.
1004: if (bean != null) {
1005: map.put(snippetKey, bean);
1006: }
1007: }
1008:
1009: if (isFirst) {
1010: // Save bean from first snippet for passing to children.
1011: newCurrentBean = bean;
1012: newSnippetKey = snippetKey;
1013: isFirst = false;
1014: }
1015: }
1016: } catch (Exception ex) {
1017: jsr88Logger
1018: .log(
1019: Level.SEVERE,
1020: "Base.newAddToGraph() -- exception processing bean",
1021: ex); // NOI18N
1022: ErrorManager.getDefault().notify(
1023: ErrorManager.INFORMATIONAL, ex);
1024: }
1025: }
1026:
1027: // Handle children before returning
1028: //
1029: Collection childList = getChildren();
1030: iter = childList.iterator();
1031: while (iter.hasNext()) {
1032: Base childDCB = (Base) iter.next();
1033: childDCB.addToGraphs(map, processParentBean(newCurrentBean,
1034: childDCB), newSnippetKey);
1035: }
1036:
1037: jsr88Logger.exiting(this .getClass().toString(), "addToGraphs"); // NOI18N
1038: }
1039:
1040: /** This method allows some customization of how parent basebeans are adjusted
1041: * when the depthfirst search in addToGraphs is passing a parent basebean
1042: * on to a child DConfigBean. In most cases, no adjustment is necessary.
1043: * See BaseEjb.processParentBean for a case where it is.
1044: */
1045: protected CommonDDBean processParentBean(CommonDDBean bean,
1046: DConfigBean child) {
1047: // Calculate what the parent S2B bean should be for this child and
1048: // return that.
1049: //
1050: // Basic implementation assumes no translation is necessary
1051: return bean;
1052: }
1053:
1054: /**
1055: * @return
1056: */
1057: Collection getChildren() {
1058: return children;
1059: }
1060:
1061: public String getUriText() {
1062: if (parent != null) {
1063: return parent.getUriText();
1064: }
1065:
1066: // This should never get executed actually, since this method is
1067: // overridden in BaseRoot. But just in case...
1068: return ""; // NOI18N
1069: }
1070:
1071: static private char XPATH_SEPCHAR = '/';
1072:
1073: /** Holds value of property xpath. */
1074: //private String xpath;
1075: /** hack to clean up the J2EE 1.4 RI beta 1 getText() value on a DDBean.
1076: * @param dDBean the bean that is having its text cleaned
1077: */
1078: static String cleanDDBeanText(DDBean dDBean) {
1079: String candidate = null;
1080:
1081: try {
1082: if (dDBean == null) {
1083: return candidate;
1084: }
1085:
1086: candidate = dDBean.getText();
1087: if (null == candidate || (candidate.length() == 0)) {
1088: return candidate;
1089: }
1090:
1091: if (!candidate.startsWith("<?xml")) { // NOI18N
1092: return candidate;
1093: }
1094:
1095: String xpath = dDBean.getXpath();
1096: if (null == xpath || (xpath.length() == 0)) {
1097: return candidate;
1098: }
1099:
1100: int lindex = xpath.lastIndexOf(XPATH_SEPCHAR);
1101: if (lindex > -1) {
1102: lindex += 1;
1103: String finalEl = xpath.substring(lindex);
1104: finalEl = "<" + finalEl + ">"; // NOI18N
1105: int elementPos = candidate.indexOf(finalEl);
1106: if (elementPos < 0) {
1107: return candidate;
1108: }
1109: String retVal = candidate.substring(elementPos
1110: + finalEl.length());
1111: if (retVal.length() < finalEl.length() + 1) {
1112: return retVal;
1113: }
1114: retVal = retVal.substring(0, retVal.length()
1115: - (finalEl.length() + 2));
1116: return retVal;
1117: }
1118: } catch (RuntimeException ex) {
1119: jsr88Logger.throwing("Base", "cleanDDBeanText", ex); // NOI18N
1120: }
1121:
1122: return candidate;
1123: }
1124:
1125: private DDBean secondary;
1126: private Set children = new LinkedHashSet();
1127:
1128: /**
1129: * @param newKid
1130: */
1131: protected void addChild(DConfigBean bean) {
1132: children.add(bean);
1133: }
1134:
1135: protected boolean removeChild(DConfigBean bean) {
1136: return children.remove(bean);
1137: }
1138:
1139: /**
1140: * @param secondary
1141: */
1142: void setSecondary(DDBean secondary) {
1143: this .secondary = secondary;
1144: }
1145:
1146: /**
1147: * @return
1148: */
1149: public SunONEDeploymentConfiguration getConfig() {
1150: if (null != parent) {
1151: return parent.getConfig();
1152: }
1153:
1154: return null;
1155: }
1156:
1157: /* ------------------------------------------------------------------------
1158: * Implementation of DConfigBeanUIFactory interface
1159: *
1160: * This interface allows DConfigBeanProperties object retrieval which is
1161: * primarily toallow UI customization beyond that provided by JSR-88 1.1 and
1162: * the Java Beans 1.01 specification.
1163: */
1164: /** Retrieve the DConfigBeanProperties object for this DConfigBean
1165: *
1166: * @param self Implementation artifact. Should be null or same as 'this'.
1167: * It is not used.
1168: * @return DConfigBeanProperties Object that provides the extra properties
1169: * needed for display.
1170: */
1171: public DConfigBeanProperties getUICustomization(DConfigBean self) {
1172: return new DConfigBeanProperties() {
1173: public String getDisplayName() {
1174: return Base.this .getDisplayName();
1175: }
1176:
1177: public String getHelpId() {
1178: return Base.this .getHelpId();
1179: }
1180: };
1181: }
1182:
1183: /** Getter for displayName property
1184: * @return String suitable for display
1185: */
1186: public String getDisplayName() {
1187: // This shows the name of the particular bean, if it has one. For example,
1188: // servlets, ejb's, modules inside EAR's, etc.
1189: //
1190: String name = getComponentName();
1191: Object[] args = new Object[1];
1192: args[0] = Utils.notEmpty(name) ? name : getDescriptorElement();
1193: String pattern = bundle
1194: .getString(isValid() ? "LBL_BeanDisplayName"
1195: : "LBL_BeanDisplayNameBroken");
1196: return MessageFormat.format(pattern, args);
1197: }
1198:
1199: /** Getter for helpId property. Override this method to provide the correct
1200: * help context id for any specific DConfigBean.
1201: *
1202: * @return Help context ID for this DConfigBean
1203: */
1204: abstract public String getHelpId();
1205:
1206: /* public String getHelpId() {
1207: // the default should be no help, not a debug message only targetted at us, the developer
1208: // of this plugin.
1209: //assert false : this.getClass().getName() + " does not override getHelpId!!!"; // NOI18N
1210: return "";
1211: }
1212: */
1213: /* ------------------------------------------------------------------------
1214: * DConfigBean caching support. Allows lookup of existing DCB's by their
1215: * associated DDBean as a key.
1216: */
1217:
1218: /**
1219: * @param base
1220: */
1221: protected void putDCBInstance(Base base) {
1222: DDBean key = base.getDDBean();
1223: if (key != null) {
1224: SunONEDeploymentConfiguration config = getConfig();
1225: if (config != null) {
1226: Map cache = config.getDCBCache();
1227:
1228: Object existingDCB = cache.get(key);
1229: if (existingDCB != null) {
1230: // jsr88Logger.finest("DCBCache: Replacing existing DCB '" + existingDCB + "' with '" + base + "'"); // NOI18N
1231: } else {
1232: // jsr88Logger.finest("DCBCache: Adding DCB to cache, ddbean key = '" + key.getXpath() + "'"); // NOI18N
1233: }
1234:
1235: cache.put(key, base);
1236: } else {
1237: // jsr88Logger.finest("DCBCache: Error: DCB '" + this + "' has null config so '" + base + "' cannot be cached"); // NOI18N
1238: }
1239: } else {
1240: // jsr88Logger.finest("DCBCache: Error: DCB '" + base + "' has null DDBean"); // NOI18N
1241: }
1242: }
1243:
1244: /**
1245: * @param key
1246: * @return
1247: */
1248: protected Base getDCBInstance(DDBean key) {
1249: // jsr88Logger.finest("DCBCache: Looking for DCB to match ddbean key '" + key.getXpath() + "'");
1250:
1251: Base result = null;
1252: SunONEDeploymentConfiguration config = getConfig();
1253:
1254: if (config != null) {
1255: Map cache = config.getDCBCache();
1256: Object o = cache.get(key);
1257:
1258: if (o != null) {
1259: if (o instanceof Base) {
1260: result = (Base) o;
1261: } else {
1262: // jsr88Logger.finest("DCBCache(get): Error: object matching DDBean key is wrong type: '" + o.getClass().getName() + "'"); // NOI18N
1263: }
1264: } else {
1265: // jsr88Logger.finest("DCBCache: No DCB match for key: '" + key.getXpath() + "'"); // NOI18N
1266: }
1267: } else {
1268: // jsr88Logger.finest("DCBCache(get): Error: DCB '" + this + "' has null config therefore no cache to search"); // NOI18N
1269: }
1270:
1271: return result;
1272: }
1273:
1274: /**
1275: * @param base
1276: * @return
1277: */
1278: protected Base removeDCBInstance(Base base) {
1279: return removeDCBInstance(base.getDDBean());
1280: }
1281:
1282: /**
1283: * @param key
1284: * @return
1285: */
1286: protected Base removeDCBInstance(DDBean key) {
1287: Base result = null;
1288: SunONEDeploymentConfiguration config = getConfig();
1289:
1290: if (config != null) {
1291: Map cache = config.getDCBCache();
1292: Object o = cache.remove(key);
1293:
1294: if (o != null) {
1295: if (o instanceof Base) {
1296: result = (Base) o;
1297: } else {
1298: // jsr88Logger.finest("DCBCache: Error: object matching DDBean key is wrong type: '" + o.getClass().getName() + "'"); // NOI18N
1299: }
1300: }
1301: } else {
1302: // jsr88Logger.finest("DCBCache(get): Error: DCB '" + this + "' has null config therefore no cache to search"); // NOI18N
1303: }
1304:
1305: return result;
1306: }
1307:
1308: /* ------------------------------------------------------------------------
1309: * Group child bean support. For any child beans that are stored as groups,
1310: * e.g. SecurityRoleMapping, ResourceEnvRef, etc., the parent owns the head
1311: * of the group. This support provides that storage, as well as a mechanism
1312: * for locating the head for a particular group (some parents have children
1313: * in more than one group, e.g. WarRoot has both ejbRef's and resRef's, both
1314: * of which are grouped.
1315: */
1316: /** ----------------------- Support used by parent DCB --------------------
1317: */
1318: /** dcbChildGroupMap is initally null because most beans will not even use
1319: * this system. Only DCB's that have linked groups of like child DCB's,
1320: * such as SecurityRoleMapping, ResRef, or EjbRef will use this capability.
1321: */
1322: private Map dcbChildGroupMap = null;
1323:
1324: /**
1325: * @param dDBean
1326: * @return
1327: */
1328: protected Base getDCBGroup(DDBean dDBean) {
1329: Base dcbResult = null;
1330:
1331: if (dcbChildGroupMap != null) {
1332: dcbResult = (Base) dcbChildGroupMap.get(dDBean.getXpath());
1333: }
1334:
1335: return dcbResult;
1336: }
1337:
1338: /**
1339: * @param dcb
1340: */
1341: protected void addDCBGroup(Base dcb) {
1342: if (dcbChildGroupMap == null) {
1343: dcbChildGroupMap = new HashMap(7);
1344: }
1345:
1346: if (getDCBGroup(dcb.getDDBean()) == null) {
1347: dcbChildGroupMap.put(dcb.getDDBean().getXpath(), dcb);
1348: }
1349: }
1350:
1351: /** ----------------------- Support used by child DCB --------------------
1352: */
1353: /** internal list of beans */
1354: private List groupDCBList = null;
1355: private Base dcbHead = null;
1356:
1357: /** initializes a member of a bean group, making the bean the head (or
1358: * adding this bean to an existing group in the specified parent).
1359: * @param dDBean used to get the xpath that allows finding an existing
1360: * group in the parent, if any.
1361: * @param parent the parent of this bean, where we look to see if there
1362: * is an existing group
1363: */
1364: protected void initGroup(DDBean dDBean, Base parent) {
1365: if (parent != null) {
1366: Base dcb = parent.getDCBGroup(dDBean);
1367: if (dcb != null) {
1368: /* Head has already been created -- this is an additional bean
1369: * of same type.
1370: */
1371: dcbHead = dcb;
1372: dcbHead.addDCBToGroup(this );
1373: } else {
1374: /* Head is null, this is the first bean of it's type in for
1375: * the given parent.
1376: */
1377: dcbHead = this ;
1378: addDCBToGroup(this );
1379:
1380: parent.addDCBGroup(this );
1381: }
1382: }
1383: }
1384:
1385: /** Adds beans to the list of like-grouped beans. Only the head bean in the
1386: * list will initialize and use this list. Other beans in the list will
1387: * have a reference to the head bean. If the head bean is null, the list
1388: * reference should also be null and that indicates this bean does not
1389: * support being grouped (though it may still be the parent of beans that
1390: * are grouped).
1391: * @param dcb DConfigBean to add to this group. Should only call this
1392: * method on the bean that is the head of a group (i.e. do not call it
1393: * on a member of a group that is not the head.)
1394: */
1395: private void addDCBToGroup(Base dcb) {
1396: if (groupDCBList == null) {
1397: groupDCBList = new ArrayList(10);
1398: }
1399:
1400: groupDCBList.add(dcb);
1401: }
1402:
1403: /** Retrieves the head bean of a bean group.
1404: * @return returns the head bean of bean group.
1405: */
1406: protected Base getDCBHead() {
1407: return dcbHead;
1408: }
1409:
1410: /* ------------------------------------------------------------------------
1411: * Xpath to Factory mapping support
1412: */
1413: private static final java.util.Map defaultXPathToFactory = new java.util.HashMap();
1414:
1415: /** Retrieve the XPathToFactory map for this DConfigBean. For Base, this is
1416: * the default map, which is empty.
1417: * @return
1418: */
1419: protected java.util.Map getXPathToFactoryMap() {
1420: return defaultXPathToFactory;
1421: }
1422:
1423: private DCBFactoryMgr factoryMgrInstance = null;
1424:
1425: /** Retrieve the factory manager for this DConfigBean. If one has not been
1426: * constructed yet, create it.
1427: * @return
1428: */
1429: DCBFactoryMgr getDCBFactoryMgr() {
1430: if (factoryMgrInstance == null) {
1431: factoryMgrInstance = new DCBFactoryMgr(
1432: getXPathToFactoryMap(), getDDBean().getXpath());
1433: }
1434:
1435: return factoryMgrInstance;
1436: }
1437:
1438: /* ------------------------------------------------------------------------
1439: * More persistence support
1440: */
1441:
1442: /** Determine which file this bean is likely to go into. This is based on
1443: * the bean's DDBean "buddy".
1444: */
1445: protected String constructFileName() {
1446: String ddXpath = baseXpath; // dDBean.getXpath();
1447: StringBuffer fname = new StringBuffer(32);
1448: fname.append("sun-"); // NOI18N
1449:
1450: if (null != ddXpath) {
1451: if (ddXpath.startsWith("/ejb-jar")) { // NOI18N
1452: fname.append("ejb-jar"); // NOI18N
1453: } else if (ddXpath.startsWith("/web-app")) { // NOI18N
1454: fname.append("web"); // NOI18N
1455: } else if (ddXpath.startsWith("/application")) { // NOI18N
1456: if (ddXpath.indexOf("client") > -1) { // NOI18N
1457: fname.append("application-client"); // NOI18N
1458: } else {
1459: fname.append("application"); // NOI18N
1460: }
1461: } else if (ddXpath.startsWith("/connector")) { // NOI18N
1462: fname.append("connector"); // NOI18N
1463: } else {
1464: String mess = MessageFormat.format(bundle
1465: .getString("ERR_InvalidXPathValueUsage"), // NOI18N
1466: new Object[] { ddXpath });
1467: throw new java.lang.IllegalStateException(mess);
1468: }
1469: } else {
1470: // this is bad
1471: throw new java.lang.IllegalStateException(
1472: "null Xpath value"); // FIXME
1473: }
1474:
1475: fname.append(".xml"); // NOI18N
1476: return fname.toString();
1477: }
1478:
1479: /** This is a basic snippet and will be the base class for most if not all
1480: * snippet objects in the DCB hierarchy. The methods most likely to need
1481: * overriding are getDefaultSnippet(), hasDDSnippet() if the snippet in
1482: * question could be optional in it's entirety, and getPropertyName() if
1483: * the default merge code is being used.
1484: *
1485: * If custom merge is required, see the interface documentation in Snippet
1486: * for specifics on all of these methods.
1487: */
1488: abstract class DefaultSnippet implements Snippet {
1489:
1490: public abstract CommonDDBean getDDSnippet();
1491:
1492: public org.netbeans.modules.schema2beans.BaseBean getCmpDDSnippet() {
1493: return null;
1494: }
1495:
1496: public boolean hasDDSnippet() {
1497: return true;
1498: }
1499:
1500: public String getFileName() {
1501: return constructFileName();
1502: }
1503:
1504: public CommonDDBean mergeIntoRootDD(CommonDDBean ddRoot) {
1505: throw new java.lang.UnsupportedOperationException();
1506: }
1507:
1508: public CommonDDBean mergeIntoRovingDD(CommonDDBean ddParent) {
1509: CommonDDBean newBean = getDDSnippet();
1510: if (newBean != null) {
1511: if (ddParent != null) {
1512: String propertyName = getPropertyName();
1513: if (propertyName != null) {
1514: ddParent.addValue(propertyName, newBean);
1515: } else {
1516: jsr88Logger.severe("No property name for "
1517: + Base.this .getClass()); // NOI18N
1518: }
1519: } else {
1520: jsr88Logger
1521: .severe("mergeIntoRovingDD() called with null parent (called on root bean?)"); // NOI18N
1522: }
1523: } else {
1524: jsr88Logger.severe("No snippet to merge for "
1525: + Base.this .getClass()); // NOI18N
1526: }
1527: return newBean;
1528: }
1529:
1530: public String getPropertyName() {
1531: return null;
1532: }
1533: }
1534:
1535: protected static class NameBasedFinder implements ConfigFinder {
1536: private String propertyName;
1537: private String propertyValue;
1538: private Class beanType;
1539:
1540: public NameBasedFinder(String propName, String propValue,
1541: Class type) {
1542: this .propertyName = propName;
1543: this .propertyValue = propValue;
1544: this .beanType = type;
1545: }
1546:
1547: public Object find(Object obj) {
1548: Object result = null;
1549: CommonDDBean root = (CommonDDBean) obj;
1550: String[] props = root.findPropertyValue(propertyName,
1551: propertyValue);
1552:
1553: for (int i = 0; i < props.length; i++) {
1554: CommonDDBean candidate = root
1555: .getPropertyParent(props[i]);
1556: if (beanType.isInstance(candidate)) {
1557: result = candidate;
1558: break;
1559: }
1560: }
1561:
1562: return result;
1563: }
1564: }
1565: }
|