001: /*
002: * $Id: Function.java,v 1.156 2007/08/29 11:39:47 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.server;
008:
009: import org.xins.common.FormattedParameters;
010: import org.xins.common.MandatoryArgumentChecker;
011: import org.xins.common.manageable.Manageable;
012:
013: /**
014: * Base class for function implementation classes.
015: *
016: * <p>A function can be enabled or disabled using the
017: * {@link #setEnabled(boolean)} method. A function that is enabled can be
018: * invoked, while a function that is disabled cannot. By default a function
019: * is enabled.
020: *
021: * @version $Revision: 1.156 $ $Date: 2007/08/29 11:39:47 $
022: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
023: *
024: * @since XINS 1.0.0
025: */
026: public abstract class Function extends Manageable {
027:
028: /**
029: * Call result to be returned when a function is currently disabled. See
030: * {@link #isEnabled()}.
031: */
032: private static final FunctionResult DISABLED_FUNCTION_RESULT = new FunctionResult(
033: "_DisabledFunction");
034:
035: /**
036: * The API implementation this function is part of. This field cannot be
037: * <code>null</code>.
038: */
039: private final API _api;
040:
041: /**
042: * The name of this function. This field cannot be <code>null</code>.
043: */
044: private final String _name;
045:
046: /**
047: * The version of the specification this function implements. This field
048: * cannot be <code>null</code>.
049: */
050: private final String _version;
051:
052: /**
053: * Flag that indicates if this function is currently accessible.
054: */
055: private boolean _enabled;
056:
057: /**
058: * Lock object for <code>_callCount</code>. This field cannot be
059: * <code>null</code>.
060: */
061: private final Object _callCountLock = new Object();
062:
063: /**
064: * The total number of calls executed up until now.
065: */
066: private int _callCount;
067:
068: /**
069: * Statistics object linked to this function. This field cannot be
070: * <code>null</code>.
071: */
072: private final FunctionStatistics _statistics;
073:
074: /**
075: * Constructs a new <code>Function</code>.
076: *
077: * @param api
078: * the API to which this function belongs, not <code>null</code>.
079: *
080: * @param name
081: * the name, not <code>null</code>.
082: *
083: * @param version
084: * the version of the specification this function implements, not
085: * <code>null</code>.
086: *
087: * @throws IllegalArgumentException
088: * if <code>api == null || name == null || version == null</code>.
089: */
090: protected Function(API api, String name, String version)
091: throws IllegalArgumentException {
092:
093: // Check arguments
094: MandatoryArgumentChecker.check("api", api, "name", name,
095: "version", version);
096:
097: // Initialize fields
098: _statistics = new FunctionStatistics();
099: _api = api;
100: _name = name;
101: _version = version;
102: _enabled = true;
103:
104: // Notify the API that a Function has been added
105: _api.functionAdded(this );
106: }
107:
108: /**
109: * Returns the API that contains this function.
110: *
111: * @return
112: * the {@link API}, not <code>null</code>.
113: */
114: public final API getAPI() {
115: return _api;
116: }
117:
118: /**
119: * Returns the name of this function.
120: *
121: * @return
122: * the name, not <code>null</code>.
123: *
124: * @since XINS 1.5.0.
125: */
126: public final String getName() {
127: return _name;
128: }
129:
130: /**
131: * Returns the specification version for this function.
132: *
133: * @return
134: * the version, not <code>null</code>.
135: */
136: final String getVersion() {
137: return _version;
138: }
139:
140: /**
141: * Checks if this function is currently accessible.
142: *
143: * @return
144: * <code>true</code> if this function is currently accessible,
145: * <code>false</code> otherwise.
146: *
147: * @see #setEnabled(boolean)
148: */
149: public final boolean isEnabled() {
150: return _enabled;
151: }
152:
153: /**
154: * Sets if this function is currently accessible.
155: *
156: * @param enabled
157: * <code>true</code> if this function should be accessible,
158: * <code>false</code> if not.
159: *
160: * @see #isEnabled()
161: */
162: public final void setEnabled(boolean enabled) {
163: _enabled = enabled;
164: }
165:
166: /**
167: * Returns the call statistics for this function.
168: *
169: * @return
170: * the statistics, never <code>null</code>.
171: */
172: final FunctionStatistics getStatistics() {
173: return _statistics;
174: }
175:
176: /**
177: * Assigns a new call ID for the caller. Every call to this method will
178: * return an increasing number.
179: *
180: * @return
181: * the assigned call ID, >= 0.
182: */
183: final int assignCallID() {
184: int callID;
185: synchronized (_callCountLock) {
186: callID = _callCount++;
187: }
188: return callID;
189: }
190:
191: /**
192: * Handles a call to this function (wrapper method). This method will call
193: * {@link #handleCall(CallContext context)}.
194: *
195: * @param start
196: * the start time of the call, as milliseconds since the
197: * <a href="http://en.wikipedia.org/wiki/Unix_Epoch">UNIX Epoch</a>.
198: *
199: * @param functionRequest
200: * the request, never <code>null</code>.
201: *
202: * @param ip
203: * the IP address of the requester, never <code>null</code>.
204: *
205: * @return
206: * the call result, never <code>null</code>.
207: *
208: * @throws IllegalStateException
209: * if this object is currently not initialized.
210: */
211: FunctionResult handleCall(long start,
212: FunctionRequest functionRequest, String ip)
213: throws IllegalStateException {
214:
215: // Check state first
216: assertUsable();
217:
218: // Assign a call ID
219: int callID = assignCallID();
220:
221: // Check if this function is enabled
222: if (!_enabled) {
223: performedCall(functionRequest, ip, start,
224: DISABLED_FUNCTION_RESULT);
225: return DISABLED_FUNCTION_RESULT;
226: }
227:
228: // Skipped the function call if asked to
229: if (functionRequest.shouldSkipFunctionCall()) {
230: Object inParams = new FormattedParameters(functionRequest
231: .getParameters(), functionRequest.getDataElement());
232: Log.log_3516(functionRequest.getFunctionName(), inParams);
233: performedCall(functionRequest, ip, start,
234: API.SUCCESSFUL_RESULT);
235: return API.SUCCESSFUL_RESULT;
236: }
237:
238: // Construct a CallContext object
239: CallContext context = new CallContext(functionRequest, start,
240: this , callID, ip);
241:
242: FunctionResult result;
243: try {
244:
245: // Handle the call
246: result = handleCall(context);
247:
248: // Make sure the result is valid
249: InvalidResponseResult invalidResponse = result
250: .checkOutputParameters();
251: if (invalidResponse != null) {
252: result = invalidResponse;
253: String details = invalidResponse.toString();
254: Log.log_3501(functionRequest.getFunctionName(), callID,
255: details);
256: }
257:
258: } catch (Throwable exception) {
259: result = _api.handleFunctionException(start,
260: functionRequest, ip, callID, exception);
261: }
262:
263: // Update function statistics
264: // We assume that this method will never throw any exception
265: performedCall(functionRequest, ip, start, result);
266:
267: return result;
268: }
269:
270: /**
271: * Handles a call to this function.
272: *
273: * @param context
274: * the context for this call, never <code>null</code>.
275: *
276: * @return
277: * the result of the call, never <code>null</code>.
278: *
279: * @throws Throwable
280: * if anything goes wrong.
281: */
282: protected abstract FunctionResult handleCall(CallContext context)
283: throws Throwable;
284:
285: /**
286: * Callback method that should be called after a call to this function.
287: * This method will update the statistics for this funciton and perform
288: * transaction logging.
289: *
290: * <p />This method should <em>never</em> throw any
291: * {@link RuntimeException}. If it does, then that should be considered a
292: * serious bug.
293: *
294: * @param functionRequest
295: * the request, should not be <code>null</code>.
296: *
297: * @param ip
298: * the ip of the requester, should not be <code>null</code>.
299: *
300: * @param start
301: * the start time, as a number of milliseconds since the
302: * <a href="http://en.wikipedia.org/wiki/Unix_Epoch">UNIX Epoch</a>.
303: *
304: * @param result
305: * the call result, should not be <code>null</code>.
306: *
307: * @throws NullPointerException
308: * if <code>parameters == null || result == null</code>.
309: */
310: private final void performedCall(FunctionRequest functionRequest,
311: String ip, long start, FunctionResult result)
312: throws NullPointerException {
313:
314: // NOTE: Since XINS 1.0.0-beta11, callID is ignored.
315:
316: // Get the error code
317: String code = result.getErrorCode();
318:
319: // Update statistics and determine the duration of the call
320: boolean isSuccess = code == null;
321: long duration = _statistics.recordCall(start, isSuccess, code);
322: Engine.logTransaction(functionRequest, result, ip, start,
323: duration);
324: }
325: }
|