001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.pageflow.internal;
020:
021: import java.beans.beancontext.BeanContext;
022: import java.lang.reflect.Field;
023: import java.lang.reflect.Modifier;
024: import java.util.Map;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.HashSet;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpServletResponse;
030: import javax.servlet.ServletContext;
031:
032: import org.apache.beehive.controls.api.bean.Controls;
033: import org.apache.beehive.controls.api.bean.ControlBean;
034: import org.apache.beehive.controls.api.bean.Control;
035: import org.apache.beehive.controls.api.context.ControlBeanContext;
036: import org.apache.beehive.controls.api.properties.AnnotatedElementMap;
037: import org.apache.beehive.netui.pageflow.ControlFieldInitializationException;
038: import org.apache.beehive.netui.pageflow.PageFlowManagedObject;
039: import org.apache.beehive.netui.pageflow.PageFlowUtils;
040: import org.apache.beehive.netui.pageflow.PageFlowControlContainerFactory;
041: import org.apache.beehive.netui.pageflow.PageFlowControlContainer;
042: import org.apache.beehive.netui.util.internal.concurrent.InternalConcurrentHashMap;
043: import org.apache.beehive.netui.util.logging.Logger;
044:
045: /**
046: * This class provides utilities for integrating Page Flow and Beehive's Controls technology. In order
047: * to use the two together, a Page Flow must act as a <em>Control container</em> and implement the
048: * Control lifecycle contract.
049: */
050: public class JavaControlUtils {
051: private static final Logger LOG = Logger
052: .getInstance(JavaControlUtils.class);
053: private static final String CONTROL_ANNOTATION_CLASSNAME = Control.class
054: .getName();
055:
056: /** Map of control-container-class (e.g., PageFlowController) to Map of fields/control-properties. */
057: private static InternalConcurrentHashMap/*< String, Map< Field, PropertyMap > >*/_controlFieldCache = new InternalConcurrentHashMap/*< String, Map< Field, PropertyMap > >*/();
058:
059: /**
060: * Initialize all null member variables that are Java Controls.
061: *
062: * @param request the current HttpServletRequest
063: * @param response the current HttpServletRequest
064: * @param servletContext the ServletContext
065: * @param controlClient the class with possible annotated Control fields
066: * @throws org.apache.beehive.netui.pageflow.ControlFieldInitializationException
067: */
068: public static void initJavaControls(HttpServletRequest request,
069: HttpServletResponse response,
070: ServletContext servletContext,
071: PageFlowManagedObject controlClient)
072: throws ControlFieldInitializationException {
073: Class controlClientClass = controlClient.getClass();
074:
075: //
076: // First, just return if there are no annotated Control fields. This saves us from having to catch a
077: // (wrapped) ClassNotFoundException for the control client initializer if we were to simply call
078: // Controls.initializeClient().
079: //
080: Map controlFields = getAccessibleControlFieldAnnotations(
081: controlClientClass, servletContext);
082: if (controlFields.isEmpty()) {
083: if (LOG.isTraceEnabled())
084: LOG
085: .trace("No control field annotations were found for "
086: + controlClient);
087: PageFlowControlContainer pfcc = PageFlowControlContainerFactory
088: .getControlContainer(request, servletContext);
089: pfcc.beginContextOnPageFlow(controlClient, request,
090: response, servletContext);
091: return;
092: }
093:
094: request = PageFlowUtils.unwrapMultipart(request);
095:
096: PageFlowControlContainer pfcc = PageFlowControlContainerFactory
097: .getControlContainer(request, servletContext);
098: pfcc.createAndBeginControlBeanContext(controlClient, request,
099: response, servletContext);
100: ControlBeanContext beanContext = pfcc
101: .getControlContainerContext(controlClient);
102: assert beanContext != null : "ControlBeanContext was not initialized by PageFlowRequestProcessor";
103:
104: try {
105: if (LOG.isDebugEnabled())
106: LOG.debug("Initializing control client "
107: + controlClient);
108: Controls.initializeClient(null, controlClient, beanContext);
109: } catch (Exception e) {
110: LOG.error("Exception occurred while initializing controls",
111: e);
112: throw new ControlFieldInitializationException(
113: controlClientClass.getName(), controlClient, e);
114: }
115: }
116:
117: /**
118: * Clean up all member variables that are Java Controls.
119: */
120: public static void uninitJavaControls(
121: ServletContext servletContext,
122: PageFlowManagedObject controlClient) {
123: Map controlFields = getAccessibleControlFieldAnnotations(
124: controlClient.getClass(), servletContext);
125:
126: for (Iterator i = controlFields.keySet().iterator(); i
127: .hasNext();) {
128: Field controlField = (Field) i.next();
129:
130: try {
131: Object fieldValue = controlField.get(controlClient);
132:
133: if (fieldValue != null) {
134: controlField.set(controlClient, null);
135: destroyControl(fieldValue);
136: }
137: } catch (IllegalAccessException e) {
138: LOG.error(
139: "Exception while uninitializing Java Control "
140: + controlField.getName(), e);
141: }
142: }
143: }
144:
145: /**
146: * Destroy a Beehive Control.
147: *
148: * @param controlInstance the Control instance
149: */
150: private static void destroyControl(Object controlInstance) {
151: assert controlInstance instanceof ControlBean : controlInstance
152: .getClass().getName();
153: BeanContext beanContext = ((ControlBean) controlInstance)
154: .getBeanContext();
155: if (beanContext != null) {
156: if (LOG.isTraceEnabled())
157: LOG.trace("Removing control " + controlInstance
158: + " from ControlBeanContext " + beanContext);
159: beanContext.remove(controlInstance);
160: }
161: }
162:
163: /**
164: * @return a map of Field (accessible) -> AnnotationMap
165: */
166: private static Map getAccessibleControlFieldAnnotations(
167: Class controlClientClass, ServletContext servletContext) {
168: String className = controlClientClass.getName();
169:
170: //
171: // Reading the annotations is expensive. See if there's a cached copy of the map.
172: //
173: Map/*< Field, PropertyMap >*/cached = (Map) _controlFieldCache
174: .get(className);
175:
176: if (cached != null)
177: return cached;
178:
179: HashMap/*< Field, PropertyMap >*/ret = new HashMap/*< Field, PropertyMap >*/();
180:
181: // Note that the annnotation reader doesn't change per-class. Inherited annotated elements are all read.
182: AnnotationReader annReader = AnnotationReader
183: .getAnnotationReader(controlClientClass, servletContext);
184:
185: //
186: // Go through this class and all superclasses, looking for control fields. Make sure that a superclass control
187: // field never replaces a subclass control field (this is what the 'fieldNames' HashSet is for).
188: //
189: HashSet fieldNames = new HashSet();
190: do {
191: Field[] fields = controlClientClass.getDeclaredFields();
192:
193: for (int i = 0; i < fields.length; i++) {
194: Field field = fields[i];
195: String fieldName = field.getName();
196: int modifiers = field.getModifiers();
197:
198: if (!fieldNames.contains(fieldName)
199: && !Modifier.isStatic(modifiers)
200: && annReader.getAnnotation(field.getName(),
201: CONTROL_ANNOTATION_CLASSNAME) != null) {
202: if (!Modifier.isPublic(field.getModifiers()))
203: field.setAccessible(true);
204: ret.put(field, new AnnotatedElementMap(field));
205: fieldNames.add(fieldName);
206: if (LOG.isDebugEnabled())
207: LOG.debug("Found control field " + fieldName
208: + " in control client class "
209: + controlClientClass.getName());
210: }
211: }
212:
213: controlClientClass = controlClientClass.getSuperclass();
214: } while (controlClientClass != null);
215:
216: _controlFieldCache.put(className, ret);
217: return ret;
218: }
219: }
|