001: /**
002: * Copyright 2006 Webmedia Group Ltd.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: **/package org.araneaframework.core;
016:
017: import java.util.ArrayList;
018: import java.util.Collections;
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.List;
022: import java.util.Map;
023: import org.apache.commons.collections.map.LinkedMap;
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026: import org.araneaframework.Component;
027: import org.araneaframework.Composite;
028: import org.araneaframework.Environment;
029: import org.araneaframework.InputData;
030: import org.araneaframework.Message;
031: import org.araneaframework.OutputData;
032: import org.araneaframework.Path;
033: import org.araneaframework.Scope;
034: import org.araneaframework.Service;
035: import org.araneaframework.Viewable;
036: import org.araneaframework.core.util.ExceptionUtil;
037:
038: /**
039: * A full featured Service with support for composite, eventlisteners, viewmodel.
040: *
041: */
042: public abstract class BaseApplicationService extends BaseService
043: implements ApplicationService {
044: private static final Log log = LogFactory
045: .getLog(BaseApplicationService.class);
046: //*******************************************************************
047: // CONSTANTS
048: //*******************************************************************
049: /**
050: * The attribute of the action id.
051: * @deprecated
052: */
053: public static final String ACTION_ID_ATTRIBUTE = ApplicationService.ACTION_HANDLER_ID_KEY;
054:
055: //*******************************************************************
056: // FIELDS
057: //*******************************************************************
058: private Map actionListeners;
059: private Map viewData;
060:
061: //*******************************************************************
062: // PROTECTED CLASSES
063: //*******************************************************************
064: protected class ViewableImpl implements Viewable.Interface {
065: public Object getViewModel() {
066: try {
067: return BaseApplicationService.this .getViewModel();
068: } catch (Exception e) {
069: throw ExceptionUtil.uncheckException(e);
070: }
071: }
072: }
073:
074: protected class CompositeImpl implements Composite.Interface {
075: public Map getChildren() {
076: return BaseApplicationService.this .getChildren();
077: }
078:
079: public void attach(Object key, Component comp) {
080: _getChildren().put(key, comp);
081: }
082:
083: public Component detach(Object key) {
084: return (Component) _getChildren().remove(key);
085: }
086: }
087:
088: public class ViewModel implements
089: ApplicationService.ServiceViewModel {
090: /** @since 1.1 */
091: public Scope getScope() {
092: return BaseApplicationService.this .getScope();
093: }
094:
095: /**
096: * Returns the children of this StandardService.
097: */
098: public Map getChildren() {
099: return BaseApplicationService.this .getChildren();
100: }
101:
102: public Map getData() {
103: return getViewData();
104: }
105: }
106:
107: //*******************************************************************
108: // PRIVATE METHODS
109: //*******************************************************************
110:
111: private synchronized Map getActionListeners() {
112: if (actionListeners == null)
113: actionListeners = new LinkedMap(1);
114:
115: return actionListeners;
116: }
117:
118: private synchronized Map getViewData() {
119: if (viewData == null)
120: viewData = new LinkedMap(1);
121:
122: return viewData;
123: }
124:
125: //*******************************************************************
126: // PUBLIC METHODS
127: //*******************************************************************
128:
129: public Composite.Interface _getComposite() {
130: return new CompositeImpl();
131: }
132:
133: public Viewable.Interface _getViewable() {
134: return new ViewableImpl();
135: }
136:
137: /**
138: * Adds the ActionListener listener with the specified action id.
139: */
140: public void addActionListener(Object actionId,
141: ActionListener listener) {
142: Assert.notNullParam(this , actionId, "actionId");
143: Assert.notNullParam(this , listener, "listener");
144:
145: List list = (List) getActionListeners().get(actionId);
146:
147: if (list == null) {
148: list = new ArrayList(1);
149: }
150: list.add(listener);
151:
152: getActionListeners().put(actionId, list);
153: }
154:
155: /**
156: * Removes the ActionListener listener from this component.
157: */
158: public void removeActionListener(ActionListener listener) {
159: Assert.notNullParam(this , listener, "listener");
160:
161: Iterator ite = (new HashMap(getActionListeners())).values()
162: .iterator();
163: while (ite.hasNext()) {
164: ((List) ite.next()).remove(listener);
165: }
166: }
167:
168: /**
169: * Clears all the ActionListeners with the specified actionId.
170: * @param actionId the id of the ActionListeners.
171: */
172: public void clearActionlisteners(Object actionId) {
173: Assert.notNullParam(this , actionId, "actionId");
174:
175: getActionListeners().remove(actionId);
176: }
177:
178: /**
179: * Adds custom data to the widget view model (${widget.custom['key']}). This data will be available until explicitly
180: * removed with {@link #removeViewData(String)}.
181: */
182: public void putViewData(String key, Object customDataItem) {
183: Assert.notNullParam(this , key, "key");
184:
185: getViewData().put(key, customDataItem);
186: }
187:
188: /**
189: * Removes the custom data under key.
190: */
191: public void removeViewData(String key) {
192: Assert.notNullParam(this , key, "key");
193:
194: getViewData().remove(key);
195: }
196:
197: /**
198: * Returns an unmodifiable map of the children.
199: */
200: public Map getChildren() {
201: return Collections
202: .unmodifiableMap(new LinkedMap(_getChildren()));
203: }
204:
205: /**
206: * Adds a service with the specified key. Allready initilized services cannot be added. Duplicate
207: * keys not allowed. The child is initialized with the Environment env.
208: */
209: public void addService(Object key, Service child, Environment env) {
210: _addComponent(key, child, env);
211: }
212:
213: /**
214: * Adds a service with the specified key. Allready initilized services cannot be added. Duplicate
215: * keys not allowed. The child is initialized with the Environment from
216: * <code>getChildServiceEnvironment()</code>.
217: */
218: public void addService(Object key, Service child) {
219: try {
220: _addComponent(key, child, getChildServiceEnvironment());
221: } catch (Exception e) {
222: throw ExceptionUtil.uncheckException(e);
223: }
224: }
225:
226: /**
227: * Removes the service with the specified key.
228: */
229: public void removeService(Object key) {
230: _removeComponent(key);
231: }
232:
233: /**
234: * Relocates parent's child with keyFrom to this service with a new key keyTo. The child
235: * will get the Environment specified by newEnv.
236: * @param parent is the current parent of the child to be relocated.
237: * @param newEnv the new Environment of the child.
238: * @param keyFrom is the key of the child to be relocated.
239: * @param keyTo is the the key, with which the child will be added to this StandardService.
240: */
241: public void relocateService(Composite parent, Environment newEnv,
242: Object keyFrom, Object keyTo) {
243: _relocateComponent(parent, newEnv, keyFrom, keyTo);
244: }
245:
246: /**
247: * Relocates parent's child with keyFrom to this service with a new key keyTo. The child
248: * will get the Environment of this StandardService.
249: * @param parent is the current parent of the child to be relocated.
250: * @param keyFrom is the key of the child to be relocated.
251: * @param keyTo is the the key, with which the child will be added to this StandardService.
252: */
253: public void relocateService(Composite parent, Object keyFrom,
254: Object keyTo) {
255: try {
256: _relocateComponent(parent, getChildServiceEnvironment(),
257: keyFrom, keyTo);
258: } catch (Exception e) {
259: throw ExceptionUtil.uncheckException(e);
260: }
261: }
262:
263: /**
264: * Enables the service with the specified key. Only a disabled service can be enabled.
265: */
266: public void enableService(Object key) {
267: _enableComponent(key);
268: }
269:
270: /**
271: * Disables the service with the specified key. Only a enabled service can be disabled. A disabled
272: * service does not get any actions routed to them.
273: */
274: public void disableService(Object key) {
275: _disableComponent(key);
276: }
277:
278: public Environment getEnvironment() {
279: return super .getEnvironment();
280: }
281:
282: public Environment getChildEnvironment() {
283: try {
284: return getChildServiceEnvironment();
285: } catch (Exception e) {
286: throw ExceptionUtil.uncheckException(e);
287: }
288: }
289:
290: //*******************************************************************
291: // PROTECTED METHODS
292: //*******************************************************************
293: /**
294: * Returns the view model. Usually overridden.
295: */
296: protected Object getViewModel() throws Exception {
297: return new ViewModel();
298: }
299:
300: /**
301: * Returns the the Environment of this Service by default. Usually overridden.
302: */
303: protected Environment getChildServiceEnvironment() throws Exception {
304: return getEnvironment();
305: }
306:
307: /**
308: * Returns the id of the action based on the input. Uses the ACTION_HANDLER_ID_KEY key
309: * to extract it from InputData's global data.
310: */
311: protected Object getActionId(InputData input) {
312: Assert.notNull(this , input,
313: "Cannot extract action id from a null input!");
314: return input.getGlobalData().get(
315: ApplicationService.ACTION_HANDLER_ID_KEY);
316: }
317:
318: protected void propagate(Message message) throws Exception {
319: _propagate(message);
320: }
321:
322: /**
323: * If path hasNextStep() routes to the correct child, otherwise calls the
324: * appropriate listener.
325: */
326: protected void action(Path path, InputData input, OutputData output)
327: throws Exception {
328: if (path != null && path.hasNext()) {
329: Object next = path.next();
330:
331: Assert.notNull(this , next,
332: "Cannot deliver action to child under null key!");
333:
334: Service service = (Service) getChildren().get(next);
335: if (service == null) {
336: log.warn("Service '" + getScope()
337: + "' could not deliver action as child '"
338: + next + "' was not found!"
339: + Assert.this ToString(this ));
340: return;
341: }
342:
343: service._getService().action(path, input, output);
344: } else {
345: handleAction(input, output);
346: }
347: }
348:
349: /**
350: * Calls the approriate listener
351: */
352: protected void handleAction(InputData input, OutputData output)
353: throws Exception {
354: Object actionId = getActionId(input);
355:
356: if (actionId == null) {
357: log.warn("Service '" + getScope()
358: + "' cannot deliver action for a null action id!"
359: + Assert.this ToString(this ));
360: return;
361: }
362:
363: List listener = actionListeners == null ? null
364: : (List) actionListeners.get(actionId);
365:
366: log.debug("Delivering action '" + actionId + "' to service '"
367: + getClass() + "'");
368:
369: if (listener != null && listener.size() > 0) {
370: Iterator ite = (new ArrayList(listener)).iterator();
371: while (ite.hasNext()) {
372: ((ActionListener) ite.next()).processAction(actionId,
373: input, output);
374: }
375:
376: return;
377: }
378:
379: log
380: .warn("Service '"
381: + getScope()
382: + "' cannot deliver action as no action listeners were registered for action id '"
383: + actionId + "'!" + Assert.thisToString(this));
384: }
385: }
|