alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

/*

How to use this class to display ConfigBeans next to your deployment
descriptor fragments:

1.  Create the basebean
2.  Decide where you're going to potentially show sheets and customizers.
3.  Implement ModuleSupportCallback (minimal implementation fine for now).
4.  Create a ModuleDeploymentSupport instance.
5.  Get the standardDDBean back from the MDS.
6.  Query each of your plugins for their ConfigBeans.
7.  Give the ConfigBeans back to your MDS.
8.  When you want to show property sheets or components from the plugin,
    call add{sheet,customizer}listener.
9.  Receive the {Sheets,Customizers} and display them/remove them when
    pertinent
10. When ModuleSupportCallback.beanModified() is called, activate the
    save cookie on your DataObject.

 */


package org.netbeans.modules.j2ee.deployment.config;

import javax.enterprise.deploy.spi.*;
import javax.enterprise.deploy.spi.exceptions.*;
import javax.enterprise.deploy.model.*;
import javax.enterprise.deploy.shared.*;
import org.netbeans.modules.j2ee.deployment.plugins.api.*;
import org.netbeans.modules.j2ee.deployment.devmodules.api.J2eeModule;
import org.netbeans.modules.j2ee.deployment.impl.Server;
import org.netbeans.modules.schema2beans.*;
import org.openide.nodes.*;
import org.openide.*;
//import org.openide.util.WeakListener;

import java.util.*;

import java.beans.*;

public class ModuleDeploymentSupport implements PropertyChangeListener {

    public static final String SEPARATOR = "/"; //NOI18N
    final DeployableObjectImpl obj;
    Map rootMap = new HashMap(5); // DD location string -> DDRoot
    Map configMap = new IdentityHashMap(5); // DD object -> ConfigBeanStorage
    Map beanMap = Collections.synchronizedMap(new IdentityHashMap()); // BaseBean -> StandardDDImpl
    Map leafMap = Collections.synchronizedMap(new IdentityHashMap()); // BaseProp -> StandardDDImpl
    Set xpathListeners = new HashSet();
    PropertyChangeListener weakListener;
    J2eeModule provider;

    static Map moduleDDlocationMap = new HashMap(10); {
        moduleDDlocationMap.put(J2eeModule.EAR,
        new String[] {J2eeModule.APP_XML});
        moduleDDlocationMap.put(J2eeModule.WAR,
        new String[] {J2eeModule.WEB_XML,J2eeModule.WEBSERVICES_XML});
        moduleDDlocationMap.put(J2eeModule.EJB,
        new String[] {J2eeModule.EJBJAR_XML,J2eeModule.EJBSERVICES_XML});
        moduleDDlocationMap.put(J2eeModule.CONN,
        new String[] { J2eeModule.CONNECTOR_XML});
        moduleDDlocationMap.put(J2eeModule.CLIENT,
        new String[] { J2eeModule.CLIENT_XML});
    }

    public ModuleDeploymentSupport(J2eeModule provider) {
        this.provider = provider;
        obj = new DeployableObjectImpl(this);
        String[] ddLocs = (String[]) moduleDDlocationMap.get(provider.getModuleType());
        for(int i = 0; i < ddLocs.length; i++) {
            createRoot(ddLocs[i]);
        }
    }

    private void createRoot(String ddLoc) {
        BaseBean bean = provider.getDeploymentDescriptor(ddLoc);
        if (bean == null) // no support for that descriptor
            return;
        while(!bean.isRoot()) bean = bean.parent();
        DDRoot root = new DDRoot(new DDNodeBean(null,bean,this));
        rootMap.put(ddLoc,root);
        beanMap.put(bean,root);
//        weakListener = WeakListener.propertyChange(this,root.proxy.bean);
        root.proxy.bean.addPropertyChangeListener(/*weakListener*/ this);
    }

    public DeployableObjectImpl getDeployableObject() {
        return obj;
    }

    // This is broken in jsr88 that I even have to supply this
    public DDRoot getDDBeanRoot() {
        String loc = ((String[]) moduleDDlocationMap.get(provider.getModuleType()))[0];
        return getDDBeanRoot(loc);
    }

    // PENDING no wsdl support yet
    public DDRoot getDDBeanRoot(String loc) {
        return (DDRoot) rootMap.get(loc);
    }

    public ModuleType getType() {
        return (ModuleType) provider.getModuleType();
    }

    public String getVersion() {
        return provider.getModuleVersion();
    }

    /* PENDING get from CompilationUnit */
    public Class getClassFromScope(String cls) {
        return null; // provider.getClassFromScope(cls);
    }

    public Node[] getNodes() {
        String[] ddLocs = (String[]) moduleDDlocationMap.get(provider.getModuleType());
        Node[] ret = new Node[ddLocs.length];
        for(int i = 0; i < ddLocs.length; i++) {
            Object dd = rootMap.get(ddLocs[i]);
            if(dd == null) continue;
            ConfigBeanStorage cbs = (ConfigBeanStorage) configMap.get(dd);
            if(cbs == null) continue;
            ret[i] = cbs.getNode();
        }
        return ret;
    }

    public void createConfigs(DeploymentConfiguration config, Server server, ConfigurationStorage storage) throws ConfigurationException {
        String[] ddLocs = (String[]) moduleDDlocationMap.get(provider.getModuleType());
        DDRoot root = (DDRoot) rootMap.get(ddLocs[0]);
        DConfigBeanRoot cbroot = config.getDConfigBeanRoot(root);
        ConfigBeanStorage cbs = new ConfigBeanStorage(cbroot, null, storage);
        configMap.put(root,cbs);

        for(Iterator it = rootMap.keySet().iterator(); it.hasNext() ;) {
            String ddLoc = (String) it.next();
            if (isPrimaryDD(ddLoc, provider.getModuleType()))
                continue;
            root = (DDRoot) rootMap.get(ddLoc);
            DConfigBean cb = cbroot.getDConfigBean(root);
            if(cb == null) continue;
            ConfigBeanStorage cbStorage = new ConfigBeanStorage(cb, null, storage);
            configMap.put(root,cbs);
        }
    }

    static public boolean isPrimaryDD(String ddLocation, Object type) {
        String[] ddLocs = (String[]) moduleDDlocationMap.get(type);
        if (ddLocs.length < 1)
            return false;
        return ddLocs[0].equals(ddLocation);
    }
    
    /* Called when the module/app is closed from the ide, clean up listeners and
     * references */
    public void cleanup() {
        rootMap = null; configMap = null; beanMap = null; xpathListeners = null;
    }

    /* Called when the module is removed from the app. */
    public void dispose(DeploymentConfiguration config) {
        for(Iterator it = configMap.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry entry = (Map.Entry) it.next();
            DDRoot root = (DDRoot) entry.getKey();
            ConfigBeanStorage cbs = (ConfigBeanStorage) entry.getValue();
            root.proxy.bean.removePropertyChangeListener(weakListener);
            try {
                config.removeDConfigBean((DConfigBeanRoot)cbs.bean);
            } catch (BeanNotFoundException bnfe) {
                // IGNORE
            }
        }
    }

    StandardDDImpl getBean(BaseBean bean) {
        //     System.out.println("Getting bean for " + bean);
        //     System.out.println(bean.fullName());
        //     System.out.println(bean.dtdName());
        if (bean == null) return null;
        StandardDDImpl ret = (StandardDDImpl) beanMap.get(bean);
        
        if (ret == null) {
            /*
            DDCommon base;
            //            System.out.println("Creating new bean");
            BaseBean bb = bean;
            while(!bb.isRoot()) {
                bb = bb.parent();
                if (bb== null) {
                    // We are in an unattached tree, we have expressed no prior
                    // interest in this Xpath so we just toss it.
                    // See: addTemporaryBean in this object
                    return null;
                }

            }
            if(bb == root.proxy.bean) base = new DDNodeBean(bean,this);
            else { // must build proxy tree
                if (bean.isRoot()) {
                    // PENDING This probably means that there is an error, can it legaly happen?
                    throw new IllegalStateException("Found a bean rooted in a tree not previously registered with Module Deployment Support. Bean = : " + bean + "@" +  Integer.toHexString(bean.hashCode())); //NO I18N
                }
                StandardDDImpl parent = getBean(bean.parent());
                base = new DDProxy(parent.proxy,bean,bean.dtdName(),this);
            }
             **/
            ret = new StandardDDImpl(new DDNodeBean(bean,this));
            beanMap.put(bean,ret);
        }
        return ret;
    }

    // for indexed leaf properties
    StandardDDImpl getBean(BaseProperty prop,int index) {

        if(index < 0) return getBean(prop);

        if (!leafMap.containsKey(prop)) {
            leafMap.put(prop, new StandardDDImpl[index + 1]);
        } else if (((StandardDDImpl[])leafMap.get(prop)).length <= index) {
            StandardDDImpl[] a = (StandardDDImpl[])leafMap.get(prop);
            StandardDDImpl[] b = new StandardDDImpl[index + 1];

            leafMap.put(prop, b);
            for (int i = 0; i < a.length; i++) {
                b[i] = a[i];
            }
        }

        StandardDDImpl[] arr = (StandardDDImpl[])leafMap.get(prop);
        StandardDDImpl elem = arr[index];

        if (elem == null) {
            elem = new StandardDDImpl(new DDLeafBean(prop, index, this));
            arr[index] = elem;
        }

        return elem;
     }

    // for non-indexed leaf properties
    StandardDDImpl getBean(BaseProperty prop) {
       StandardDDImpl elem = (StandardDDImpl) leafMap.get(prop);
       if(elem == null) {
          elem = new StandardDDImpl(new DDLeafBean(prop, this));
          leafMap.put(prop,elem);
       }
       return elem;
    }

    StandardDDImpl getBean(String name) {
        return getBean(name,getDDBeanRoot().proxy.bean);
    }

    StandardDDImpl getBean(String name,BaseBean rootBean) {
        Bean parent = GraphManager.getPropertyParent(rootBean, name);
        String shortName = GraphManager.getPropertyName(name);
        int index = GraphManager.getPropertyIndex(rootBean, name);
        //        System.out.println(name);
        //        System.out.println(index);

        BaseProperty prop = parent.getProperty(shortName);

        if(index < 0 && prop.isIndexed()) index = 0;

        StandardDDImpl ret;
        if(prop.isBean()) {
            if(prop.isIndexed())
                ret = getBean((BaseBean) parent.getValue(shortName,index));
            else
                ret = getBean((BaseBean) parent.getValue(shortName));
        }
        else {
            if(prop.isIndexed())
                ret = getBean(prop,index);
            else
                ret = getBean(prop);
        }
        //        System.out.println(ret.proxy.bean.fullName());
        //        System.out.println(((Object)ret.proxy.bean).toString());
        return ret;
    }

    void addXpathListener(DDCommon bean, String xpath, XpathListener listen) {
        xpathListeners.add(new XpathListenerStorage(bean,xpath,listen));
    }

    void removeXpathListener(DDCommon bean, String xpath, XpathListener listen) {
        xpathListeners.remove(new XpathListenerStorage(bean,xpath,listen));
    }

    /* functional spec for processing the PropertyChangeEvents:
     *
     * Ways in which listeners are added:
     * 1.  Customizer/Sheet Listeners
     * 2.  ConfigBean getChildBean()
     * 3.  ConfigBean associated Bean.
     * 4.  Xpath listeners (in all situations just fire XpathEvent)
     *
     * Types of Events:
     * 1.  Bean added
     * 2.  Bean removed
     * 3.  Bean changed
     *
     * 4.  Plugin added
     * 5.  Plugin removed
     * 6.  Listener added
     *
     * Location of Event:
     * 1.  Current bean
     * 2.  Descendant bean
     * 3.  Ancestor bean (removal only)
     *
     * Other event type:
     *
     * Case-by-case breakdown:
     *
     * Location of Event: Current Bean
     *
     *               Added              Removed              Changed
     *
     *  Listener:    N/A            remove listener           N/A
     *  getChild():  if matches,
     *          Call ConfigBean.getChild()
     *          Add it to parent property
     *          sheet if necessary.
     *                                 N/A                   N/A
     *  Bean:             fire notifyStandardDDBean changed.
     *
     * Location of Event: Descendant Bean
     *
     *               Added              Removed              Changed
     *
     *  Listener:                 N/A
     *  getChild():  see above           N/A                   N/A
     *  Bean:              fire notifyStandardDDBean changed
     *
     * Location of Event: Ancestor Bean
     *
     *               Added              Removed              Changed
     *
     *  Listener:                        N/A
     *  getChild():  check match         N/A                  N/A
     *  Bean:                         if removed is self,
     *                                call removeChildBean()
     *                                on parent
     *
     *  Other events:
     *            ListenerAdded       PluginAdded            PluginRemoved
     *  Listener:      N/A        Calculate display for new     remove display
     *                             plugin
     *  getChild(): Calculate display   Call all getChild()   remove listeners
     *              for associated      methods applicable
     *              ConfigBean
     *  bean:       as above              as above            remove listeners
     */

    public void propertyChange(PropertyChangeEvent event) {

        Object oldValue = event.getOldValue();
        //        System.out.println("Old value" + oldValue);
        Object newValue = event.getNewValue();
        //        System.out.println("New value" + newValue);
        String name = event.getPropertyName();

        //        System.out.println("Processing ddbeans event " + name);
        //        System.out.println("From source " + event.getSource());
        //        System.out.println(event.getSource().getClass());

        try {
            StandardDDImpl eventBean;
            if(newValue == null && oldValue instanceof BaseBean) {
                eventBean = getBean((BaseBean) oldValue);
            }
            else {
                eventBean = getBean(name);
            }

            //  this is the case where an array assignment is made
            //  too change a whole set of properties, and to make
            //  sense of the events.  Our UI only generates these
            //  array assignments if the oldvalue is non-null.
            if (eventBean == null && oldValue instanceof Object[]) {
                // process separate propertyChange events for each
                // array element.
                List newElements = new ArrayList();
                if(newValue != null)
                    newElements.add(Arrays.asList((Object[])newValue));
                Object[] values = (Object[]) oldValue;
                for(int i = 0; i < values.length; i++) {
                    Object value = values[i];
                    // PENDING tracking indicies of non-BaseBean
                    // properties does not work.
                    if(!(value instanceof BaseBean)) break;
                    // no change in this element
                    if(newElements.contains(value)) {
                        newElements.remove(value);
                        continue;
                    }
                    StandardDDImpl valueBean = getBean((BaseBean)value);
                    // I still don't know anything about this bean.
                    if(valueBean == null) continue;
                    // this element has been removed.
                    processEvent(value,null,valueBean.proxy,event);
                }
                for(Iterator i = newElements.iterator();i.hasNext();) {
                    i.next();
                    // PENDING ignore for now - these should have already
                    // generated events for adds?
                }
            }

            // swallow events we know nothing about.
            if (eventBean == null) return;

            if(oldValue == null && eventBean.proxy.isProxy()) {
                eventBean.setProxy(new DDNodeBean((DDProxy)eventBean.proxy));
                return; // swallow this event
            }

            processEvent(oldValue,newValue,eventBean.proxy,event);

        } catch (Exception e) {
            ErrorManager.getDefault().notify(e);
        }
    }

    void processEvent(Object oldValue, Object newValue, DDCommon eventBean, PropertyChangeEvent event) {

        //        System.out.println("Processing event on " + eventBean);


        // Start with just XpathEvents.
        // 0.  Make the StandardDDBean for the Event, get its Xpath.
        // 1.  Iterate through all the listeners.  // optimize lookup later
        // 2.  for listener l
        // 3.  Make the listener's xpath.
        // 4.  If (3) is related to source, continue.
        // 5.  Find the real BB + xpath for the listener
        // 6.  Check the ancestry relationship between the Event's BB
        //     and the Listener's BB
        // 7.  If share ancestry, Construct XpathEvent, fire.

        // PENDING should get from source + property

        String eventDtdPath = eventBean.getXpath();

        Object type = XpathEvent.BEAN_CHANGED;
        if(oldValue == null) type = XpathEvent.BEAN_ADDED;
        if(newValue == null) type = XpathEvent.BEAN_REMOVED;
        XpathEvent xe = new XpathEvent(eventBean.container,type);
        xe.setChangeEvent(event);


        Object xpathListenerArray[] = xpathListeners.toArray();
        for (int i = 0; i < xpathListenerArray.length; i++) {
            XpathListenerStorage x = (XpathListenerStorage) xpathListenerArray[i];

            String xp = x.getNormalizedPath();

            //            System.out.println("Checking against listener " + xp);

            //PENDING - handle delete events on completely different code path?
            // need to get this code working for DDBean ancestry traversal.
            DDCommon leftBean,rightBean;
            if(eventDtdPath.startsWith(xp)) {
                //               System.out.println("Event dtd is smaller");
                leftBean = x.bean;
                rightBean = eventBean;
            } else if(xp.startsWith(eventDtdPath)) {
                //               System.out.println("Event dtd is bigger");
                leftBean = eventBean;
                rightBean = x.bean;
            } else continue;
            while (leftBean != rightBean && rightBean != null)
                rightBean = rightBean.parent;
            if(leftBean == rightBean) {
                x.listen.fireXpathEvent(xe);
            }
        }
        // should look through DDBeans we know about and check for
        // relative listeners that way.  This perhaps means we pop
        // up the event bean ancestor list and just look up the DDBean
        // directly to process events.
        eventBean.fireEvent(xe);
        // PENDING remove should remove the DDBean and any children from the cache.

    }

    private class XpathListenerStorage {
        private DDCommon bean = null;
        private String xpath;
        private boolean xpathRelative;
        private XpathListener listen;
        private String normal = null;

        XpathListenerStorage(DDCommon bean,String xpath,XpathListener listen) {
            this.bean = bean; 
            this.xpath = xpath; 
            this.listen = listen;
            xpathRelative = ! xpath.startsWith(SEPARATOR);
        }

        public String getNormalizedPath() {
            if(normal == null) {
                String base = xpath;
                if (xpathRelative) {
                    base = bean.getXpath() + SEPARATOR + base;
                }
                normal = normalizePath(base);
            }
            return normal;
        }

        public String toString() {
            return bean + " " + xpath + " " + listen;
        }

        public int hashCode() { return listen.hashCode(); }

        public boolean equals(Object o) {
            if(o instanceof XpathListenerStorage) {
                XpathListenerStorage x = (XpathListenerStorage) o;
                return (x.bean == bean) && (x.xpath == xpath) && (x.listen == listen);
            }
            return false;
        }

    }

    static String normalizePath(String path) {
        boolean absolute = path.startsWith(SEPARATOR);
        StringTokenizer tokens = new StringTokenizer(path, SEPARATOR, false);

        LinkedList l = new LinkedList();

        while(tokens.hasMoreElements())
            l.addLast(tokens.nextElement());

        for(int i = 0 ; i < l.size(); ) {
            String tok = (String) l.get(i);
            if(tok.equals(".")) l.remove(i);
            else if(tok.equals("..") && i > 0 && !l.get(i-1).equals("..")) {
                l.remove(i);
                l.remove(i-1);
                i--;
            } else i++;
        }

        StringBuffer ret = new StringBuffer();

        for(int i = 0; i < l.size(); i++) {
            if(absolute || i > 0) ret.append(SEPARATOR);
            ret.append(l.get(i));
        }

        return ret.toString();

    }

}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.