First, as to the last question, I haven't quite solved the problem of
automatically getting back yet. I'm working on saving a mapping, and
then retrieving it later to get back the URI, but haven't quite got the
right combination of servlet methods together yet ;-(.

What was the registry I'm now calling a ResourceCache (since Sun is
already using "Registry"). The cache itself is just a hashtable. 

public class ResourceCache extends Hashtable {
}

When the Actor logs in, I insert a ResourceCache into their session
under a known key

// -- Instantiate cache
session.setAttribute(Constants.RESOURCE_CACHE_KEY, new ResourceCache());

To help manage the cache, I setup some static convenience methods 

public final class SessionCache {

    /**
     * Store an ActionForm in the cache under it's mapping form name.
     *
     * @param mapping The ActionMapping used to select this instance
     * @param actionForm The ActionForm bean for this request
     * @param request The HTTP request we are processing
     */
    public static void putForm(ActionMapping mapping,
             ActionForm form,
             HttpServletRequest request) {
        // Acquire cache
        HttpSession session = request.getSession();
        ResourceCache cache = (ResourceCache)
                session.getAttribute(Constants.RESOURCE_CACHE_KEY);
        if (cache!=null) {
            // Store form under mapping name
            // servlet.log("cache: Saving Form",2);
            // Tempted to clone this first, but shouldn't be necessary.
            cache.put(mapping.getName(),form);
        }
    }

    public static void remove(HttpServletRequest request, String key) {
        // Acquire cache
        HttpSession session = request.getSession();
        ResourceCache cache = (ResourceCache)
                session.getAttribute(Constants.RESOURCE_CACHE_KEY);
         if (cache!=null) cache.remove(key);
    }

    public static void setString(HttpServletRequest request, String key,
            String value) {
        if ((key!=null) && (value!=null)) {
            // Acquire cache
            HttpSession session = request.getSession();
            ResourceCache cache = (ResourceCache)
                    session.getAttribute(Constants.RESOURCE_CACHE_KEY);
             if (cache!=null) cache.put(new String(key),new
String(value));
        }
    }
}

On forms I want to interrupt and complete later, I have a special Cancel
button. The Action checks for this and saves the form (the "browse"
buttons are labeled "X").

if ((browse!=null) && ("X".equals(browse.trim()))) {
    SessionCache.putForm(mapping,form,request);
    return mapping.findForward("browse");
}

Usually, you want to browse for some foreign key(s), so when an Action
accesses a key that may be used as a foreign key by another Action (or,
table), it caches the key

// Remove or update script key
if ("delete".equals(task))
   SessionCache.remove(request,"script");
else
    SessionCache.setString(request,"script",thisForm.getScript());

On ActionForms that use foreign keys (that may be cached), I setup a
CacheableForm interface

    /**
     * Reset only selected (key) fields in ActionForm, for use after
     * removing ActionForm from a resource.
     */
    public void resetKeys(ActionMapping mapping, HttpServletRequest
request);

    /**
     * Return map of properties.
     *
     * @return map of properties.
     */
    public Map toMap() throws java.lang.IllegalAccessError;

    /**
     * Set properties from given map.
     */
    public void set(Map map) throws java.lang.IllegalAccessException;

}

---

The resetKeys method retrieves cached foreign keys when available (this
form uses three): 

    /**
     * Set key fields from cached values, when available.
     *
     * @param mapping The mapping used to select this instance
     * @param request The servlet request we are processing
     */
    public void resetKeys(ActionMapping mapping, HttpServletRequest
request) {
        HttpSession session = request.getSession();
        ResourceCache cache = (ResourceCache)
            session.getAttribute(Constants.RESOURCE_CACHE_KEY);
        this.donor = (String) cache.get("donor");
        this.script = (String) cache.get("script");
        this.category = (String) cache.get("category");
    }

This is used with the toMap and set(Map) methods are used by another
Session method to repoulate the current form bean from the cached form
bean

    /**
     * Remove a cached ActionForm, if it exists, and populate the given
     * form.
     *
     * Reports an "unexpected" error if BeanUtil generates an exception.
     *
     * @param mapping The ActionMapping used to select this instance
     * @param actionForm The ActionForm bean for this request
     * @param request The HTTP request we are processing
     * @param errors The ActionErrors resource
     */
    public static void populateForm(ActionMapping mapping,
                     ActionForm form,
                     HttpServletRequest request,
             ActionErrors errors) {
        // Acquire cache
        HttpSession session = request.getSession();
        ResourceCache cache = (ResourceCache)
                session.getAttribute(Constants.RESOURCE_CACHE_KEY);
        if (cache!=null) {
            // Check for form
            String formName = mapping.getName();
            if (cache.containsKey(formName)) {
                // servlet.log("ActionAdmin: Restoring Form from
Cache",2);
                CacheableForm thisForm = (CacheableForm) form;
                CacheableForm cachedForm = (CacheableForm)
cache.remove(formName);
                try {
                    // BeanUtils.populate(form,cachedForm.toMap());
                    thisForm.set(cachedForm.toMap());
                    thisForm.resetKeys(mapping,request);
                }
                catch (IllegalAccessException iae) {
                    errors.add(ActionErrors.GLOBAL_ERROR,
                        new ActionError("action.error.unexpected"));
                }
            }
        }
    }


The next result is that I can exit a complex form, wander around looking
up items it needs, and when I go back through "ADD" again, the keys I
looked up are automagically filled in. When going through a workflow,
this process also automatically saves keys from prior forms and
automatically inserts them into later (related) forms, with no
additional programming.

I've also started using toMap and set(Map) as standard data-transfer
agents for moving properties between ActionForms and internal objects,
which seems to be working rather well from a design standpoint.

I'm almost finished with my "Stepping Out" article, which includes all
this, along with sample forms.

HTH.

-- Ted Husted, Husted dot Com, Fairport NY USA.
-- Custom Software ~ Technical Services.
-- Tel 716 737-3463.
-- http://www.husted.com/about/struts/

Reply via email to