001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036: package com.sun.xml.ws.tx.coordinator;
037:
038: import com.sun.istack.NotNull;
039: import com.sun.istack.Nullable;
040: import com.sun.xml.ws.tx.common.ActivityIdentifier;
041: import com.sun.xml.ws.tx.common.Identifier;
042: import com.sun.xml.ws.tx.common.TxLogger;
043: import com.sun.xml.ws.tx.webservice.member.coord.CreateCoordinationContextType;
044:
045: import javax.xml.ws.EndpointReference;
046: import javax.xml.ws.WebServiceContext;
047: import java.util.List;
048: import java.util.Timer;
049: import java.util.TimerTask;
050: import java.util.logging.Level;
051:
052: /**
053: * This class encapsulates a coordinated activity.
054: * <p/>
055: * Whenever a client (participant) registers for the activity, a {@link Registrant}
056: * is constructed and managed by this class.
057: *
058: * @author Ryan.Shoemaker@Sun.COM
059: * @version $Revision: 1.9 $
060: * @since 1.0
061: */
062: public abstract class Coordinator {
063:
064: /* the actual coordination context for this activity */
065: private final CoordinationContextInterface context;
066:
067: /* the SOAP request to create a new context, if it exists */
068: private final CreateCoordinationContextType request;
069:
070: /* the unique coordination activity id */
071: private final ActivityIdentifier id;
072:
073: /**
074: * Timer to manage expiration of registrants
075: */
076: private final static Timer expirationTimer = new Timer(
077: "WS-TX Expiration Timer");
078: private ExpirationTask expirationTask = null;
079: private boolean expired = false;
080:
081: static private TxLogger logger = TxLogger
082: .getCoordLogger(Coordinator.class);
083:
084: /**
085: * Construct a new Coordinator object from the specified context and soap request.
086: *
087: * @param context The coordination context
088: * @param request The soap request
089: */
090: public Coordinator(@NotNull
091: CoordinationContextInterface context, @Nullable
092: CreateCoordinationContextType request) {
093: this .context = context;
094: this .request = request;
095:
096: this .id = new ActivityIdentifier(context.getIdentifier());
097:
098: if (logger.isLogging(Level.FINER)) {
099: logger.finer("Coordinator constructor",
100: "New Coordinator created for activity: "
101: + context.getIdentifier());
102: }
103:
104: if (context.getExpires() != 0L) {
105: // start a expiration timer task if necessary
106: if (logger.isLogging(Level.FINER)) {
107: logger.finer("Coordinator constructor",
108: "Starting expiration task for activity: "
109: + context.getIdentifier()
110: + " will expire in "
111: + context.getExpires() + "ms");
112: }
113: expirationTask = new ExpirationTask(this );
114: expirationTimer.schedule(expirationTask, context
115: .getExpires());
116: }
117: }
118:
119: /**
120: * Construct a new Coordinator object from the specified context.
121: * <p/>
122: * This constructor will be the main entry point for activity within the
123: * AppServer.
124: *
125: * @param context The coordination context
126: */
127: public Coordinator(@NotNull
128: CoordinationContextInterface context) {
129: this (context, null);
130: }
131:
132: /**
133: * Get the coordination context associated with this coordinated activity
134: *
135: * @return The coordination context
136: */
137: @NotNull
138: public CoordinationContextInterface getContext() {
139: return context;
140: }
141:
142: /**
143: * Get the SOAP request associated with this coordinated activity, if it
144: * exists.
145: *
146: * @return The original SOAP request (createCoordinationContext) or null
147: * if it doesn't exist.
148: */
149: @Nullable
150: public CreateCoordinationContextType getRequest() {
151: return request;
152: }
153:
154: /**
155: * Get the activity id value
156: *
157: * @return The activity id value
158: */
159: @NotNull
160: public String getIdValue() {
161: return id.getValue();
162: }
163:
164: /**
165: * Get the {@link ActivityIdentifier} object.
166: * <p/>
167: * This object can be used when it is necessary to insert the id as
168: * a ReferenceParameter in a soap message
169: *
170: * @return The activity id object
171: */
172: @NotNull
173: public Identifier getId() {
174: return id;
175: }
176:
177: /**
178: * Get the expiration value
179: *
180: * @return The expiration value
181: */
182: public long getExpires() {
183: return context.getExpires();
184: }
185:
186: public void setExpires(final long i) {
187: if (context != null) {
188: context.setExpires(i);
189: }
190: }
191:
192: /**
193: * Get the list of {@link Registrant}s for this coordinated activity.
194: * <p/>
195: * The returned list is unmodifiable (read-only). Add new Registrants
196: * with the {@link #addRegistrant(Registrant,WebServiceContext)} api instead.
197: *
198: * @return the list of Registrant objects
199: */
200: @NotNull
201: public abstract List<Registrant> getRegistrants();
202:
203: /**
204: * Add the specified Registrant to the list of registrants for this
205: * coordinated activity.
206: *
207: * @param registrant The {@link Registrant}
208: * @param wsContext the web service context of the incoming message or null if it isn't available
209: */
210: public abstract void addRegistrant(Registrant registrant,
211: WebServiceContext wsContext);
212:
213: /**
214: * Get the registrant with the specified id or null if it does not exist.
215: *
216: * @param id the registrant id
217: * @return the Registrant object or null if the id does not exist
218: */
219: @Nullable
220: public abstract Registrant getRegistrant(String id);
221:
222: /**
223: * Remove the registrant with the specified id
224: *
225: * @param id the registrant id
226: */
227:
228: public abstract void removeRegistrant(String id);
229:
230: /**
231: * Return true iff this coordinator is delegating to a root coordinator
232: *
233: * @return true iff this coordinator is delegating to a root coordinator
234: */
235: public boolean isSubordinate() {
236: return context.getRootRegistrationService() != null;
237: }
238:
239: /**
240: * Return the Coordinator Protocol Service EPR for registrant r.
241: *
242: * @param r registrant
243: * @return the CPS EPT for the specified registrant
244: */
245: @NotNull
246: public abstract EndpointReference getCoordinatorProtocolServiceForRegistrant(
247: @NotNull
248: Registrant r);
249:
250: /**
251: * Return true iff registrant should register with its root registration service.
252: * <p/>
253: * Enables local participants to be cached with coordinator locally when this method returns
254: * true.
255: *
256: * @param r restistrant
257: * @return Return true iff registrant should register with its root registration service
258: */
259: public boolean registerWithRootRegistrationService(@NotNull
260: final Registrant r) {
261: return false;
262: }
263:
264: /**
265: * Sub classes will implement this method to indicate whether or not they
266: * are subject to expiration.
267: *
268: * @return true if the coordinator should NOT expire, false otherwise.
269: */
270: abstract public boolean expirationGuard();
271:
272: /**
273: * Release resources held by this coordinator.
274: * <p/>
275: * This method will be automatically invoked once if the activity has a non-zero expiration.
276: * <p/>
277: * During expiration, the coordinator will iterate over all of its registrants and tell them
278: * to expire. Depending on their state, registrants will either expire or not. A coordinator
279: * will not completely expire until all of its registrants have expired.
280: */
281: public void expire() {
282: expired = true;
283: if (logger.isLogging(Level.FINEST)) {
284: logger.finest("Coordinator.expire",
285: "attempting to expire coordinator: "
286: + id.getValue());
287: }
288: if (!expirationGuard()) {
289: if (logger.isLogging(Level.FINEST)) {
290: logger.finest("Coordinator.expire",
291: "forgetting resources for: " + id.getValue());
292: }
293: // TODO: send fault S4.4 wscoor:NoActivity
294:
295: forget();
296: } else {
297: if (logger.isLogging(Level.FINEST)) {
298: logger
299: .finest("Coordinator.expire",
300: "expiration was guarded, returning without expiration");
301: }
302: }
303: }
304:
305: public boolean isExpired() {
306: return expired;
307: }
308:
309: public void setExpired(final boolean expired) {
310: this .expired = expired;
311: }
312:
313: /**
314: * Release all resources associated with this coordinator
315: */
316: public void forget() {
317: if (expirationTask != null) {
318: expirationTask.cancel();
319: expirationTask = null;
320: }
321: CoordinationManager.getInstance().removeCoordinator(
322: this .id.getValue());
323: }
324:
325: /**
326: * Timer class for controlling the expiration of registrants.
327: * <p/>
328: * Schedule timer tasks whenever we add a registrant and expire them when
329: * ever the timers go off.
330: */
331: static class ExpirationTask extends TimerTask {
332: Coordinator c;
333:
334: ExpirationTask(Coordinator c) {
335: this .c = c;
336: }
337:
338: public void run() {
339: c.expire();
340: this .cancel(); // we only want to be triggered once
341: }
342: }
343: }
|