1 16 package org.apache.cocoon.components.pipeline; 17 18 import java.io.ByteArrayOutputStream ; 19 import java.io.IOException ; 20 import java.net.SocketException ; 21 import java.util.ArrayList ; 22 import java.util.Iterator ; 23 import java.util.NoSuchElementException ; 24 import java.util.StringTokenizer ; 25 26 import org.apache.avalon.excalibur.pool.Recyclable; 27 import org.apache.avalon.framework.component.Component; 28 import org.apache.avalon.framework.component.ComponentException; 29 import org.apache.avalon.framework.component.ComponentManager; 30 import org.apache.avalon.framework.component.ComponentSelector; 31 import org.apache.avalon.framework.logger.AbstractLogEnabled; 32 import org.apache.avalon.framework.parameters.ParameterException; 33 import org.apache.avalon.framework.parameters.Parameterizable; 34 import org.apache.avalon.framework.parameters.Parameters; 35 import org.apache.cocoon.ConnectionResetException; 36 import org.apache.cocoon.ProcessingException; 37 import org.apache.cocoon.components.CocoonComponentManager; 38 import org.apache.cocoon.environment.Environment; 39 import org.apache.cocoon.environment.ObjectModelHelper; 40 import org.apache.cocoon.environment.Response; 41 import org.apache.cocoon.generation.Generator; 42 import org.apache.cocoon.reading.Reader; 43 import org.apache.cocoon.serialization.Serializer; 44 import org.apache.cocoon.sitemap.SitemapErrorHandler; 45 import org.apache.cocoon.sitemap.SitemapModelComponent; 46 import org.apache.cocoon.transformation.Transformer; 47 import org.apache.cocoon.util.location.Locatable; 48 import org.apache.cocoon.util.location.Location; 49 import org.apache.cocoon.xml.SaxBuffer; 50 import org.apache.cocoon.xml.XMLConsumer; 51 import org.apache.cocoon.xml.XMLProducer; 52 import org.apache.excalibur.source.SourceValidity; 53 import org.xml.sax.SAXException ; 54 55 62 public abstract class AbstractProcessingPipeline 63 extends AbstractLogEnabled 64 implements ProcessingPipeline, Parameterizable, Recyclable { 65 66 protected Generator generator; 68 protected Parameters generatorParam; 69 protected String generatorSource; 70 protected ComponentSelector generatorSelector; 71 72 protected ArrayList transformers = new ArrayList (); 74 protected ArrayList transformerParams = new ArrayList (); 75 protected ArrayList transformerSources = new ArrayList (); 76 protected ArrayList transformerSelectors = new ArrayList (); 77 78 protected Serializer serializer; 80 protected Parameters serializerParam; 81 protected String serializerSource; 82 protected String serializerMimeType; 83 protected String sitemapSerializerMimeType; 84 protected OutputComponentSelector serializerSelector; 85 86 protected Reader reader; 88 protected Parameters readerParam; 89 protected String readerSource; 90 protected String readerMimeType; 91 protected String sitemapReaderMimeType; 92 protected OutputComponentSelector readerSelector; 93 94 private SitemapErrorHandler errorHandler; 96 private ProcessingPipeline errorPipeline; 97 98 99 private boolean prepared; 100 101 105 protected XMLConsumer lastConsumer; 106 107 108 protected ComponentManager manager; 109 110 111 protected ComponentManager newManager; 112 113 114 protected Parameters configuration; 115 116 117 protected long configuredExpires; 118 119 120 protected int configuredOutputBufferSize; 121 122 123 protected Parameters parameters; 124 125 126 protected long expires; 127 128 129 protected int outputBufferSize; 130 131 134 public void compose(ComponentManager manager) 135 throws ComponentException { 136 this.manager = manager; 137 this.newManager = manager; 138 } 139 140 143 public void recompose(ComponentManager manager) 144 throws ComponentException { 145 this.newManager = manager; 146 } 147 148 151 public void parameterize(Parameters params) 152 throws ParameterException { 153 this.configuration = params; 154 final String expiresValue = params.getParameter("expires", null); 155 if (expiresValue != null) { 156 this.configuredExpires = parseExpires(expiresValue); 157 } 158 this.configuredOutputBufferSize = params.getParameterAsInteger("outputBufferSize", -1); 159 } 160 161 164 public void setup(Parameters params) { 165 this.parameters = params; 166 final String expiresValue = params.getParameter("expires", null); 167 if (expiresValue != null) { 168 this.expires = parseExpires(expiresValue); 169 } else { 170 this.expires = this.configuredExpires; 171 } 172 this.outputBufferSize = params.getParameterAsInteger("outputBufferSize", 173 this.configuredOutputBufferSize); 174 } 175 176 182 public void release() { 183 try { 184 CocoonComponentManager.removeFromAutomaticRelease(this); 185 } catch (ProcessingException e) { 186 getLogger().error("Unable to release self from automatic release.", e); 188 } 189 } 190 191 195 public void informBranchPoint() { 196 } 198 199 202 public Generator getGenerator() { 203 return this.generator; 204 } 205 206 218 public void setGenerator(String role, String source, Parameters param, Parameters hintParam) 219 throws ProcessingException { 220 if (this.generator != null) { 221 throw new ProcessingException ("Generator already set. Cannot set generator '" + role + "'", 222 getLocation(param)); 223 } 224 if (this.reader != null) { 225 throw new ProcessingException ("Reader already set. Cannot set generator '" + role + "'", 226 getLocation(param)); 227 } 228 try { 229 this.generatorSelector = (ComponentSelector) this.newManager.lookup(Generator.ROLE + "Selector"); 230 } catch (ComponentException ce) { 231 throw ProcessingException.throwLocated("Lookup of generator selector failed", ce, getLocation(param)); 232 } 233 try { 234 this.generator = (Generator) this.generatorSelector.select(role); 235 } catch (ComponentException ce) { 236 throw ProcessingException.throwLocated("Lookup of generator '" + role + "' failed", ce, getLocation(param)); 237 } 238 this.generatorSource = source; 239 this.generatorParam = param; 240 } 241 242 254 public void addTransformer(String role, String source, Parameters param, Parameters hintParam) 255 throws ProcessingException { 256 if (this.reader != null) { 257 throw new ProcessingException ("Reader already set. Cannot add transformer '" + role + "'", 259 getLocation(param)); 260 } 261 if (this.generator == null) { 262 throw new ProcessingException ("Must set a generator before adding transformer '" + role + "'", 263 getLocation(param)); 264 } 265 ComponentSelector selector = null; 266 try { 267 selector = (ComponentSelector) this.newManager.lookup(Transformer.ROLE + "Selector"); 268 } catch (ComponentException ce) { 269 throw ProcessingException.throwLocated("Lookup of transformer selector failed", ce, getLocation(param)); 270 } 271 try { 272 this.transformers.add(selector.select(role)); 273 this.transformerSelectors.add(selector); 274 } catch (ComponentException ce) { 275 throw ProcessingException.throwLocated("Lookup of transformer '"+role+"' failed", ce, getLocation(param)); 276 } 277 this.transformerSources.add(source); 278 this.transformerParams.add(param); 279 } 280 281 285 public void setSerializer(String role, String source, Parameters param, Parameters hintParam, String mimeType) 286 throws ProcessingException { 287 if (this.serializer != null) { 288 throw new ProcessingException ("Serializer already set. Cannot set serializer '" + role + "'", 290 getLocation(param)); 291 } 292 if (this.reader != null) { 293 throw new ProcessingException ("Reader already set. Cannot set serializer '" + role + "'", 295 getLocation(param)); 296 } 297 if (this.generator == null) { 298 throw new ProcessingException ("Must set a generator before setting serializer '" + role + "'", 299 getLocation(param)); 300 } 301 302 try { 303 this.serializerSelector = (OutputComponentSelector) this.newManager.lookup(Serializer.ROLE + "Selector"); 304 } catch (ComponentException ce) { 305 throw ProcessingException.throwLocated("Lookup of serializer selector failed", ce, getLocation(param)); 306 } 307 try { 308 this.serializer = (Serializer)serializerSelector.select(role); 309 } catch (ComponentException ce) { 310 throw ProcessingException.throwLocated("Lookup of serializer '" + role + "' failed", ce, getLocation(param)); 311 } 312 this.serializerSource = source; 313 this.serializerParam = param; 314 this.serializerMimeType = mimeType; 315 this.sitemapSerializerMimeType = serializerSelector.getMimeTypeForHint(role); 316 this.lastConsumer = this.serializer; 317 } 318 319 323 public void setReader(String role, String source, Parameters param, String mimeType) 324 throws ProcessingException { 325 if (this.reader != null) { 326 throw new ProcessingException ("Reader already set. Cannot set reader '" + role + "'", 328 getLocation(param)); 329 } 330 if (this.generator != null) { 331 throw new ProcessingException ("Generator already set. Cannot use reader '" + role + "'", 333 getLocation(param)); 334 } 335 336 try { 337 this.readerSelector = (OutputComponentSelector) this.newManager.lookup(Reader.ROLE + "Selector"); 338 } catch (ComponentException ce) { 339 throw ProcessingException.throwLocated("Lookup of reader selector failed", ce, getLocation(param)); 340 } 341 try { 342 this.reader = (Reader)readerSelector.select(role); 343 } catch (ComponentException ce) { 344 throw ProcessingException.throwLocated("Lookup of reader '"+role+"' failed", ce, getLocation(param)); 345 } 346 this.readerSource = source; 347 this.readerParam = param; 348 this.readerMimeType = mimeType; 349 this.sitemapReaderMimeType = readerSelector.getMimeTypeForHint(role); 350 } 351 352 357 public void setErrorHandler(SitemapErrorHandler errorHandler) { 358 this.errorHandler = errorHandler; 359 } 360 361 365 protected boolean checkPipeline() { 366 if (this.generator == null && this.reader == null) { 367 return false; 368 } 369 370 if (this.generator != null && this.serializer == null) { 371 return false; 372 } 373 374 return true; 375 } 376 377 380 protected void setupPipeline(Environment environment) 381 throws ProcessingException { 382 try { 383 this.generator.setup( 385 environment, 386 environment.getObjectModel(), 387 generatorSource, 388 generatorParam 389 ); 390 391 Iterator transformerItt = this.transformers.iterator(); 392 Iterator transformerSourceItt = this.transformerSources.iterator(); 393 Iterator transformerParamItt = this.transformerParams.iterator(); 394 395 while (transformerItt.hasNext()) { 396 Transformer trans = (Transformer)transformerItt.next(); 397 trans.setup(environment, 398 environment.getObjectModel(), 399 (String )transformerSourceItt.next(), 400 (Parameters)transformerParamItt.next() 401 ); 402 } 403 404 if (this.serializer instanceof SitemapModelComponent) { 405 ((SitemapModelComponent)this.serializer).setup( 406 environment, 407 environment.getObjectModel(), 408 this.serializerSource, 409 this.serializerParam 410 ); 411 } 412 } catch (Exception e) { 413 handleException(e); 414 } 415 } 416 417 420 protected void connect(Environment environment, 421 XMLProducer producer, 422 XMLConsumer consumer) 423 throws ProcessingException { 424 producer.setConsumer(consumer); 426 } 427 428 431 protected void connectPipeline(Environment environment) 432 throws ProcessingException { 433 XMLProducer prev = this.generator; 434 435 Iterator itt = this.transformers.iterator(); 436 while (itt.hasNext()) { 437 Transformer next = (Transformer) itt.next(); 438 connect(environment, prev, next); 439 prev = next; 440 } 441 442 connect(environment, prev, this.lastConsumer); 444 } 445 446 449 public boolean process(Environment environment) 450 throws ProcessingException { 451 if (!this.prepared) { 452 preparePipeline(environment); 453 } 454 455 if (this.expires != 0) { 457 Response res = ObjectModelHelper.getResponse(environment.getObjectModel()); 458 res.setDateHeader("Expires", System.currentTimeMillis() + expires); 459 res.setHeader("Cache-Control", "max-age=" + expires/1000 + ", public"); 460 if (getLogger().isDebugEnabled()) { 461 getLogger().debug("Setting a new Expires object for this resource"); 462 } 463 environment.getObjectModel().put(ObjectModelHelper.EXPIRES_OBJECT, 464 new Long (expires + System.currentTimeMillis())); 465 } 466 467 if (this.reader != null) { 468 if (checkIfModified(environment, this.reader.getLastModified())) { 469 return true; 470 } 471 472 return processReader(environment); 473 } else { 474 if (this.lastConsumer == null) { 476 this.lastConsumer = this.serializer; 477 } 478 479 connectPipeline(environment); 480 return processXMLPipeline(environment); 481 } 482 } 483 484 487 protected void preparePipeline(Environment environment) 488 throws ProcessingException { 489 if (!checkPipeline()) { 490 throw new ProcessingException("Attempted to process incomplete pipeline."); 491 } 492 493 if (this.prepared) { 494 throw new ProcessingException("Duplicate preparePipeline call caught."); 495 } 496 497 if (this.reader != null) { 498 setupReader(environment); 499 } else { 500 setupPipeline(environment); 501 } 502 this.prepared = true; 503 } 504 505 510 public void prepareInternal(Environment environment) 511 throws ProcessingException { 512 this.lastConsumer = null; 513 try { 514 preparePipeline(environment); 515 } catch (ProcessingException e) { 516 prepareInternalErrorHandler(environment, e); 517 } 518 } 519 520 523 protected void prepareInternalErrorHandler(Environment environment, ProcessingException ex) 524 throws ProcessingException { 525 if (this.errorHandler != null) { 526 try { 527 this.errorPipeline = this.errorHandler.prepareErrorPipeline(ex); 528 if (this.errorPipeline != null) { 529 this.errorPipeline.prepareInternal(environment); 530 return; 531 } 532 } catch (ProcessingException e) { 533 getLogger().error("Failed to process error handler for exception", ex); 535 throw e; 536 } catch (Exception e) { 537 getLogger().error("Failed to process error handler for exception", ex); 538 throw new ProcessingException("Failed to handle exception <" + ex.getMessage() + ">", e); 539 } 540 } else { 541 throw ex; 543 } 544 } 545 546 549 protected boolean isInternalError() { 550 return this.errorPipeline != null; 551 } 552 553 556 protected boolean processXMLPipeline(Environment environment) 557 throws ProcessingException { 558 559 setMimeTypeForSerializer(environment); 560 try { 561 if (this.lastConsumer == null) { 562 this.generator.generate(); 564 } else { 565 if (this.serializer.shouldSetContentLength()) { 566 ByteArrayOutputStream os = new ByteArrayOutputStream (); 568 this.serializer.setOutputStream(os); 569 570 this.generator.generate(); 572 environment.setContentLength(os.size()); 573 os.writeTo(environment.getOutputStream(0)); 574 } else { 575 this.serializer.setOutputStream(environment.getOutputStream(this.outputBufferSize)); 577 this.generator.generate(); 579 } 580 } 581 } catch (Exception e) { 582 handleException(e); 583 } 584 585 return true; 586 } 587 588 591 protected void setupReader(Environment environment) 592 throws ProcessingException { 593 try { 594 this.reader.setup(environment,environment.getObjectModel(),readerSource,readerParam); 595 if (readerParam.isParameter("expires")) { 597 this.expires = readerParam.getParameterAsLong("expires"); 599 } 600 } catch (Exception e){ 601 handleException(e); 602 } 603 } 604 605 609 protected void setMimeTypeForReader(Environment environment) 610 throws ProcessingException { 611 if ( this.readerMimeType != null ) { 620 environment.setContentType(this.readerMimeType); 622 } else if ( this.sitemapReaderMimeType != null ) { 623 environment.setContentType(this.sitemapReaderMimeType); 625 } else { 626 final String mimeType = this.reader.getMimeType(); 628 if (mimeType != null) { 629 environment.setContentType(mimeType); 630 } 631 } 634 } 635 636 640 protected void setMimeTypeForSerializer(Environment environment) 641 throws ProcessingException { 642 if (this.lastConsumer == null) { 643 environment.setContentType("text/xml"); 645 } else { 646 if (serializerMimeType != null) { 649 environment.setContentType(serializerMimeType); 651 } else if (sitemapSerializerMimeType != null) { 652 environment.setContentType(sitemapSerializerMimeType); 654 } else { 655 String mimeType = this.serializer.getMimeType(); 657 if (mimeType != null) { 658 environment.setContentType (mimeType); 659 } else { 660 final String message = "Unable to determine MIME type for " + 662 environment.getURIPrefix() + "/" + environment.getURI(); 663 throw new ProcessingException(message); 664 } 665 } 666 } 667 } 668 669 protected boolean checkIfModified(Environment environment, 670 long lastModified) 671 throws ProcessingException { 672 if(!environment.isResponseModified(lastModified)) { 674 environment.setResponseIsNotModified(); 676 return true; 677 } 678 return false; 679 } 680 681 685 protected boolean processReader(Environment environment) 686 throws ProcessingException { 687 try { 688 this.setMimeTypeForReader(environment); 689 if (this.reader.shouldSetContentLength()) { 690 ByteArrayOutputStream os = new ByteArrayOutputStream (); 691 this.reader.setOutputStream(os); 692 this.reader.generate(); 693 environment.setContentLength(os.size()); 694 os.writeTo(environment.getOutputStream(0)); 695 } else { 696 this.reader.setOutputStream(environment.getOutputStream(this.outputBufferSize)); 697 this.reader.generate(); 698 } 699 } catch (Exception e) { 700 handleException(e); 701 } 702 703 return true; 704 } 705 706 public void recycle() { 707 this.prepared = false; 708 709 if (this.readerSelector != null) { 711 this.readerSelector.release(this.reader); 712 this.newManager.release(this.readerSelector); 713 this.readerSelector = null; 714 this.reader = null; 715 this.readerParam = null; 716 } 717 718 if (this.generatorSelector != null) { 720 this.generatorSelector.release(this.generator); 721 this.newManager.release(this.generatorSelector); 722 this.generatorSelector = null; 723 this.generator = null; 724 this.generatorParam = null; 725 } 726 727 int size = this.transformerSelectors.size(); 729 for (int i = 0; i < size; i++) { 730 final ComponentSelector selector = 731 (ComponentSelector) this.transformerSelectors.get(i); 732 selector.release((Component) this.transformers.get(i)); 733 this.newManager.release(selector); 734 } 735 this.transformerSelectors.clear(); 736 this.transformers.clear(); 737 this.transformerParams.clear(); 738 this.transformerSources.clear(); 739 740 if (this.serializerSelector != null) { 742 this.serializerSelector.release(this.serializer); 743 this.newManager.release(this.serializerSelector); 744 this.serializerSelector = null; 745 this.serializerParam = null; 746 } 747 this.serializer = null; 748 this.parameters = null; 749 this.lastConsumer = null; 750 751 this.errorHandler = null; 753 if (this.errorPipeline != null) { 754 this.errorPipeline.release(); 755 this.errorPipeline = null; 756 } 757 } 758 759 763 public boolean process(Environment environment, XMLConsumer consumer) 764 throws ProcessingException { 765 if (this.reader != null) { 766 throw new ProcessingException("Streaming of an internal pipeline is not possible with a reader."); 767 } 768 769 if (this.errorPipeline != null) { 771 return this.errorPipeline.process(environment, consumer); 772 } 773 774 SaxBuffer buffer = null; 776 this.lastConsumer = this.errorHandler == null? consumer: (buffer = new SaxBuffer()); 777 try { 778 connectPipeline(environment); 779 return processXMLPipeline(environment); 780 } catch (ProcessingException e) { 781 buffer = null; 782 return processErrorHandler(environment, e, consumer); 783 } finally { 784 if (buffer != null) { 785 try { 786 buffer.toSAX(consumer); 787 } catch (SAXException e) { 788 throw new ProcessingException("Failed to execute pipeline.", e); 789 } 790 } 791 } 792 } 793 794 protected boolean processErrorHandler(Environment environment, ProcessingException e, XMLConsumer consumer) 795 throws ProcessingException { 796 if (this.errorHandler != null) { 797 try { 798 this.errorPipeline = this.errorHandler.prepareErrorPipeline(e); 799 if (this.errorPipeline != null) { 800 this.errorPipeline.prepareInternal(environment); 801 return this.errorPipeline.process(environment, consumer); 802 } 803 } catch (Exception ignored) { 804 getLogger().debug("Exception in error handler", ignored); 805 } 806 } 807 808 throw e; 809 } 810 811 817 public SourceValidity getValidityForEventPipeline() { 818 return null; 819 } 820 821 827 public String getKeyForEventPipeline() { 828 return null; 829 } 830 831 832 835 private long parseExpires(String expire) { 836 StringTokenizer tokens = new StringTokenizer (expire); 837 838 String current = tokens.nextToken(); 840 if (current.equals("modification")) { 841 getLogger().warn("the \"modification\" keyword is not yet" + 842 " implemented. Assuming \"now\" as the base attribute"); 843 current = "now"; 844 } 845 846 if (!current.equals("now") && !current.equals("access")) { 847 getLogger().error("bad <base> attribute, Expires header will not be set"); 848 return -1; 849 } 850 851 long number = 0; 852 long modifier = 0; 853 long expires = 0; 854 855 while (tokens.hasMoreTokens()) { 856 current = tokens.nextToken(); 857 858 if (current.equals("plus")) { 860 current = tokens.nextToken(); 861 } 862 863 try { 866 number = Long.parseLong(current); 867 } catch (NumberFormatException nfe) { 868 getLogger().error("state violation: a number was expected here"); 869 return -1; 870 } 871 872 try { 874 current = tokens.nextToken(); 875 } catch (NoSuchElementException nsee) { 876 getLogger().error("State violation: expecting a modifier" + 877 " but no one found: Expires header will not be set"); 878 } 879 if (current.equals("years")) { 880 modifier = 365L * 24L * 60L * 60L * 1000L; 881 } else if (current.equals("months")) { 882 modifier = 30L * 24L * 60L * 60L * 1000L; 883 } else if (current.equals("weeks")) { 884 modifier = 7L * 24L * 60L * 60L * 1000L; 885 } else if (current.equals("days")) { 886 modifier = 24L * 60L * 60L * 1000L; 887 } else if (current.equals("hours")) { 888 modifier = 60L * 60L * 1000L; 889 } else if (current.equals("minutes")) { 890 modifier = 60L * 1000L; 891 } else if (current.equals("seconds")) { 892 modifier = 1000L; 893 } else { 894 getLogger().error("Bad modifier (" + current + 895 "): ignoring expires configuration"); 896 return -1; 897 } 898 expires += number * modifier; 899 } 900 901 return expires; 902 } 903 904 protected Location getLocation(Parameters param) { 905 Location location = null; 906 if (param instanceof Locatable) { 907 location = ((Locatable)param).getLocation(); 908 } 909 if (location == null) { 910 location = Location.UNKNOWN; 911 } 912 return location; 913 } 914 915 923 protected void handleException(Exception e) throws ProcessingException { 924 if (e instanceof SocketException ) { 926 if (e.getMessage().indexOf("reset") > 0 927 || e.getMessage().indexOf("aborted") > 0 928 || e.getMessage().indexOf("connection abort") > 0) { 929 throw new ConnectionResetException("Connection reset by peer", e); 930 } 931 } else if (e instanceof IOException ) { 932 if (e.getClass().getName().endsWith("ClientAbortException")) { 934 throw new ConnectionResetException("Connection reset by peer", e); 935 } 936 } else if (e instanceof ConnectionResetException) { 937 throw (ConnectionResetException)e; 939 } 940 941 if (this.reader == null) { 943 ArrayList locations = new ArrayList (this.transformers.size() + 2); 945 locations.add(getLocation(this.serializerParam)); 946 for (int i = this.transformerParams.size() - 1; i >= 0; i--) { 947 locations.add(getLocation((Parameters)this.transformerParams.get(i))); 948 } 949 locations.add(getLocation(this.generatorParam)); 950 951 throw ProcessingException.throwLocated("Failed to process pipeline", e, locations); 952 953 } else { 954 throw ProcessingException.throwLocated("Failed to process reader", e, getLocation(this.readerParam)); 956 } 957 } 958 } 959 | Popular Tags |