View Javadoc
1 /* 2 * $Header: /cvsroot/ebxmlrr/jaxr/src/com/sun/xml/registry/ebxml/SoapMessenger.java,v 1.28 2003/08/29 02:33:38 farrukh_najmi Exp $ 3 */ 4 5 package com.sun.xml.registry.ebxml; 6 7 // ??eeg SecurityUtil and RegistryException are server-side specific and 8 // probably need to be generalized. 9 import com.sun.ebxml.registry.security.SecurityUtil; 10 import com.sun.ebxml.registry.RegistryException; 11 12 import java.security.PrivateKey; 13 import java.security.cert.X509Certificate; 14 import java.security.cert.Certificate; 15 16 import javax.security.auth.x500.X500PrivateCredential; 17 18 import javax.activation.*; 19 import javax.mail.*; 20 import javax.mail.internet.*; 21 22 import javax.xml.registry.*; 23 import javax.xml.registry.infomodel.*; 24 25 import javax.xml.messaging.*; 26 import javax.xml.soap.*; 27 import javax.xml.parsers.*; 28 29 import javax.xml.transform.*; 30 import javax.xml.transform.dom.*; 31 import javax.xml.transform.stream.*; 32 import org.w3c.dom.*; 33 34 import org.w3c.dom.*; 35 import org.xml.sax.*; 36 37 38 import java.util.*; 39 import java.io.*; 40 41 import org.apache.commons.logging.Log; 42 import org.apache.commons.logging.LogFactory; 43 import org.apache.xml.security.signature.XMLSignature; 44 import org.apache.xml.security.exceptions.*; 45 import org.apache.xml.security.utils.resolver.ResourceResolver; 46 import org.apache.xml.security.utils.resolver.implementations.ResolverLocalFilesystem; 47 import org.apache.xml.security.utils.XMLUtils; 48 49 import org.oasis.ebxml.registry.bindings.rs.RegistryResponse; 50 51 /*** 52 * Class is responsible for communicating w/ registry using SOAP 53 * Implementation private class 54 */ 55 public class SoapMessenger { 56 static final String EBXML_NS = 57 "urn:oasis:names:tc:ebxml-regrep:query:xsd:2.0"; 58 59 private URLEndpoint endpoint; 60 private RegistryServiceImpl regService; 61 private BusinessLifeCycleManagerImpl lcm; 62 private Log log; 63 64 // Credential related fields 65 private boolean isCredentialValidated; 66 private X509Certificate cert; 67 private Certificate[] certChain; 68 private PrivateKey privateKey; 69 private String signingAlgo; 70 71 private static final com.sun.xml.registry.ebxml.util.SecurityUtil su = 72 com.sun.xml.registry.ebxml.util.SecurityUtil.getInstance(); 73 74 75 /*** Simple way to init a jakarta commons Log object */ 76 // private Log log = LogFactory.getLog(SoapMessenger.class); 77 78 SoapMessenger(RegistryServiceImpl regService, String registryUrl, 79 BusinessLifeCycleManagerImpl lcm) 80 { 81 this.regService = regService; 82 endpoint = new URLEndpoint(registryUrl); 83 log = regService.getConnection().getConnectionFactory().getLog(); 84 this.lcm = lcm; 85 } 86 87 void dumpMessage(String info, SOAPMessage msg) throws SOAPException { 88 if (log.isTraceEnabled()) { 89 if (info != null) { 90 System.err.print(info); 91 } 92 try { 93 msg.writeTo(System.err); 94 System.err.println(); 95 } catch (IOException x) { 96 } 97 } 98 } 99 100 /*** 101 * Send a SOAP request to the registry server. Main entry point for 102 * this class. 103 * 104 * @param requestString String that will be placed in the body of the 105 * SOAP message to be sent to the server 106 * 107 * @return BulkResponseImpl that represents the response from the 108 * server 109 */ 110 public BulkResponseImpl sendSoapRequest(String requestString) 111 throws JAXRException 112 { 113 return sendSoapRequest(requestString, null, false); 114 } 115 116 /*** 117 * Send a SOAP request to the registry server. Main entry point for 118 * this class. 119 * 120 * @param requestString String that will be placed in the body of the 121 * SOAP message to be sent to the server 122 * 123 * @param attachments HashMap consisting of entries each of which 124 * corresponds to an attachment where the entry key is the ContentId 125 * and the entry value is a javax.activation.DataHandler of the 126 * attachment. A parameter value of null means no attachments. 127 * 128 * @return BulkResponseImpl that represents the response from the 129 * server 130 */ 131 public BulkResponseImpl sendSoapRequest(String requestString, 132 HashMap attachments) 133 throws JAXRException 134 { 135 return sendSoapRequest(requestString, attachments, false); 136 } 137 138 /*** 139 * Send a SOAP request to the registry server. Main entry point for 140 * this class. 141 * 142 * @param requestString String that will be placed in the body of the 143 * SOAP message to be sent to the server 144 * 145 * @param attachments HashMap consisting of entries each of which 146 * corresponds to an attachment where the entry key is the ContentId 147 * and the entry value is a javax.activation.DataHandler of the 148 * attachment. A parameter value of null means no attachments. 149 * 150 * @return BulkResponseImpl that represents the response from the 151 * server 152 */ 153 public BulkResponseImpl sendSoapRequest(String requestString, 154 HashMap attachments, 155 boolean doSignature) 156 throws JAXRException 157 { 158 if (doSignature && !isCredentialValidated) { 159 validateCredential(); 160 isCredentialValidated = true; 161 } 162 163 // Remove the XML Declaration, if any 164 if (requestString.startsWith("<?xml")) { 165 requestString = 166 requestString.substring(requestString.indexOf("?>") + 2).trim(); 167 } 168 169 StringBuffer soapText = new StringBuffer( 170 "<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">"); 171 172 // ??eeg The registry server seems to require an empty Header 173 // element even though the SOAP 1.1 spec says that it is optional. 174 soapText.append("<soap-env:Header/>"); 175 176 soapText.append("<soap-env:Body>"); 177 soapText.append(requestString); 178 soapText.append("</soap-env:Body>"); 179 soapText.append("</soap-env:Envelope>"); 180 181 log.trace("requestString=\"" + requestString + "\""); 182 183 try { 184 // Use Unicode (utf-8) to getBytes (server and client). Rely on platform default encoding is not safe. 185 InputStream soapStream = new ByteArrayInputStream(soapText.toString().getBytes("utf-8")); 186 if (doSignature) { 187 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 188 SecurityUtil.getInstance().signSOAPMessage( 189 soapStream, baos, privateKey, certChain, signingAlgo); 190 soapStream = new ByteArrayInputStream(baos.toByteArray()); 191 } 192 193 MessageFactory factory = MessageFactory.newInstance(); 194 SOAPMessage msg = factory.createMessage(); 195 SOAPPart soapPart = msg.getSOAPPart(); 196 soapPart.setContent(new StreamSource(soapStream)); 197 198 if (attachments != null && !attachments.isEmpty()) { 199 addAttachments(msg, attachments); 200 } 201 202 SOAPMessage response = send(msg); 203 204 // Process the main SOAPPart of the response 205 Reader reader = processResponseBody(response, "RegistryResponse"); 206 RegistryResponse ebResponse = null; 207 try { 208 ebResponse = RegistryResponse.unmarshal(reader); 209 } catch (Exception x) { 210 log.debug(x); 211 throw new JAXRException("Invalid registry server response"); 212 } 213 214 // Process the attachments of the response if any 215 HashMap responseAttachments = processResponseAttachments(response); 216 217 return new BulkResponseImpl(lcm, ebResponse, responseAttachments); 218 } catch (UnsupportedEncodingException x) { 219 throw new JAXRException(x); 220 } catch (MessagingException x) { 221 throw new JAXRException(x); 222 } catch (FileNotFoundException x) { 223 throw new JAXRException(x); 224 } catch (SOAPException x) { 225 x.printStackTrace(); 226 throw new JAXRException("Error. Cannot connect to specified URL. Please check the URL and try again.", x); 227 } catch (TransformerConfigurationException x) { 228 throw new JAXRException(x); 229 } catch (TransformerException x) { 230 throw new JAXRException(x); 231 } catch (RegistryException x) { 232 throw new JAXRException(x); 233 } 234 } 235 236 private void validateCredential() throws JAXRException { 237 X500PrivateCredential cred = 238 regService.getConnection().getX500PrivateCredential(); 239 if (cred == null) { 240 throw new JAXRException( 241 "No credentials have been set on Connection"); 242 } 243 if (cred.isDestroyed()) { 244 throw new JAXRException( 245 "Credential has been destroyed"); 246 } 247 cert = cred.getCertificate(); 248 if (cert == null) { 249 throw new JAXRException( 250 "X509Certificate not found in X500PrivateCredential"); 251 } 252 privateKey = cred.getPrivateKey(); 253 if (privateKey == null) { 254 throw new JAXRException( 255 "PrivateKey not found in X500PrivateCredential"); 256 } 257 258 certChain = su.getCertificateChain(cert); 259 260 // Get the algorithm of the key 261 signingAlgo = privateKey.getAlgorithm(); 262 if (signingAlgo.equalsIgnoreCase("DSA")) { 263 signingAlgo = XMLSignature.ALGO_ID_SIGNATURE_DSA; 264 } 265 else if (signingAlgo.equalsIgnoreCase("RSA")) { 266 signingAlgo = XMLSignature.ALGO_ID_SIGNATURE_RSA; 267 } 268 else { 269 throw new JAXRException("Signature algorithm not supported"); 270 } 271 } 272 273 private void addAttachments(SOAPMessage msg, HashMap attachments) 274 throws MessagingException, FileNotFoundException, RegistryException 275 { 276 for (Iterator it = attachments.entrySet().iterator(); it.hasNext(); ) { 277 Map.Entry entry = (Map.Entry)it.next(); 278 String id = (String)entry.getKey(); 279 280 DataHandler dh = (DataHandler)entry.getValue(); 281 282 addAttachment(msg, id, dh, dh.getContentType()) ; 283 284 /* 285 // Attachment must be contained in a MimeMultipart containing 286 // two body parts 287 MimeMultipart mp = new MimeMultipart(); 288 289 // Body part 1 is the signature 290 // XXX Checking is currently disabled on server side 291 MimeBodyPart bp1 = new MimeBodyPart(); 292 bp1.setContent("<xmldsig-goes-here/>", "text/plain"); //??text/xml 293 mp.addBodyPart(bp1); 294 295 // Body part 2 is the actual content being signed 296 MimeBodyPart bp2 = new MimeBodyPart(); 297 bp2.setDataHandler(dh); 298 mp.addBodyPart(bp2); 299 300 // Get existing boundary param from MimeMultipart 301 ContentType mpContentType = new ContentType(mp.getContentType()); 302 String boundary = mpContentType.getParameter("boundary"); 303 304 // Construct Content-Type for AttachmentPart 305 ContentType apContentType = new ContentType("multipart/related"); 306 apContentType.setParameter("boundary", boundary); 307 308 // Construct AttachmentPart and add it to SOAPMessage 309 AttachmentPart ap = msg.createAttachmentPart( 310 mp, apContentType.toString()); 311 ap.setContentId(id); 312 msg.addAttachmentPart(ap); 313 log.trace("adding attachment: contentId=" + id); 314 */ 315 } 316 } 317 318 public void addAttachment(SOAPMessage msg, String id, DataHandler dh, String mimeType) throws 319 FileNotFoundException, MessagingException, RegistryException { 320 321 322 //Create a multipart with two bodyparts 323 //First bodypart is an XMLDSIG and second is the attached file 324 325 MimeMultipart mp = new MimeMultipart(); 326 327 MimeBodyPart bp2 = new MimeBodyPart(); 328 bp2.setDataHandler(dh); 329 bp2.addHeader("Content-Type", mimeType); 330 bp2.addHeader("Content-ID", "payload2"); 331 mp.addBodyPart(bp2); 332 333 String payloadSigFilePath = "payloadSig.xml"; 334 SecurityUtil secUtil = SecurityUtil.getInstance(); 335 336 secUtil.signPayload(mp, id, new FileOutputStream(payloadSigFilePath), 337 privateKey, (X509Certificate)certChain[0], signingAlgo); 338 339 /* add the payload signature to the MimeMultipart. But the signature 340 is added at the first BodyPart and the payload is added to the second*/ 341 mp.removeBodyPart(0); 342 MimeBodyPart bp1 = new MimeBodyPart(); 343 File payloadSigFile = new File(payloadSigFilePath); 344 FileDataSource payloadSigFileDs = new FileDataSource(payloadSigFile); 345 DataHandler payloadSigFileDh = new DataHandler(payloadSigFileDs); 346 bp1.setDataHandler(payloadSigFileDh); 347 bp1.addHeader("Content-Type", "text/plain"); 348 bp1.addHeader("Content-ID", "payload1"); 349 mp.addBodyPart(bp1); 350 mp.addBodyPart(bp2); 351 352 // Get existing boundary param from MimeMultipart 353 ContentType mpContentType = new ContentType(mp.getContentType()); 354 String boundary = mpContentType.getParameter("boundary"); 355 356 // Construct Content-Type for AttachmentPart 357 ContentType apContentType = new ContentType("multipart/related"); 358 apContentType.setParameter("boundary", boundary); 359 360 // Construct AttachmentPart and add it to SOAPMessage 361 AttachmentPart ap = msg.createAttachmentPart( 362 mp, apContentType.toString()); 363 ap.setContentId(id); 364 msg.addAttachmentPart(ap); 365 log.trace("adding attachment: contentId=" + id); 366 367 } 368 369 370 SOAPMessage send(SOAPMessage msg) throws SOAPException { 371 SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance(); 372 SOAPConnection connection = scf.createConnection(); 373 374 dumpMessage("Request=", msg); 375 376 long t1 = System.currentTimeMillis(); 377 378 SOAPMessage reply = null; 379 // if (localCall) { 380 // reply = (new RegistryJAXMServlet()).onMessage(msg); 381 // } else { 382 reply = connection.call(msg, endpoint); 383 // } 384 385 long t2 = System.currentTimeMillis(); 386 387 dumpMessage("Response=", reply); 388 389 double secs = ((double) t2 - t1) / 1000; 390 log.debug("Call elapsed time in seconds: " + secs); 391 return reply; 392 } 393 394 Reader processResponseBody(SOAPMessage response, String lookFor) 395 throws JAXRException, SOAPException, TransformerConfigurationException, 396 TransformerException 397 { 398 // grab info out of reply 399 SOAPPart replyPart = response.getSOAPPart(); 400 Source replySource = replyPart.getContent(); 401 402 // transform 403 TransformerFactory tFactory = TransformerFactory.newInstance(); 404 Transformer xFormer = tFactory.newTransformer(); 405 DOMResult domResult = new DOMResult(); 406 xFormer.transform(replySource, domResult); 407 org.w3c.dom.Node node = domResult.getNode(); 408 //log.trace("Traversing DOM..."); 409 while (node != null) { 410 /* 411 if (log.isTraceEnabled()) { 412 log.trace("nodeName=" + node.getNodeName() 413 + ", localName=" + node.getLocalName()); 414 } 415 */ 416 if (lookFor.equalsIgnoreCase(node.getLocalName())) { 417 break; 418 } 419 node = nextNode(node); 420 } 421 422 if (node == null) { 423 throw new JAXRException( 424 "Could not find element '" + lookFor + "' in response"); 425 } 426 427 return domNode2StringReader(node); 428 } 429 430 private static org.w3c.dom.Node nextNode(org.w3c.dom.Node node) { 431 // assert(node != null); 432 org.w3c.dom.Node child = node.getFirstChild(); 433 if (child != null) { 434 return child; 435 } 436 437 org.w3c.dom.Node sib; 438 while ((sib = node.getNextSibling()) == null) { 439 node = node.getParentNode(); 440 if (node == null) { 441 // End of document 442 return null; 443 } 444 } 445 return sib; 446 } 447 448 StringReader domNode2StringReader(org.w3c.dom.Node node) 449 throws TransformerConfigurationException, TransformerException 450 { 451 TransformerFactory tfactory = TransformerFactory.newInstance(); 452 StringWriter writer = null; 453 454 Transformer serializer = tfactory.newTransformer(); 455 Properties oprops = new Properties(); 456 oprops.put("method", "xml"); 457 oprops.put("indent", "yes"); 458 serializer.setOutputProperties(oprops); 459 writer = new StringWriter(); 460 serializer.transform(new DOMSource(node), 461 new StreamResult(writer)); 462 String outString = writer.toString(); 463 //log.trace("outString=" + outString); 464 StringReader reader = new StringReader(outString); 465 return reader; 466 } 467 468 /*** 469 * @return HashMap containing {contentId, DataHandler} entries or null 470 * if no attachments 471 */ 472 private HashMap processResponseAttachments(SOAPMessage response) 473 throws JAXRException, SOAPException, MessagingException 474 { 475 if (response.countAttachments() == 0) { 476 return null; 477 } 478 479 HashMap attachMap = new HashMap(); 480 481 for (Iterator it = response.getAttachments(); it.hasNext(); ) { 482 AttachmentPart ap = (AttachmentPart)it.next(); 483 484 String contentId = ap.getContentId(); 485 //log.trace("Processing attachment w/ contentId=" + contentId); 486 487 Object obj = ap.getContent(); 488 if (!(obj instanceof javax.mail.internet.MimeMultipart)) { 489 // XXX Maybe errors should just be logged and skipped 490 // instead of throwing an Exception 491 throw new JAXRException( 492 "Expected javax.mail.internet.MimeMultipart got " 493 + obj.getClass().getName()); 494 } 495 496 // The Multipart should have two BodyParts. First is the 497 // signature, second is the payload 498 MimeMultipart mp = (MimeMultipart)obj; 499 500 if (mp.getCount() != 2) { 501 throw new JAXRException("Found " + mp.getCount() + " BodyParts. A Multipart for a RepositoryItem must have exactly 2 BodyParts. First is the signature, second is the repository item."); 502 } 503 504 // BodyPart bp1 = mp.getBodyPart(0); //The XMLSignature 505 BodyPart bp2 = mp.getBodyPart(1); //The repository item 506 507 DataHandler dh = bp2.getDataHandler(); 508 attachMap.put(contentId, dh); 509 } 510 return attachMap; 511 } 512 }

This page was automatically generated by Maven