View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.river.discovery;
20  
21  import java.io.IOException;
22  import java.net.DatagramPacket;
23  import java.net.Socket;
24  import java.security.AccessController;
25  import java.security.PrivilegedAction;
26  import java.util.Collection;
27  import java.util.Iterator;
28  import java.util.ServiceLoader;
29  import java.util.logging.Level;
30  import java.util.logging.Logger;
31  import net.jini.core.constraint.InvocationConstraints;
32  import net.jini.io.UnsupportedConstraintException;
33  
34  /**
35   * <p>
36   * Class providing methods for participating in versions 1 and 2 of the
37   * discovery protocol. </p>
38   *
39   * <b>Logging</b>
40   * <p>
41   *
42   * This implementation uses the {@link java.util.logging.Logger}s named
43   * <code>org.apache.river.discovery.DiscoveryV1</code> and
44   * <code>org.apache.river.discovery.DiscoveryV2</code> to log information at the
45   * following logging levels: </p>
46   *
47   * <table border="1" cellpadding="5"
48   *	  summary="Describes logging performed by the
49   *		   Discovery class to the DiscoveryV1 logger at various
50   *		   logging levels">
51   *
52   * <caption><b><code>org.apache.river.discovery.DiscoveryV1</code></b></caption>
53   *
54   * <tr> <th scope="col"> Level <th scope="col"> Description
55   *
56   * <tr> <td> {@link java.util.logging.Level#FINEST FINEST}
57   *	<td> Encoding and decoding of discovery protocol version 1 multicast
58   *	     requests, multicast announcements, and unicast responses
59   *
60   * </table>
61   *
62   * <table border="1" cellpadding="5"
63   *	  summary="Describes logging performed by the
64   *		   Discovery class to the DiscoveryV2 logger at various logging
65   *		   levels">
66   *
67   * <caption><b><code>org.apache.river.discovery.DiscoveryV2</code></b></caption>
68   *
69   * <tr> <th scope="col"> Level <th scope="col"> Description
70   *
71   * <tr> <td> {@link java.util.logging.Level#WARNING WARNING}
72   *	<td> Truncation of unicast request format ID list due to length;
73   *	     discovery format providers that are unusable or have conflicting
74   *	     discovery format IDs
75   *
76   * <tr> <td> {@link org.apache.river.logging.Levels#HANDLED HANDLED}
77   *	<td> Constraint check failures encountered during the unicast discovery
78   *	     handshake when determining a suitable discovery format to use
79   *
80   * <tr> <td> {@link java.util.logging.Level#FINEST FINEST}
81   *	<td> Encoding and decoding of discovery protocol version 2 multicast
82   *	     requests, multicast announcements, and unicast responses; also,
83   *	     access of <code>Discovery</code> instances implementing protocol
84   *	     version 2
85   *
86   * </table>
87   *
88   * @author Sun Microsystems, Inc.
89   * @since 2.0
90   */
91  public abstract class Discovery {
92  
93      /** The version number for discovery protocol version 1. */
94      public static final int PROTOCOL_VERSION_1 = 1;
95  
96      /** The version number for discovery protocol version 2. */
97      public static final int PROTOCOL_VERSION_2 = 2;
98  
99      static final int MIN_MAX_PACKET_SIZE = 512;
100    
101     private static Logger logger = Logger.getLogger(Discovery.class.getCanonicalName());
102     /**
103      * Returns an instance implementing protocol version 1.
104      *
105      * @return an instance implementing protocol version 1
106      */
107     public static Discovery getProtocol1() {
108 	return DiscoveryV1.getInstance();
109     }
110 
111     /**
112      * Returns an instance implementing protocol version 2 which uses providers
113      * loaded from the given class loader, or the current context class loader
114      * if the given loader is <code>null</code>.  Available providers are
115      * determined by interpreting any resources of the indicated class loader
116      * named
117      * <code>"META-INF/services/org.apache.river.discovery.DiscoveryFormatProvider"</code>
118      * as files containing names of provider classes, with one name per line.
119      *
120      * @param loader class loader from which to load providers, or
121      * <code>null</code> to indicate the current context class loader
122      * @return an instance implementing protocol version 2
123      */
124     public static Discovery getProtocol2(ClassLoader loader) {
125 	return DiscoveryV2.getInstance(loader);
126     }
127 
128     /**
129      * Returns an instance implementing protocol version 2 which uses the given
130      * providers.  Contents of arrays are copied; <code>null</code> array
131      * values are equivalent to empty arrays.
132      *
133      * @param mre providers for encoding multicast requests
134      * @param mrd providers for decoding multicast requests
135      * @param mae providers for encoding multicast announcements
136      * @param mad providers for decoding multicast announcements
137      * @param udc providers for performing the client side of unicast discovery
138      * @param uds providers for performing the server side of unicast discovery
139      * @return an instance implementing protocol version 2
140      */
141     public static Discovery getProtocol2(MulticastRequestEncoder[] mre,
142 					 MulticastRequestDecoder[] mrd,
143 					 MulticastAnnouncementEncoder[] mae,
144 					 MulticastAnnouncementDecoder[] mad,
145 					 UnicastDiscoveryClient[] udc,
146 					 UnicastDiscoveryServer[] uds)
147     {
148 	return DiscoveryV2.getInstance(mre, mrd, mae, mad, udc, uds);
149     }
150     
151     /**
152      * Returns an instance implementing the unicast https protocol which uses providers
153      * loaded from the given class loader, or the context class loader
154      * if the given loader is <code>null</code>.  Available providers are
155      * determined by interpreting any resources of the indicated class loader
156      * named
157      * <code>"META-INF/services/org.apache.river.discovery.Discovery"</code>
158      * with a toString() method that returns the string "UnicastHTTPS".
159      *
160      * TODO: provide a mechanism to allow more flexible mechanism for selection
161      * of loader.
162      * 
163      * @param loader class loader from which to load providers, or
164      * <code>null</code> to indicate the current context class loader
165      * @return an instance implementing the unicast https protocol
166      */
167     public static Discovery getUnicastHttps(final ClassLoader loader){
168 	return AccessController.doPrivileged(
169         new PrivilegedAction<Discovery>(){
170             @Override
171             public Discovery run() {
172 		ClassLoader ploader;
173 		if (loader == null){
174 		    ploader = Thread.currentThread().getContextClassLoader();
175 		} else {
176 		    ploader = loader;
177 		}
178                 ServiceLoader<Discovery> serviceLoader 
179                    = ServiceLoader.load(Discovery.class, ploader);
180                 Iterator<Discovery> iter = serviceLoader.iterator();
181                 Discovery spi;
182                 try {
183                     while ( iter.hasNext() ) {
184                         try {
185                             spi = iter.next();
186                             if (spi != null && spi.toString().equals("UnicastHTTPS")) {
187                                 logger.log(Level.CONFIG, "loaded: {0}", spi.getClass().getName());
188                                 return spi;
189                             }
190                         } catch (Throwable e) {
191                             logger.log( 
192                                     Level.CONFIG, 
193                                     "error loading Discovery: {0}",
194                                     new Object[]{e}
195                             );
196                         }
197                     }
198                 } catch (Throwable t) { }
199                 logger.log(Level.CONFIG, "uable to find Discovery Provider");
200                 return null;
201             }
202         });
203     }
204 
205     /**
206      * Returns an iterator which can be used to encode the given multicast
207      * request data into sets of {@link DatagramPacket}s, each bounded in
208      * length by the specified maximum packet size, in a manner that satisfies
209      * the given constraints.  <code>null</code> constraints are considered
210      * equivalent to empty constraints.  The destination of each
211      * <code>DatagramPacket</code> produced by the returned iterator is set to
212      * the address returned by
213      * {@link net.jini.discovery.Constants#getRequestAddress}, with the value
214      * of {@link net.jini.discovery.Constants#discoveryPort} used as the
215      * destination port.
216      *
217      * @param request the request data to encode
218      * @param maxPacketSize the maximum size of packets to produce
219      * @param constraints the constraints to apply when encoding the data, or
220      * <code>null</code>
221      * @return an iterator to use for encoding the data
222      * @throws NullPointerException if <code>request</code> is
223      * <code>null</code>
224      */
225     public abstract EncodeIterator encodeMulticastRequest(
226 					MulticastRequest request,
227 					int maxPacketSize,
228 					InvocationConstraints constraints);
229 
230     /**
231      * Decodes the multicast request data contained in the given datagram in a
232      * manner that satisfies the specified constraints and client subject
233      * checker (if any), returning a {@link MulticastRequest} instance that
234      * contains the decoded data.  <code>null</code> constraints are considered
235      * equivalent to empty constraints.  All the specified constraints are
236      * checked before this method returns.
237      *
238      * @param packet the packet to decode
239      * @param constraints the constraints to apply when decoding the packet, or
240      * <code>null</code>
241      * @param checker the object to use to check the client subject, or
242      * <code>null</code>
243      * @return the decoded multicast request data
244      * @throws IOException if an error occurs in interpreting the data
245      * @throws UnsupportedConstraintException if unable to satisfy the
246      * specified constraints
247      * @throws SecurityException if the given constraints cannot be satisfied
248      * due to insufficient caller permissions, or if the client subject check
249      * fails
250      * @throws NullPointerException if <code>packet</code> is <code>null</code>
251      */
252     public abstract MulticastRequest decodeMulticastRequest(
253 					DatagramPacket packet,
254 					InvocationConstraints constraints,
255 					ClientSubjectChecker checker)
256 	throws IOException;
257 
258     /**
259      * Decodes the multicast request data contained in the given datagram in a
260      * manner that satisfies the specified constraints and client subject
261      * checker (if any), returning a {@link MulticastRequest} instance that
262      * contains the decoded data, with constraint checking optionally
263      * delayed.  <code>null</code> constraints are considered
264      * equivalent to empty constraints.
265      * <p>The <code>delayConstraintCheck</code> flag is used to control delayed
266      * constraint checking. Delayed constraint checking is useful for
267      * potentially delaying the expense of complete constraint checking, until
268      * other checks have been made on the returned
269      * <code>MulticastRequest</code> for preliminary validation.
270      * Implementations may ignore the flag, in which case, the behavior is
271      * equivalent to that of {@link
272      * Discovery#decodeMulticastRequest(DatagramPacket, InvocationConstraints,
273      * ClientSubjectChecker) decodeMulticastRequest}.
274      * <p>If <code>delayConstraintCheck</code> is <code>true</code>, the method
275      * behaves as follows:<ul>
276      * <li> Some of the specified constraints may not be checked before this
277      * method returns; the returned <code>MulticastRequest</code>'s
278      * {@link MulticastRequest#checkConstraints checkConstraints}
279      * method must be invoked to complete checking of all the constraints.
280      * <li> Constraints which must be checked before accessor methods of the
281      * returned <code>MulticastRequest</code> can be invoked are always
282      * checked before this method returns.</ul>
283      * <p>If <code>delayConstraintCheck</code> is <code>false</code>, all the
284      * specified constraints are checked before this method returns.
285      * <p><code>Discovery</code> implements this method to simply invoke {@link
286      * Discovery#decodeMulticastRequest(DatagramPacket, InvocationConstraints,
287      * ClientSubjectChecker) decodeMulticastRequest}, and thus checks all the
288      * specified constraints before returning.
289      *
290      * @param packet the packet to decode
291      * @param constraints the constraints to apply when decoding the packet, or
292      * <code>null</code>
293      * @param checker the object to use to check the client subject, or
294      * <code>null</code>
295      * @param delayConstraintCheck flag to control delayed constraint checking
296      * @return the decoded multicast request data.
297      * @throws IOException if an error occurs in interpreting the data
298      * @throws SecurityException if the given constraints cannot be satisfied
299      * due to insufficient caller permissions, or if the client subject check
300      * fails
301      * @throws NullPointerException if <code>packet</code> is <code>null</code>
302      * @since 2.1
303      */
304     public MulticastRequest decodeMulticastRequest(
305 					DatagramPacket packet,
306 					InvocationConstraints constraints,
307 					ClientSubjectChecker checker,
308                                         boolean delayConstraintCheck)
309 	throws IOException 
310     {
311 	return decodeMulticastRequest(packet, constraints, checker);
312     }
313     
314     /**
315      * Returns an iterator which can be used to encode the given multicast
316      * announcement data into sets of {@link DatagramPacket}s, each bounded in
317      * length by the specified maximum packet size, in a manner that satisfies
318      * the given constraints.  <code>null</code> constraints are considered
319      * equivalent to empty constraints.  The destination of each
320      * <code>DatagramPacket</code> produced by the returned iterator is set to
321      * the address returned by
322      * {@link net.jini.discovery.Constants#getAnnouncementAddress}, with the
323      * value of {@link net.jini.discovery.Constants#discoveryPort} used as the
324      * destination port.
325      *
326      * @param announcement the announcement data to encode
327      * @param maxPacketSize the maximum size of packets to produce
328      * @param constraints the constraints to apply when encoding the data, or
329      * <code>null</code>
330      * @return an iterator to use for encoding the data
331      * @throws NullPointerException if <code>announcement</code> is
332      * <code>null</code>
333      */
334     public abstract EncodeIterator encodeMulticastAnnouncement(
335 					MulticastAnnouncement announcement,
336 					int maxPacketSize,
337 					InvocationConstraints constraints);
338 
339     /**
340      * Decodes the multicast announcement data contained in the given datagram
341      * in a manner that satisfies the specified constraints, returning a {@link
342      * MulticastAnnouncement} instance that contains the decoded data.
343      * <code>null</code> constraints are considered equivalent to empty
344      * constraints.  All the specified constraints are checked before this
345      * method returns.
346      *
347      * @param packet the packet to decode
348      * @param constraints the constraints to apply when decoding the packet, or
349      * <code>null</code>
350      * @return the decoded multicast announcement data
351      * @throws IOException if an error occurs in interpreting the data
352      * @throws UnsupportedConstraintException if unable to satisfy the
353      * specified constraints
354      * @throws SecurityException if the given constraints cannot be satisfied
355      * due to insufficient caller permissions
356      * @throws NullPointerException if <code>packet</code> is <code>null</code>
357      */
358     public abstract MulticastAnnouncement decodeMulticastAnnouncement(
359 					DatagramPacket packet,
360 					InvocationConstraints constraints)
361 	throws IOException;
362     
363     /**
364      * Decodes the multicast announcement data contained in the given datagram
365      * in a manner that satisfies the specified constraints, returning a {@link
366      * MulticastAnnouncement} instance that contains the decoded data, with
367      * constraint checking optionally delayed.  <code>null</code> constraints
368      * are considered equivalent to empty constraints.
369      * <p>The <code>delayConstraintCheck</code> flag is used to control delayed
370      * constraint checking.  Delayed constraint checking is useful for
371      * potentially delaying the expense of complete constraint checking, until
372      * other checks have been made on the returned
373      * <code>MulticastAnnouncement</code> for preliminary validation.
374      * Implementations may ignore the flag, in which case, the behavior is
375      * equivalent to that of {@link 
376      * Discovery#decodeMulticastAnnouncement(DatagramPacket,
377      * InvocationConstraints) decodeMulticastAnnouncement}.
378      * <p>If <code>delayConstraintCheck</code> is <code>true</code>, the method
379      * behaves as follows:<ul>
380      * <li> Some of the specified constraints may not be checked before this
381      * method returns; the returned <code>MulticastAnnouncement</code>'s
382      * {@link MulticastAnnouncement#checkConstraints checkConstraints}
383      * method must be invoked to complete checking of all the constraints.
384      * <li> Constraints which must be checked before accessor methods of the
385      * returned <code>MulticastAnnouncement</code> can be invoked are always
386      * checked before this method returns.</ul>
387      * <p> If <code>delayConstraintCheck</code> is <code>false</code>,
388      * all the specified constraints are checked before this method returns.
389      * <p><code>Discovery</code> implements this method to simply invoke {@link
390      * Discovery#decodeMulticastAnnouncement(DatagramPacket,
391      * InvocationConstraints) decodeMulticastAnnouncement}, and thus checks
392      * all the specified constraints before returning.
393      *
394      * @param packet the packet to decode
395      * @param constraints the constraints to apply when decoding the packet, or
396      * <code>null</code>
397      * @param delayConstraintCheck flag to control delayed constraint checking
398      * @return the decoded multicast announcement data.
399      * @throws IOException if an error occurs in interpreting the data
400      * @throws UnsupportedConstraintException if unable to satisfy the
401      * specified constraints
402      * @throws SecurityException if the given constraints cannot be satisfied
403      * due to insufficient caller permissions.
404      * @throws NullPointerException if <code>packet</code> is <code>null</code>
405      * @since 2.1
406      */
407     public MulticastAnnouncement decodeMulticastAnnouncement(
408 					DatagramPacket packet,
409 					InvocationConstraints constraints,
410                                         boolean delayConstraintCheck)
411 	throws IOException
412     {
413 	return decodeMulticastAnnouncement(packet, constraints);
414     }
415     
416     /**
417      * Performs the client side of unicast discovery, obtaining the returned
418      * response data over the provided socket using the given default and
419      * codebase verifier class loaders and collection of object stream context
420      * objects in a manner that satisfies the specified constraints.
421      * <code>null</code> constraints are considered equivalent to empty
422      * constraints.  
423      *
424      * @param socket the socket on which to perform unicast discovery
425      * @param constraints the constraints to apply to unicast discovery, or
426      * <code>null</code>
427      * @param defaultLoader the class loader value (possibly <code>null</code>)
428      * to be passed as the <code>defaultLoader</code> argument to
429      * <code>RMIClassLoader</code> methods when unmarshalling the registrar
430      * proxy
431      * @param verifierLoader the class loader value (possibly
432      * <code>null</code>) to pass to {@link
433      * net.jini.security.Security#verifyCodebaseIntegrity
434      * Security.verifyCodebaseIntegrity}, if codebase integrity verification is
435      * used when unmarshalling the registrar proxy
436      * @param context the collection of context information objects (possibly
437      * <code>null</code>) to use when unmarshalling the registrar proxy
438      * @return the received unicast response data
439      * @throws IOException if an error occurs in interpreting received data or
440      * in formatting data to send
441      * @throws UnsupportedConstraintException if unable to satisfy the
442      * specified constraints
443      * @throws SecurityException if the given constraints cannot be satisfied
444      * due to insufficient caller permissions
445      * @throws ClassNotFoundException if the class of the discovered registrar
446      * cannot be resolved
447      * @throws NullPointerException if <code>socket</code> is <code>null</code>
448      */
449     public abstract UnicastResponse doUnicastDiscovery(
450 					Socket socket,
451 					InvocationConstraints constraints,
452 					ClassLoader defaultLoader,
453 					ClassLoader verifierLoader,
454 					Collection context)
455 	throws IOException, ClassNotFoundException;
456 
457     /**
458      * Handles the server side of unicast discovery, transmitting the given
459      * response data over the provided socket using the given collection of
460      * object stream context objects in a manner that satisfies the specified
461      * constraints and client subject checker (if any).  This method assumes
462      * that the protocol version number has already been consumed from the
463      * socket, but that no further processing of the connection has occurred.
464      * <code>null</code> constraints are considered equivalent to empty
465      * constraints.
466      *
467      * @param response the unicast response data to transmit
468      * @param socket the socket on which to handle unicast discovery
469      * @param constraints the constraints to apply to unicast discovery, or
470      * <code>null</code>
471      * @param checker the object to use to check the client subject, or
472      * <code>null</code>
473      * @param context the collection of context information objects to use when
474      * marshalling the registrar proxy
475      * @throws IOException if the protocol handshake fails, or if an error
476      * occurs in interpreting received data or in formatting data to send
477      * @throws UnsupportedConstraintException if unable to satisfy the
478      * specified constraints
479      * @throws SecurityException if the given constraints cannot be satisfied
480      * due to insufficient caller permissions, or if the client subject check
481      * fails
482      * @throws NullPointerException if <code>response</code>,
483      * <code>socket</code>, or <code>context</code> is <code>null</code>
484      */
485     public abstract void handleUnicastDiscovery(
486 					UnicastResponse response,
487 					Socket socket,
488 					InvocationConstraints constraints,
489 					ClientSubjectChecker checker,
490 					Collection context)
491 	throws IOException;
492 }