1 16 package org.apache.cocoon.portal.transformation; 17 18 import java.io.BufferedInputStream ; 19 import java.io.IOException ; 20 import java.io.InputStream ; 21 import java.io.PrintWriter ; 22 import java.io.StringWriter ; 23 import java.io.UnsupportedEncodingException ; 24 import java.net.HttpURLConnection ; 25 import java.net.MalformedURLException ; 26 import java.net.URL ; 27 import java.util.Enumeration ; 28 import java.util.Map ; 29 30 import org.apache.avalon.framework.parameters.ParameterException; 31 import org.apache.avalon.framework.parameters.Parameterizable; 32 import org.apache.avalon.framework.parameters.Parameters; 33 import org.apache.avalon.framework.service.ServiceException; 34 import org.apache.avalon.framework.service.ServiceManager; 35 import org.apache.avalon.framework.service.Serviceable; 36 import org.apache.cocoon.ProcessingException; 37 import org.apache.cocoon.environment.ObjectModelHelper; 38 import org.apache.cocoon.environment.Request; 39 import org.apache.cocoon.environment.SourceResolver; 40 import org.apache.cocoon.portal.Constants; 41 import org.apache.cocoon.portal.PortalService; 42 import org.apache.cocoon.portal.coplet.CopletData; 43 import org.apache.cocoon.portal.coplet.CopletInstanceData; 44 import org.apache.cocoon.portal.profile.ProfileManager; 45 import org.apache.cocoon.transformation.AbstractTransformer; 46 import org.apache.cocoon.util.NetUtils; 47 import org.apache.cocoon.xml.XMLUtils; 48 import org.apache.cocoon.xml.dom.DOMStreamer; 49 import org.w3c.dom.Document ; 50 import org.w3c.dom.Element ; 51 import org.w3c.dom.NodeList ; 52 import org.w3c.tidy.Configuration; 53 import org.w3c.tidy.Tidy; 54 import org.xml.sax.Attributes ; 55 import org.xml.sax.SAXException ; 56 57 68 public class ProxyTransformer 69 extends AbstractTransformer 70 implements Serviceable, Parameterizable { 71 72 75 public static final String ENVELOPE_TAG_PARAMETER = "envelope-tag"; 76 77 public static final String PORTALNAME = "cocoon-portal-portalname"; 78 public static final String COPLETID = "cocoon-portal-copletid"; 79 public static final String PROXY_PREFIX = "proxy-"; 80 81 public static final String COPLET_ID_PARAM = "copletId"; 82 public static final String PORTAL_NAME_PARAM = "portalName"; 83 84 public static final String SESSIONTOKEN = "sessiontoken"; 86 public static final String COOKIE = "cookie"; 87 public static final String START_URI = "start-uri"; 88 public static final String LINK = "link"; 89 public static final String DOCUMENT_BASE = "documentbase"; 90 91 94 public static final String PROTOCOL_HANDLER_PARAMETER = "protocol-handler"; 95 96 99 protected String documentBase; 100 101 104 protected String link; 105 106 109 protected String defaultEnvelopeTag; 110 111 114 protected String envelopeTag; 115 116 119 protected ServiceManager manager; 120 121 124 protected CopletInstanceData copletInstanceData; 125 126 129 protected Request request; 130 131 134 protected int configuredEncoding; 135 136 139 protected String userAgent; 140 141 142 protected Parameters parameters; 143 144 147 public void service(ServiceManager manager) throws ServiceException { 148 this.manager = manager; 149 150 } 151 152 156 public void parameterize(Parameters parameters) { 157 this.defaultEnvelopeTag = parameters.getParameter(ENVELOPE_TAG_PARAMETER, null); 158 } 159 160 163 public void setup(SourceResolver resolver, 164 Map objectModel, 165 String src, 166 Parameters parameters) 167 throws ProcessingException, SAXException , IOException { 168 this.parameters = parameters; 169 this.request = ObjectModelHelper.getRequest(objectModel); 170 171 this.copletInstanceData = getInstanceData(this.manager, objectModel, parameters); 172 173 final CopletData copletData = this.copletInstanceData.getCopletData(); 174 175 final String startURI = (String )copletData.getAttribute(START_URI); 176 177 this.link = (String ) this.copletInstanceData.getTemporaryAttribute(LINK); 178 179 this.documentBase = (String ) this.copletInstanceData.getAttribute(DOCUMENT_BASE); 180 181 if (this.link == null) { 182 this.link = startURI; 183 } 184 185 if (documentBase == null) { 186 this.documentBase = this.link.substring(0, this.link.lastIndexOf('/') + 1); 187 copletInstanceData.setAttribute(DOCUMENT_BASE, this.documentBase); 188 } 189 190 this.configuredEncoding = encodingConstantFromString((String )copletData.getAttribute("encoding")); 191 this.userAgent = (String )copletData.getAttribute("user-agent"); 192 this.envelopeTag = parameters.getParameter(ENVELOPE_TAG_PARAMETER, this.defaultEnvelopeTag); 193 194 if (envelopeTag == null) { 195 throw new ProcessingException("Can not initialize ProxyTransformer - sitemap parameter 'envelope-tag' missing"); 196 } 197 } 198 199 202 public void recycle() { 203 super.recycle(); 204 this.envelopeTag = null; 205 this.userAgent = null; 206 this.documentBase = null; 207 this.link = null; 208 this.request = null; 209 this.parameters = null; 210 this.copletInstanceData = null; 211 } 212 213 216 public void startElement( 217 String uri, 218 String name, 219 String raw, 220 Attributes attributes) 221 throws SAXException { 222 super.startElement(uri, name, raw, attributes); 223 224 if (name.equalsIgnoreCase(this.envelopeTag)) { 225 processRequest(); 227 } 229 } 230 231 234 protected void processRequest() throws SAXException { 235 try { 236 String remoteURI = null; 237 try { 238 remoteURI = resolveURI(link, documentBase); 239 } catch (MalformedURLException ex) { 240 throw new SAXException (ex); 241 } 242 boolean firstparameter = true; 243 boolean post = ("POST".equals(request.getMethod())); 244 int pos = remoteURI.indexOf('?'); 245 final StringBuffer query = new StringBuffer (); 246 if ( pos != -1 ) { 247 if ( !post ) { 248 query.append('?'); 249 } 250 query.append(remoteURI.substring(pos+1)); 251 firstparameter = true; 252 remoteURI = remoteURI.substring(0, pos); 253 } 254 255 final Enumeration enumeration = request.getParameterNames(); 258 while (enumeration.hasMoreElements()) { 259 String paramName = (String ) enumeration.nextElement(); 260 261 if (!paramName.startsWith("cocoon-portal-")) { 262 String [] paramValues = request.getParameterValues(paramName); 263 for (int i = 0; i < paramValues.length; i++) { 264 firstparameter = this.appendParameter(query, firstparameter, post, paramName, paramValues[i]); 265 } 266 } 267 } 268 269 final String [] names = this.parameters.getNames(); 271 for(int i=0; i<names.length; i++) { 272 if ( names[i].startsWith("add:") ) { 273 final String value = this.parameters.getParameter(names[i]); 274 if ( value != null && value.trim().length() > 0 ) { 275 final String pName = names[i].substring(4); 276 firstparameter = this.appendParameter(query, firstparameter, post, pName, value.trim()); 277 } 278 } 279 280 } 281 282 Document result = null; 283 try { 284 do { 285 if ( this.getLogger().isDebugEnabled() ) { 286 this.getLogger().debug("Invoking '" + remoteURI + query.toString() +"', post="+post); 287 } 288 HttpURLConnection connection = 289 connect(request, remoteURI, query.toString(), post); 290 remoteURI = checkForRedirect(connection, documentBase); 291 292 if (remoteURI == null) { 293 result = readXML(connection); 294 remoteURI = checkForRedirect(result, documentBase); 295 } 296 297 } 298 while (remoteURI != null); 299 } catch (IOException ex) { 300 throw new SAXException ( 301 "Failed to retrieve remoteURI " + remoteURI, 302 ex); 303 } 304 305 XMLUtils.stripDuplicateAttributes(result, null); 306 307 DOMStreamer streamer = new DOMStreamer(); 308 streamer.setContentHandler(contentHandler); 309 streamer.stream(result.getDocumentElement()); 310 } catch (SAXException se) { 311 throw se; 312 } catch (Exception ex) { 313 throw new SAXException (ex); 314 } 315 } 316 317 protected boolean appendParameter(StringBuffer buffer, 318 boolean firstparameter, 319 boolean post, 320 String name, 321 String value) 322 throws UnsupportedEncodingException { 323 if (firstparameter) { 324 if (!post) { 325 buffer.append('?'); 326 } 327 firstparameter = false; 328 } else { 329 buffer.append('&'); 330 } 331 332 buffer.append(NetUtils.encode(name, "utf-8")); 333 buffer.append('='); 334 buffer.append(NetUtils.encode(value, "utf-8")); 335 336 return firstparameter; 337 } 338 345 protected String checkForRedirect( 346 HttpURLConnection connection, 347 String documentBase) 348 throws IOException { 349 350 if (connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM 351 || connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP) { 352 353 String newURI = (connection.getHeaderField("location")); 354 355 int index_semikolon = newURI.indexOf(";"); 356 int index_question = newURI.indexOf("?"); 357 358 if ((index_semikolon > -1)) { 359 String sessionToken = 360 newURI.substring( 361 index_semikolon + 1, 362 (index_question == -1 363 ? newURI.length() 364 : index_question)); 365 this.copletInstanceData.getPersistentAspectData().put( 366 SESSIONTOKEN, 367 sessionToken); 368 } 369 newURI = resolveURI(newURI, documentBase); 370 return newURI; 371 } 372 return null; 373 } 374 375 382 protected String checkForRedirect(Document doc, String documentBase) 383 throws MalformedURLException { 384 Element htmlElement = doc.getDocumentElement(); 385 NodeList headList = htmlElement.getElementsByTagName("head"); 386 if (headList.getLength() <= 0) { 387 return null; 388 } 389 390 Element headElement = (Element ) headList.item(0); 391 NodeList metaList = headElement.getElementsByTagName("meta"); 392 for (int i = 0; i < metaList.getLength(); i++) { 393 Element metaElement = (Element ) metaList.item(i); 394 String httpEquiv = metaElement.getAttribute("http-equiv"); 395 if ("refresh".equalsIgnoreCase(httpEquiv)) { 396 String content = metaElement.getAttribute("content"); 397 if (content != null) { 398 String time = 399 content.substring(0, content.indexOf(';')); 400 try { 401 if (Integer.parseInt(time) > 10) { 402 getLogger().warn( 403 "Redirects with refresh time longer than 10 seconds (" 404 + time 405 + " seconds) will be ignored!"); 406 return null; 407 } 408 } 409 catch (NumberFormatException ex) { 410 getLogger().warn( 411 "Failed to convert refresh time from redirect to integer: " 412 + time); 413 return null; 414 } 415 416 String newURI = 417 content.substring(content.indexOf('=') + 1); 418 419 int index_semikolon = newURI.indexOf(";"); 420 int index_question = newURI.indexOf("?"); 421 422 if ((index_semikolon > -1)) { 423 String sessionToken = 424 newURI.substring( 425 index_semikolon + 1, 426 (index_question == -1 427 ? newURI.length() 428 : index_question)); 429 this.copletInstanceData.getPersistentAspectData().put( 430 SESSIONTOKEN, 431 sessionToken); 432 } 433 newURI = resolveURI(newURI, documentBase); 434 return newURI; 435 } 436 } 437 } 438 return null; 439 } 440 441 446 protected Document readXML(HttpURLConnection connection) 447 throws SAXException { 448 try { 449 int charEncoding = configuredEncoding; 450 451 String contentType = connection.getHeaderField("Content-Type"); 452 int begin = contentType.indexOf("charset="); 453 int end = -1; 454 if (begin > -1) { 455 begin += "charset=".length(); 456 end = contentType.indexOf(';', begin); 457 if (end == -1) { 458 end = contentType.length(); 459 } 460 String charset = contentType.substring(begin, end); 461 charEncoding = encodingConstantFromString(charset); 462 } 463 464 InputStream stream = connection.getInputStream(); 465 Tidy tidy = new Tidy(); 467 tidy.setXmlOut(true); 468 469 tidy.setCharEncoding(charEncoding); 470 tidy.setXHTML(true); 471 472 tidy.setShowWarnings(this.getLogger().isWarnEnabled()); 474 tidy.setQuiet(!this.getLogger().isInfoEnabled()); 476 StringWriter stringWriter = new StringWriter (); 478 PrintWriter errorWriter = new PrintWriter (stringWriter); 480 tidy.setErrout(errorWriter); 481 Document doc = tidy.parseDOM(new BufferedInputStream (stream), null); 483 errorWriter.flush(); 484 errorWriter.close(); 485 return doc; 486 } catch (Exception ex) { 487 throw new SAXException (ex); 488 } 489 } 490 491 496 private int encodingConstantFromString(String encoding) { 497 if ("ISO8859_1".equalsIgnoreCase(encoding)) { 498 return Configuration.LATIN1; 499 } 500 else if ("UTF-8".equalsIgnoreCase(encoding)) { 501 return Configuration.UTF8; 502 } 503 else { 504 return Configuration.LATIN1; 505 } 506 } 507 508 519 protected HttpURLConnection connect( 520 Request request, 521 String uri, 522 String query, 523 boolean post) 524 throws IOException { 525 526 String cookie = (String ) copletInstanceData.getAttribute(COOKIE); 527 528 if (!post) { 529 uri = uri + query; 530 } 531 532 URL url = new URL (uri); 533 534 HttpURLConnection connection = (HttpURLConnection ) url.openConnection(); 535 536 connection.setInstanceFollowRedirects(false); 537 538 connection.setRequestMethod(request.getMethod()); 539 connection.setRequestProperty( 540 "User-Agent", 541 (userAgent != null) ? userAgent : request.getHeader("User-Agent")); 542 543 connection.setRequestProperty( 544 "Accept-Language", 545 request.getHeader("Accept-Language")); 546 547 if (cookie != null) { 548 connection.setRequestProperty(COOKIE, cookie); 549 } 550 551 if (post) { 552 connection.setDoOutput(true); 553 connection.setRequestProperty( 554 "Content-Type", 555 "application/x-www-form-urlencoded"); 556 connection.setRequestProperty( 557 "Content-Length", 558 String.valueOf(query.length())); 559 } 560 561 connection.connect(); 562 563 if (post) { 564 PrintWriter out = new PrintWriter (connection.getOutputStream()); 565 out.print(query); 566 out.close(); 567 } 568 569 copletInstanceData.setAttribute( 570 COOKIE, 571 connection.getHeaderField(COOKIE)); 572 documentBase = uri.substring(0, uri.lastIndexOf('/') + 1); 573 copletInstanceData.setAttribute(DOCUMENT_BASE, documentBase); 574 return connection; 575 } 576 577 584 public static String resolveURI(String uri, String documentBase) 585 throws MalformedURLException { 586 587 if (uri == null) { 588 throw new IllegalArgumentException ("URI to be resolved must not be null!"); 589 } 590 591 if (uri.indexOf("://") > -1) { 592 return uri; 593 } 594 595 if (documentBase == null) { 596 throw new IllegalArgumentException ("Documentbase String must not be null!"); 597 } 598 599 if (uri.startsWith("./")) { 601 uri = uri.substring(2); 602 } 603 604 URL documentBaseURL = new URL (documentBase); 605 606 if (uri.startsWith("/")) { 608 return documentBaseURL.getProtocol() 609 + "://" 610 + documentBaseURL.getAuthority() 611 + uri; 612 } 613 return documentBaseURL.toExternalForm() + uri; 614 } 615 616 public static CopletInstanceData getInstanceData(ServiceManager manager, 617 String copletID, 618 String portalName) 619 throws ProcessingException { 620 PortalService portalService = null; 621 try { 622 portalService = (PortalService) manager.lookup(PortalService.ROLE); 623 624 ProfileManager profileManager = portalService.getComponentManager().getProfileManager(); 625 CopletInstanceData data = profileManager.getCopletInstanceData(copletID); 626 return data; 627 } catch (ServiceException e) { 628 throw new ProcessingException("Error getting portal service.", e); 629 } finally { 630 manager.release(portalService); 631 } 632 } 633 634 642 public static CopletInstanceData getInstanceData(ServiceManager manager, 643 Map objectModel, 644 Parameters parameters) 645 throws ProcessingException { 646 647 PortalService portalService = null; 648 try { 649 portalService = (PortalService) manager.lookup(PortalService.ROLE); 650 651 String copletId = null; 653 Map context = (Map ) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 654 if (context != null) { 655 copletId = (String ) context.get(Constants.COPLET_ID_KEY); 656 if (copletId == null) { 657 throw new ProcessingException("copletId must be passed as parameter or in the object model within the parent context."); 658 } 659 } else { 660 try { 661 copletId = parameters.getParameter(COPLET_ID_PARAM); 662 663 } catch (ParameterException e) { 664 throw new ProcessingException("copletId and portalName must be passed as parameter or in the object model within the parent context."); 665 } 666 } 667 return portalService.getComponentManager().getProfileManager().getCopletInstanceData(copletId); 668 } catch (ServiceException e) { 669 throw new ProcessingException("Error getting portal service.", e); 670 } finally { 671 manager.release(portalService); 672 } 673 } 674 675 } 676 | Popular Tags |