KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > websvc > core > WsdlRetriever


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.websvc.core;
21
22 import java.io.BufferedInputStream JavaDoc;
23 import java.io.ByteArrayInputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26
27 import java.net.ConnectException JavaDoc;
28 import java.net.MalformedURLException JavaDoc;
29 import java.net.URL JavaDoc;
30 import java.net.URLConnection JavaDoc;
31 import java.security.cert.CertificateException JavaDoc;
32 import java.security.cert.X509Certificate JavaDoc;
33
34 import java.text.MessageFormat JavaDoc;
35 import java.util.Iterator JavaDoc;
36
37 import java.util.List JavaDoc;
38 import java.util.ArrayList JavaDoc;
39 import javax.net.ssl.*;
40
41 import javax.swing.SwingUtilities JavaDoc;
42 import org.netbeans.modules.xml.retriever.CertificationPanel;
43 import org.openide.DialogDescriptor;
44 import org.openide.DialogDisplayer;
45 import org.openide.ErrorManager;
46
47 import org.xml.sax.Attributes JavaDoc;
48 import org.xml.sax.InputSource JavaDoc;
49 import org.xml.sax.SAXException JavaDoc;
50 import org.xml.sax.helpers.DefaultHandler JavaDoc;
51 import javax.xml.parsers.SAXParser JavaDoc;
52 import javax.xml.parsers.SAXParserFactory JavaDoc;
53 import javax.xml.parsers.ParserConfigurationException JavaDoc;
54
55 import org.openide.util.NbBundle;
56
57
58 /** !PW FIXME This thread runs without a monitor thread ensuring a proper
59  * timeout and shutdown of the HttpConnection. It should use the timeout
60  * feature of JDK 1.5.0 and the timeout global properties in JDK 1.4.2.
61  *
62  * As is, it is probably possible for this thread to hang if it opens a
63  * connection to an HTTP server, sends a request, and that server never
64  * responds.
65  *
66  * @author Peter Williams
67  */

68 public class WsdlRetriever implements Runnable JavaDoc {
69     
70     public static final int STATUS_START = 0;
71     public static final int STATUS_CONNECTING = 1;
72     public static final int STATUS_DOWNLOADING = 2;
73     public static final int STATUS_COMPLETE = 3;
74     public static final int STATUS_FAILED = 4;
75     public static final int STATUS_TERMINATED = 5;
76     public static final int STATUS_BAD_WSDL = 6;
77     
78     static final String JavaDoc [] STATUS_MESSAGE = {
79         NbBundle.getMessage(WsdlRetriever.class, "LBL_Ready"), // NOI18N
80
NbBundle.getMessage(WsdlRetriever.class, "LBL_Connecting"), // NOI18N
81
NbBundle.getMessage(WsdlRetriever.class, "LBL_Downloading"), // NOI18N
82
NbBundle.getMessage(WsdlRetriever.class, "LBL_Complete"), // NOI18N
83
NbBundle.getMessage(WsdlRetriever.class, "LBL_Exception"), // NOI18N
84
NbBundle.getMessage(WsdlRetriever.class, "LBL_Terminated"), // NOI18N
85
NbBundle.getMessage(WsdlRetriever.class, "LBL_UnknownFileType") // NOI18N
86
};
87     
88     // Thread plumbing
89
private volatile boolean shutdown;
90     private volatile int status;
91     
92     // Wsdl collection information
93
private String JavaDoc wsdlUrlName;
94     private byte [] wsdlContent;
95     private List JavaDoc<SchemaInfo> schemas; // List of imported schema/wsdl files
96

97     private String JavaDoc wsdlFileName;
98     // Parent
99
private MessageReceiver receiver;
100     
101     public WsdlRetriever(MessageReceiver r, String JavaDoc url) {
102         this.shutdown = false;
103         this.status = STATUS_START;
104         this.wsdlUrlName = url;
105         this.wsdlContent = null;
106         this.wsdlFileName = null;
107         this.schemas=null;
108         this.receiver = r;
109     }
110     
111     // Properties
112
public byte [] getWsdl() {
113         return wsdlContent;
114     }
115     
116     public List JavaDoc<SchemaInfo> getSchemas() {
117         return schemas;
118     }
119     
120     public int getState() {
121         return status;
122     }
123     
124     public String JavaDoc getWsdlFileName() {
125         return wsdlFileName;
126     }
127     
128     public String JavaDoc getWsdlUrl() {
129         return wsdlUrlName;
130     }
131     
132     // not sure this is necessary -- for controller to signal shutdown in case
133
// interrupted() doesn't work.
134
public synchronized void stopRetrieval() {
135         shutdown = true;
136     }
137     
138     private URL JavaDoc wsdlUrl;
139     private URLConnection JavaDoc connection;
140     private InputStream JavaDoc in;
141     
142     public void run() {
143         // Set name of thread for easier debugging in case of deadlocks, etc.
144
Thread.currentThread().setName("WsdlRetrieval"); // NOI18N
145

146         wsdlUrl = null;
147         connection = null;
148         in = null;
149         
150         try {
151             // !PW FIXME if we wanted to add an option to turn beautification of
152
// the URL off (because our algorithm conflicted with what the user
153
// need to enter), this is the method that such option would need to
154
// disable.
155
wsdlUrlName = beautifyUrlName(wsdlUrlName);
156             wsdlUrl = new URL JavaDoc(wsdlUrlName);
157             setState(STATUS_CONNECTING);
158             if (wsdlUrlName.startsWith("https")) { //NOI18N
159
setRetrieverTrustManager();
160             }
161             connection = wsdlUrl.openConnection();
162             in = connection.getInputStream();
163             
164             setState(STATUS_DOWNLOADING);
165             
166             // Download the wsdl file
167
wsdlContent = downloadWsdlFileEncoded(new BufferedInputStream JavaDoc(in));
168             
169             WsdlInfo wsdlInfo=null;
170             // Dowdload all imported schemas
171
if(!shutdown) {
172                 wsdlInfo = getWsdlInfo();
173                 if (wsdlInfo != null) {
174                     List JavaDoc /*String*/ schemaNames = wsdlInfo.getSchemaNames();
175                     if (!schemaNames.isEmpty()) {
176                         schemas = new ArrayList JavaDoc<SchemaInfo>();
177                         Iterator JavaDoc it = schemaNames.iterator();
178                         while (!shutdown && it.hasNext()) {
179                             String JavaDoc schemaName = (String JavaDoc)it.next();
180                             String JavaDoc schemaUrlName = getSchemaUrlName(wsdlUrlName,schemaName);
181                             URL JavaDoc schemaUrl = new URL JavaDoc(schemaUrlName);
182                             connection = schemaUrl.openConnection();
183                             in = connection.getInputStream();
184                             schemas.add(new SchemaInfo(schemaName, downloadWsdlFileEncoded(new BufferedInputStream JavaDoc(in))));
185                         }
186                     }
187                 } else {
188                     throw new MalformedURLException JavaDoc();
189                 }
190             }
191             if (!shutdown) {
192                 // extract the (first) service name to use as a suggested filename.
193
List JavaDoc serviceNames = wsdlInfo.getServiceNameList();
194                 if(serviceNames != null && serviceNames.size() > 0) {
195                     wsdlFileName = wsdlUrl.getPath();
196                     int slashIndex = wsdlFileName.lastIndexOf('/');
197                     if(slashIndex != -1) {
198                         wsdlFileName = wsdlFileName.substring(slashIndex+1);
199                     }
200                     
201                     if(wsdlFileName.length() == 0) {
202                         wsdlFileName = (String JavaDoc) serviceNames.get(0) + ".wsdl"; // NOI18N
203
} else if(wsdlFileName.length() < 5 || !".wsdl".equals(wsdlFileName.substring(wsdlFileName.length()-5))) { // NOI18N
204
wsdlFileName += ".wsdl"; // NOI18N
205
}
206                     setState(STATUS_COMPLETE);
207                 } else {
208                     // !PW FIXME bad wsdl file -- can we save and return the parser error message?
209
setState(STATUS_BAD_WSDL);
210                 }
211             } else {
212                 setState(STATUS_TERMINATED);
213             }
214         } catch(ConnectException JavaDoc ex) {
215             setState(STATUS_FAILED, NbBundle.getMessage(WsdlRetriever.class, "ERR_Connection"), ex); // NOI18N
216
log(ex.getMessage());
217         } catch(MalformedURLException JavaDoc ex) {
218             setState(STATUS_FAILED, NbBundle.getMessage(WsdlRetriever.class, "ERR_BadUrl"), ex); // NOI18N
219
log(ex.getMessage());
220         } catch(IOException JavaDoc ex) {
221             setState(STATUS_FAILED, NbBundle.getMessage(WsdlRetriever.class, "ERR_IOException"), ex); // NOI18N
222
log(ex.getMessage());
223         } finally {
224             if(in != null) {
225                 try {
226                     in.close();
227                 } catch(IOException JavaDoc ex) {
228                 }
229             }
230         }
231     }
232     
233     /** Retrieve the wsdl file from the specified inputstream. We don't know how big
234      * the file might be, and while many WSDL files are less than 30-40K, eBay's
235      * WSDL is over 1MB, so there is extra logic here to be very flexible on buffer
236      * space with minimal copying.
237      *
238      * This routine could possibly be cleaned up a bit, but might lose in readability.
239      * For example, 'chunksize' is probably redundant and could be replaced by 'i',
240      * but the java optimizer will do that for us anyway and the usage is more clear
241      * this way.
242      *
243      */

244     private byte [] downloadWsdlFileEncoded(InputStream JavaDoc in) throws IOException JavaDoc {
245         ArrayList JavaDoc<Chunk> chunks = new ArrayList JavaDoc<Chunk>();
246         final int BUF = 65536;
247         boolean eof = false;
248         byte [] data = new byte [0];
249         
250         while(!shutdown && !eof) {
251             byte [] b = new byte[BUF]; // New buffer for this block
252
int i = 0; // index within this block we're writing at
253
int l = 0; // number of bytes read during last call to read().
254
int limit = b.length; // maximum number of bytes to read during call to read()
255
int chunksize = 0; // number of bytes read into this block. Should be always be BUF, except for last block of file.
256

257             while(!shutdown && (l = in.read(b, i, limit)) != -1) {
258                 limit -= l;
259                 i += l;
260                 chunksize += l;
261                 
262                 if(limit == 0) {
263                     break;
264                 }
265             }
266             
267             // if we downloaded any data, add a chunk containing the data to our list of chunks.
268
if(chunksize > 0) {
269                 chunks.add(new Chunk(b, chunksize));
270             }
271             
272             eof = (l == -1);
273         }
274         
275         if(!shutdown) {
276             // calculate length for single byte array that contains the entire WSDL
277
int bufLen = 0;
278             Iterator JavaDoc<Chunk> iter = chunks.iterator();
279             while(iter.hasNext()) {
280                 bufLen += iter.next().getLength();
281             }
282             
283             // Now fill the single byte array with all the chunks we downloaded.
284
data = new byte[bufLen];
285             int index = 0;
286             iter = chunks.iterator();
287             while(iter.hasNext()) {
288                 Chunk c = iter.next();
289                 System.arraycopy(c.getData(), 0, data, index, c.getLength());
290                 index += c.getLength();
291             }
292         }
293         
294         return data;
295     }
296     
297     
298     public static String JavaDoc beautifyUrlName(String JavaDoc urlName) {
299         // 1. verify protocol, use http if not specified.
300
if(urlName.indexOf("://") == -1) { // NOI18N
301
urlName = "http://" + urlName; // NOI18N
302
}
303         
304         // 2. if this looks like a service, add a ?WSDL argument.
305
try {
306             URL JavaDoc testUrl = new URL JavaDoc(urlName);
307             String JavaDoc testName = testUrl.getPath();
308             boolean hasArguments = (testUrl.getFile().indexOf('?') != -1);
309             int slashIndex = testName.lastIndexOf('/');
310             if(slashIndex != -1) {
311                 testName = testName.substring(slashIndex+1);
312             }
313             int dotIndex = testName.lastIndexOf('.');
314             if(dotIndex != -1) {
315                 String JavaDoc extension = testName.substring(dotIndex+1);
316                 if(!"xml".equals(extension) && !"wsdl".equals(extension) && !hasArguments) { // NOI18N
317
urlName += "?WSDL"; // NOI18N
318
}
319             } else if(!hasArguments) {
320                 // no file extension and no http arguments -- probably needs extension
321
urlName = urlName + "?WSDL"; // NOI18N
322
}
323         } catch(MalformedURLException JavaDoc ex) {
324             // do nothing about this here. This error will occur again for real
325
// in the caller and be handled there.
326
}
327         
328         return urlName;
329     }
330     
331     private String JavaDoc getSchemaUrlName(String JavaDoc wsdlUrl, String JavaDoc schemaName) {
332         int index = wsdlUrl.lastIndexOf("/"); //NOI18N
333
if (index>=0) return wsdlUrl.substring(0,index+1)+schemaName;
334         else return null;
335     }
336     
337     private void setState(int newState) {
338         status = newState;
339         log(STATUS_MESSAGE[newState]);
340         SwingUtilities.invokeLater(new MessageSender(receiver, STATUS_MESSAGE[newState]));
341     }
342     
343     private void setState(int newState, String JavaDoc msg, Exception JavaDoc ex) {
344         status = newState;
345         Object JavaDoc [] args = new Object JavaDoc [] { msg, ex.getMessage()};
346         String JavaDoc message = MessageFormat.format(STATUS_MESSAGE[newState], args);
347         log(message);
348         SwingUtilities.invokeLater(new MessageSender(receiver, message));
349     }
350     
351     private void log(String JavaDoc message) {
352         // This method for debugging only.
353
// System.out.println(message);
354
}
355     
356     // private class used to cache a message and post to UI component on AWT Thread.
357
private static class MessageSender implements Runnable JavaDoc {
358         private MessageReceiver receiver;
359         private String JavaDoc message;
360         
361         public MessageSender(MessageReceiver r, String JavaDoc m) {
362             receiver = r;
363             message = m;
364         }
365         
366         public void run() {
367             receiver.setWsdlDownloadMessage(message);
368         }
369     }
370     
371     public interface MessageReceiver {
372         public void setWsdlDownloadMessage(String JavaDoc m);
373     }
374     
375     /** Private method to sanity check the overall format of the WSDL file and
376      * determine the names of the one or more services defined therein.
377      */

378     private WsdlInfo getWsdlInfo() {
379         WsdlInfo result = null;
380         
381         try {
382             SAXParserFactory JavaDoc factory = SAXParserFactory.newInstance();
383             factory.setNamespaceAware(true);
384             SAXParser JavaDoc saxParser = factory.newSAXParser();
385             ServiceNameParser handler= new ServiceNameParser();
386             saxParser.parse(new InputSource JavaDoc(new ByteArrayInputStream JavaDoc(wsdlContent)), handler);
387             result = new WsdlInfo(handler.getServiceNameList(),handler.getSchemaNames());
388         } catch(ParserConfigurationException JavaDoc ex) {
389             // Bogus WSDL, return null.
390
} catch(SAXException JavaDoc ex) {
391             // Bogus WSDL, return null.
392
} catch(IOException JavaDoc ex) {
393             // Bogus WSDL, return null.
394
}
395         
396         return result;
397     }
398     
399     private static final class ServiceNameParser extends DefaultHandler JavaDoc {
400         
401         private static final String JavaDoc W3C_WSDL_SCHEMA = "http://schemas.xmlsoap.org/wsdl"; // NOI18N
402
private static final String JavaDoc W3C_WSDL_SCHEMA_SLASH = "http://schemas.xmlsoap.org/wsdl/"; // NOI18N
403

404         private List JavaDoc<String JavaDoc> serviceNameList;
405         private List JavaDoc<String JavaDoc> schemaNames;
406         
407         private boolean insideSchema;
408         
409         ServiceNameParser() {
410             serviceNameList = new ArrayList JavaDoc<String JavaDoc>();
411             schemaNames = new ArrayList JavaDoc<String JavaDoc>();
412         }
413         
414         public void startElement(String JavaDoc uri, String JavaDoc localname, String JavaDoc qname, Attributes JavaDoc attributes) throws SAXException JavaDoc {
415             if(W3C_WSDL_SCHEMA.equals(uri) || W3C_WSDL_SCHEMA_SLASH.equals(uri)) {
416                 if("service".equals(localname)) { // NOI18N
417
serviceNameList.add(attributes.getValue("name")); // NOI18N
418
}
419                 if("types".equals(localname)) { // NOI18N
420
insideSchema=true;
421                 }
422                 if("import".equals(localname)) { // NOI18N
423
String JavaDoc wsdlLocation = attributes.getValue("location"); //NOI18N
424
if (wsdlLocation!=null && wsdlLocation.indexOf("/")<0 && wsdlLocation.endsWith(".wsdl")) { //NOI18N
425
schemaNames.add(wsdlLocation);
426                     }
427                 }
428             }
429             if(insideSchema && "import".equals(localname)) { // NOI18N
430
String JavaDoc schemaLocation = attributes.getValue("schemaLocation"); //NOI18N
431
if (schemaLocation!=null && schemaLocation.indexOf("/")<0 && schemaLocation.endsWith(".xsd")) { //NOI18N
432
schemaNames.add(schemaLocation);
433                 }
434             }
435         }
436         
437         public void endElement(String JavaDoc uri, String JavaDoc localname, String JavaDoc qname) throws SAXException JavaDoc {
438             if(W3C_WSDL_SCHEMA.equals(uri) || W3C_WSDL_SCHEMA_SLASH.equals(uri)) {
439                 if("types".equals(localname)) { // NOI18N
440
insideSchema=false;
441                 }
442             }
443         }
444         
445         public List JavaDoc<String JavaDoc> getServiceNameList() {
446             return serviceNameList;
447         }
448         
449         public List JavaDoc<String JavaDoc> getSchemaNames() {
450             return schemaNames;
451         }
452     }
453     
454     /** Data chunk of downloaded WSDL.
455      */

456     private static class Chunk {
457         private int length;
458         private byte [] data;
459         
460         public Chunk(byte [] d, int l) {
461             data = d;
462             length = l;
463         }
464         
465         public byte [] getData() {
466             return data;
467         }
468         
469         public int getLength() {
470             return length;
471         }
472     }
473     
474     private static class WsdlInfo {
475         private List JavaDoc<String JavaDoc> serviceNameList;
476         private List JavaDoc<String JavaDoc> schemaNames;
477         
478         WsdlInfo(List JavaDoc<String JavaDoc> serviceNameList, List JavaDoc<String JavaDoc> schemaNames) {
479             this.serviceNameList=serviceNameList;
480             this.schemaNames=schemaNames;
481         }
482         
483         List JavaDoc<String JavaDoc> getSchemaNames() {
484             return schemaNames;
485         }
486         List JavaDoc<String JavaDoc> getServiceNameList() {
487             return serviceNameList;
488         }
489     }
490     
491     public static class SchemaInfo {
492         private String JavaDoc schemaName;
493         private byte[] schemaContent;
494         
495         SchemaInfo(String JavaDoc schemaName, byte[] schemaContent) {
496             this.schemaName=schemaName;
497             this.schemaContent=schemaContent;
498         }
499         
500         public String JavaDoc getSchemaName() {
501             return schemaName;
502         }
503         
504         public byte[] getSchemaContent() {
505             return schemaContent;
506         }
507     }
508     
509     // Install the trust manager for retriever
510
private void setRetrieverTrustManager() {
511         TrustManager[] trustAllCerts = new TrustManager[]{
512             new X509TrustManager() {
513                 public X509Certificate JavaDoc[] getAcceptedIssuers() {
514                     return new X509Certificate JavaDoc[0];
515                 }
516                 public void checkClientTrusted(X509Certificate JavaDoc[] certs, String JavaDoc authType) {
517                 }
518                 public void checkServerTrusted(X509Certificate JavaDoc[] certs, String JavaDoc authType)
519                 throws CertificateException JavaDoc {
520                     // ask user to accept the unknown certificate
521
if (certs!=null) {
522                         for (int i=0;i<certs.length;i++) {
523                             DialogDescriptor desc = new DialogDescriptor(new CertificationPanel(certs[i]),
524                                     NbBundle.getMessage(WsdlRetriever.class,"TTL_CertifiedWebSite"),
525                                     true,
526                                     DialogDescriptor.YES_NO_OPTION,
527                                     DialogDescriptor.YES_OPTION,
528                                     null);
529                             DialogDisplayer.getDefault().notify(desc);
530                             if (!DialogDescriptor.YES_OPTION.equals(desc.getValue())) {
531                                 throw new CertificateException JavaDoc(
532                                         NbBundle.getMessage(WsdlRetriever.class,"ERR_NotTrustedCertificate"));
533                             }
534                         } // end for
535
}
536                 }
537             }
538         };
539         
540         
541         try {
542             SSLContext sslContext = SSLContext.getInstance("SSL"); //NOI18N
543
sslContext.init(null, trustAllCerts, new java.security.SecureRandom JavaDoc());
544             HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
545             HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
546                 public boolean verify(String JavaDoc string, SSLSession sSLSession) {
547                     // accept all hosts
548
return true;
549                 }
550             });
551         } catch (java.security.GeneralSecurityException JavaDoc e) {
552             ErrorManager.getDefault().notify(e);
553         }
554     
555     }
556 }
557
Popular Tags