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: package org.apache.wicket;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.util.Collections;
022: import java.util.HashMap;
023: import java.util.Map;
024:
025: import org.apache.wicket.authorization.AuthorizationException;
026: import org.apache.wicket.request.RequestParameters;
027: import org.apache.wicket.request.target.component.listener.ListenerInterfaceRequestTarget;
028: import org.apache.wicket.util.lang.Classes;
029: import org.slf4j.Logger;
030: import org.slf4j.LoggerFactory;
031:
032: /**
033: * Base class for request listener interfaces.
034: *
035: * @author Jonathan Locke
036: */
037: public class RequestListenerInterface {
038: /** Map from name to request listener interface */
039: private static final Map interfaces = Collections
040: .synchronizedMap(new HashMap());
041:
042: /** Log. */
043: private static final Logger log = LoggerFactory
044: .getLogger(RequestListenerInterface.class);
045:
046: /**
047: * Looks up a request interface listener by name.
048: *
049: * @param interfaceName
050: * The interface name
051: * @return The RequestListenerInterface object, or null if none is found
052: *
053: */
054: public static final RequestListenerInterface forName(
055: final String interfaceName) {
056: return (RequestListenerInterface) interfaces.get(interfaceName);
057: }
058:
059: /** The listener interface method */
060: private Method method;
061:
062: /** The name of this listener interface */
063: private final String name;
064:
065: /**
066: * Whether or not this listener is targetted for a specific page version. If
067: * recordVersion is true the page will be rolled back to the version which
068: * created the url, if false the latest version of the page will be used.
069: */
070: private boolean recordsPageVersion = true;
071:
072: /**
073: * Constructor that creates listener interfaces which record the page
074: * version.
075: *
076: * @param listenerInterfaceClass
077: * The interface class, which must extend IRequestListener.
078: */
079: public RequestListenerInterface(final Class listenerInterfaceClass) {
080: this (listenerInterfaceClass, true);
081: }
082:
083: /**
084: * Constructor.
085: *
086: * @param listenerInterfaceClass
087: * The interface class, which must extend IRequestListener.
088: * @param recordsPageVersion
089: * Whether or not urls encoded for this interface contain the
090: * page version. If set to false the latest page version is
091: * always used.
092: */
093: public RequestListenerInterface(final Class listenerInterfaceClass,
094: boolean recordsPageVersion) {
095: // Ensure that i extends IRequestListener
096: if (!IRequestListener.class
097: .isAssignableFrom(listenerInterfaceClass)) {
098: throw new IllegalArgumentException("Class "
099: + listenerInterfaceClass
100: + " must extend IRequestListener");
101: }
102:
103: this .recordsPageVersion = recordsPageVersion;
104:
105: // Get interface methods
106: final Method[] methods = listenerInterfaceClass.getMethods();
107:
108: // If there is only one method
109: if (methods.length == 1) {
110: // and that method takes no parameters
111: if (methods[0].getParameterTypes().length == 0) {
112: this .method = methods[0];
113: } else {
114: throw new IllegalArgumentException("Method "
115: + methods[0] + " in interface "
116: + listenerInterfaceClass
117: + " cannot take any arguments");
118: }
119: } else {
120: throw new IllegalArgumentException("Interface "
121: + listenerInterfaceClass
122: + " can have only one method");
123: }
124:
125: // Save short class name
126: this .name = Classes.simpleName(listenerInterfaceClass);
127:
128: // Register this listener
129: register();
130: }
131:
132: /**
133: * @return The method for this request listener interface
134: */
135: public final Method getMethod() {
136: return method;
137: }
138:
139: /**
140: * @return The name of this request listener interface
141: */
142: public final String getName() {
143: return name;
144: }
145:
146: /**
147: * @return true if urls encoded for this interface should record the page
148: * version, false if they should always be encoded for the latest
149: * page version
150: */
151: public final boolean getRecordsPageVersion() {
152: return recordsPageVersion;
153: }
154:
155: /**
156: * Invokes a given interface on a component.
157: *
158: * @param page
159: * The Page that contains the component
160: * @param component
161: * The component
162: */
163: public final void invoke(final Page page, final Component component) {
164: if (!component.isEnabled() || !component.isVisibleInHierarchy()) {
165: // just return so that we have a silent fail and just re-render the
166: // page
167: log
168: .info("component not enabled or visible; ignoring call. Component: "
169: + component);
170: return;
171: }
172:
173: page.beforeCallComponent(component, this );
174:
175: try {
176: // Invoke the interface method on the component
177: method.invoke(component, new Object[] {});
178: } catch (InvocationTargetException e) {
179: // Honor redirect exception contract defined in IPageFactory
180: if (e.getTargetException() instanceof AbstractRestartResponseException
181: || e.getTargetException() instanceof AuthorizationException
182: || e.getTargetException() instanceof WicketRuntimeException) {
183: throw (RuntimeException) e.getTargetException();
184: }
185: throw new WicketRuntimeException("Method "
186: + method.getName() + " of "
187: + method.getDeclaringClass()
188: + " targeted at component " + component
189: + " threw an exception", e);
190: } catch (Exception e) {
191: throw new WicketRuntimeException("Method "
192: + method.getName() + " of "
193: + method.getDeclaringClass()
194: + " targeted at component " + component
195: + " threw an exception", e);
196: } finally {
197: page.afterCallComponent(component, this );
198: }
199: }
200:
201: /**
202: * Creates a new request target for this request listener interface
203: *
204: * @param page
205: * The page
206: * @param component
207: * The component
208: * @param listener
209: * The listener to call
210: * @param requestParameters
211: * Request parameters
212: * @return The request target
213: */
214: public IRequestTarget newRequestTarget(final Page page,
215: final Component component,
216: final RequestListenerInterface listener,
217: final RequestParameters requestParameters) {
218: return new ListenerInterfaceRequestTarget(page, component,
219: listener, requestParameters);
220: }
221:
222: /**
223: * Method to call to register this interface for use
224: */
225: public void register() {
226: // Register this listener interface
227: registerRequestListenerInterface(this );
228: }
229:
230: /**
231: * @see java.lang.Object#toString()
232: */
233: public String toString() {
234: return "[RequestListenerInterface name=" + name + ", method="
235: + method + "]";
236: }
237:
238: /**
239: * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT.
240: * <p>
241: * In previous versions of Wicket, request listeners were manually
242: * registered by calling this method. Now there is a first class
243: * RequestListenerInterface object which should be constructed as a constant
244: * member of the interface to enable automatic interface registration.
245: * <p>
246: * Adds a request listener interface to the map of interfaces that can be
247: * invoked by outsiders.
248: *
249: * @param requestListenerInterface
250: * The request listener interface object
251: */
252: private final void registerRequestListenerInterface(
253: final RequestListenerInterface requestListenerInterface) {
254: // Check that a different interface method with the same name has not
255: // already been registered
256: final RequestListenerInterface existingInterface = RequestListenerInterface
257: .forName(requestListenerInterface.getName());
258: if (existingInterface != null
259: && existingInterface.getMethod() != requestListenerInterface
260: .getMethod()) {
261: throw new IllegalStateException(
262: "Cannot register listener interface "
263: + requestListenerInterface
264: + " because it conflicts with the already registered interface "
265: + existingInterface);
266: }
267:
268: // Save this interface method by the non-qualified class name
269: interfaces.put(requestListenerInterface.getName(),
270: requestListenerInterface);
271:
272: log.info("registered listener interface " + this);
273: }
274: }
|