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:
037: /*
038: * RMSource.java
039: *
040: * @author Mike Grogan
041: * Created on October 15, 2005, 6:24 PM
042: */
043:
044: package com.sun.xml.ws.rm.jaxws.runtime.client;
045:
046: import com.sun.xml.ws.rm.Message;
047: import com.sun.xml.ws.rm.RMException;
048: import com.sun.xml.ws.rm.jaxws.runtime.RMProvider;
049: import com.sun.xml.ws.rm.jaxws.runtime.SequenceConfig;
050: import javax.xml.namespace.QName;
051: import javax.xml.ws.Dispatch;
052: import javax.xml.ws.Service;
053: import javax.xml.transform.Source;
054: import javax.xml.transform.stream.StreamSource;
055: import java.io.ByteArrayInputStream;
056: import com.sun.xml.ws.rm.Constants;
057: import java.util.logging.Logger;
058: import java.util.logging.Level;
059:
060: /**
061: * An RMSource represents a Collection of RMSequences with a
062: * common acksTo endpoint.
063: */
064: public class RMSource extends
065: RMProvider<ClientInboundSequence, ClientOutboundSequence> {
066:
067: private static final Logger logger = Logger
068: .getLogger(RMSource.class.getName());
069:
070: private static RMSource rmSource = new RMSource();
071:
072: public static RMSource getRMSource() {
073: return rmSource;
074: }
075:
076: private long retryInterval;
077: private RetryTimer retryTimer;
078:
079: public RMSource() {
080:
081: retryInterval = 2000;
082:
083: retryTimer = new RetryTimer(this );
084:
085: }
086:
087: public void setRetryInterval(long retryInterval) {
088: this .retryInterval = retryInterval;
089: }
090:
091: public long getRetryInterval() {
092: return retryInterval;
093: }
094:
095: public synchronized void terminateSequence(
096: ClientOutboundSequence seq) throws RMException {
097:
098: String id = seq.getId();
099: if (seq != null && outboundMap.keySet().contains(id)) {
100: seq.disconnect();
101: removeOutboundSequence(id);
102: }
103: }
104:
105: public synchronized void addOutboundSequence(
106: ClientOutboundSequence seq) {
107: logger.fine(Messages.ADDING_SEQUENCE_MESSAGE
108: .format(seq.getId()));
109:
110: boolean firstSequence = outboundMap.isEmpty();
111: outboundMap.put(seq.getId(), seq);
112:
113: ClientInboundSequence iseq = (ClientInboundSequence) seq
114: .getInboundSequence();
115:
116: String iseqid = null;
117:
118: if (iseq != null && null != (iseqid = iseq.getId())) {
119: inboundMap.put(iseqid, iseq);
120: }
121: if (firstSequence) {
122: retryTimer.start();
123: }
124: }
125:
126: public synchronized void removeOutboundSequence(
127: ClientOutboundSequence seq) {
128:
129: logger.fine(Messages.REMOVING_SEQUENCE_MESSAGE.format(seq
130: .getId()));
131:
132: String id = seq.getId();
133:
134: ClientInboundSequence iseq = (ClientInboundSequence) seq
135: .getInboundSequence();
136:
137: String iseqid = null;
138: if (iseq != null && null != (iseqid = iseq.getId())) {
139: inboundMap.remove(iseqid);
140: }
141: outboundMap.remove(id);
142:
143: if (outboundMap.isEmpty()) {
144: retryTimer.stop();
145: }
146: }
147:
148: private void removeOutboundSequence(String id) {
149:
150: ClientOutboundSequence seq = outboundMap.get(id);
151:
152: if (seq != null) {
153: removeOutboundSequence(seq);
154: } else {
155: String message = Messages.NO_SUCH_OUTBOUND_SEQUENCE
156: .format(id);
157: IllegalArgumentException e = new IllegalArgumentException(
158: message);
159: logger.log(Level.FINE, message, e);
160: throw e;
161: }
162: }
163:
164: /**
165: * Do the necessary maintenance tasks for each <code>ClientInboundSequence</code>
166: * managed by this RMSource. This is done by calling the <code>doMaintenanceTasks</code>
167: * method of each managed sequence.
168: *
169: * @throws RMException Propogates <code>RMException</code> thrown by any of the managed
170: * sequences.
171: */
172:
173: public void doMaintenanceTasks() throws RMException {
174:
175: for (String key : outboundMap.keySet()) {
176:
177: ClientOutboundSequence seq = getOutboundSequence(key);
178:
179: synchronized (seq) {
180: //1. resend all incomplete messages
181: //2. send ackRequested messages in any sequences
182: // in danger of timing out.
183: seq.doMaintenanceTasks();
184: }
185: }
186:
187: }
188:
189: /**
190: * Initialize a sequence using a CreateSequence handshake. The
191: * returned Sequence can be set in BindingProvider properies which will
192: * result in the Sequence being used for the BindingProvider's request messages.
193: *
194: * @param client A Service hosting the endpoint
195: * @param port The QName for the RM enpoint.
196: * @return The ClientOutboundSequence. null if the sequence could not be created
197: *
198: */
199: public ClientOutboundSequence createSequence(
200: javax.xml.ws.Service service, QName portName) {
201:
202: Dispatch<Source> disp = service.createDispatch(portName,
203: Source.class, Service.Mode.PAYLOAD,
204: new javax.xml.ws.RespectBindingFeature());
205:
206: byte[] bytes = Constants.createSequencePayload.getBytes();
207: ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
208: StreamSource source = new StreamSource(stream);
209:
210: try {
211: disp.invoke(source);
212: } catch (Exception e) {
213:
214: //dont care what happened processing the response message. We are only
215: //interested in the sequence that has been stored in the request context
216: //
217: //TODO - At the same time, it would be prettier to get something other than
218: //a fault
219: }
220:
221: ClientOutboundSequence seq = (ClientOutboundSequence) disp
222: .getRequestContext().get(Constants.sequenceProperty);
223: seq.setService(service);
224: return seq;
225:
226: }
227:
228: /**
229: * Initialize a sequence using an existing seuence id known to an RM endpoint.
230: * The method is designed to be used after a startup to reinitialize a
231: * sequence from persisted data.
232: *
233: * @param client A Service hosting the endpoint
234: * @param port The QName for the RM enpoing.
235: * @param sequencID The id to be used for the outbound sequence
236: * @param companionSequenceID The id to be used for the companion inbound sequence,
237: * if any
238: * @return The ClientOutboundSequence. null if the sequence could not be created
239: */
240: public ClientOutboundSequence createSequence(
241: javax.xml.ws.Service service, QName portName,
242: String sequenceID, String companionSequenceID) {
243:
244: //this will throw and exception if the specified sequence does not exist.
245: //removeOutboundSequence(sequenceID);
246:
247: ClientOutboundSequence seq = createSequence(service, portName);
248: if (seq == null) {
249: return null;
250: }
251:
252: try {
253: seq.disconnect(false);
254: } catch (Exception e) {
255: e.printStackTrace();
256: }
257:
258: seq.setId(sequenceID);
259:
260: ClientInboundSequence iseq = (ClientInboundSequence) seq
261: .getInboundSequence();
262:
263: if (companionSequenceID != null) {
264:
265: if (iseq == null || iseq.getId() == null) {
266:
267: String message = Messages.NO_TWO_WAY_OPERATION.format();
268: IllegalArgumentException e = new IllegalArgumentException(
269: message);
270: logger.log(Level.FINE, message, e);
271: throw e;
272: }
273: iseq.setId(companionSequenceID);
274:
275: } else if (iseq != null && iseq.getId() != null) {
276:
277: String message = Messages.NO_INBOUND_SEQUENCE_ID_SPECIFIED
278: .format();
279: IllegalArgumentException e = new IllegalArgumentException(
280: message);
281: logger.log(Level.FINE, message, e);
282: throw e;
283: }
284:
285: if (outboundMap.get(sequenceID) != null) {
286:
287: String message = Messages.SEQUENCE_ALREADY_EXISTS
288: .format(sequenceID);
289: IllegalArgumentException e = new IllegalArgumentException(
290: message);
291: logger.log(Level.FINE, message, e);
292: throw e;
293:
294: }
295:
296: if (companionSequenceID != null
297: && inboundMap.get(companionSequenceID) != null) {
298:
299: String message = Messages.SEQUENCE_ALREADY_EXISTS
300: .format(companionSequenceID);
301: IllegalArgumentException e = new IllegalArgumentException(
302: message);
303: logger.log(Level.FINE, message, e);
304: throw e;
305:
306: }
307:
308: addOutboundSequence(seq);
309:
310: return seq;
311: }
312:
313: }
|