Coverage Report - org.apache.myfaces.context.PartialResponseWriterImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
PartialResponseWriterImpl
0%
0/126
0%
0/54
1.833
PartialResponseWriterImpl$StackEntry
0%
0/10
N/A
1.833
 
 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,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 
 20  
 package org.apache.myfaces.context;
 21  
 
 22  
 import java.io.IOException;
 23  
 import java.io.Writer;
 24  
 import java.util.ArrayList;
 25  
 import java.util.List;
 26  
 
 27  
 import javax.faces.component.UIComponent;
 28  
 import javax.faces.context.PartialResponseWriter;
 29  
 import javax.faces.context.ResponseWriter;
 30  
 
 31  
 import org.apache.myfaces.util.CDataEndEscapeFilterWriter;
 32  
 import org.apache.myfaces.util.IllegalXmlCharacterFilterWriter;
 33  
 
 34  
 /**
 35  
  * <p/>
 36  
  * Double buffering partial response writer
 37  
  * to take care if embedded CDATA blocks in update delete etc...
 38  
  * <p/>
 39  
  * According to the spec 13.4.4.1 Writing The Partial Response
 40  
  * implementations have to take care to handle nested cdata blocks properly
 41  
  * <p/>
 42  
  * This means we cannot allow nested CDATA
 43  
  * according to the xml spec http://www.w3.org/TR/REC-xml/#sec-cdata-sect
 44  
  * everything within a CDATA block is unparsed except for ]]>
 45  
  * <p/>
 46  
  * Now we have following problem, that CDATA inserts can happen everywhere
 47  
  * not only within the CDATA instructions.
 48  
  * <p/>
 49  
  * What we have to do now is to double buffer CDATA blocks until their end
 50  
  * and also!!! parse their content for CDATA embedding and replace it with an escaped end sequence.
 51  
  * <p/>
 52  
  * Now parsing CDATA embedding is a little bit problematic in case of PPR because
 53  
  * it can happen that someone simply adds a CDATA in a javascript string or somewhere else.
 54  
  * Because he/she is not aware that we wrap the entire content into CDATA.
 55  
  * Simply encoding and decoding of the CDATA is similarly problematic
 56  
  * because the browser then chokes on embedded //<![CDATA[ //]]> sections
 57  
  * <p/>
 58  
  * What we do for now is to simply remove //<![CDATA[ and //]]>
 59  
  * and replace all other pending cdatas with their cdata escapes
 60  
  * ]]&gt; becomes &lt;![CDATA[]]]]&gt;&lt;![CDATA[&gt;
 61  
  * <p/>
 62  
  * If this causes problems in corner cases we also can add a second encoding step in
 63  
  * case of the cdata Javascript comment removal is not enough to cover all corner cases.
 64  
  * <p/>
 65  
  * For now I will only implement this in the impl, due to the spec stating
 66  
  * that implementations are responsible of the correct CDATA handling!
 67  
  *
 68  
  * @author Werner Punz (latest modification by $Author$)
 69  
  * @version $Revision$ $Date$
 70  
  */
 71  
 
 72  0
 public class PartialResponseWriterImpl extends PartialResponseWriter
 73  
 {
 74  
 
 75  
     class StackEntry
 76  
     {
 77  
         ResponseWriter writer;
 78  
         Writer _doubleBuffer;
 79  
 
 80  
         StackEntry(ResponseWriter writer, Writer doubleBuffer)
 81  0
         {
 82  0
             this.writer = writer;
 83  0
             _doubleBuffer = doubleBuffer;
 84  0
         }
 85  
 
 86  
         public ResponseWriter getWriter()
 87  
         {
 88  0
             return writer;
 89  
         }
 90  
 
 91  
         public void setWriter(ResponseWriter writer)
 92  
         {
 93  0
             this.writer = writer;
 94  0
         }
 95  
 
 96  
         public Writer getDoubleBuffer()
 97  
         {
 98  0
             return _doubleBuffer;
 99  
         }
 100  
 
 101  
         public void setDoubleBuffer(Writer doubleBuffer)
 102  
         {
 103  0
             _doubleBuffer = doubleBuffer;
 104  0
         }
 105  
     }
 106  
 
 107  0
     ResponseWriter _cdataDoubleBufferWriter = null;
 108  0
     Writer _doubleBuffer = null;
 109  0
     List<StackEntry> _nestingStack = new ArrayList<StackEntry>(4);
 110  
 
 111  
     public PartialResponseWriterImpl(ResponseWriter writer)
 112  
     {
 113  0
         super(writer.cloneWithWriter(new IllegalXmlCharacterFilterWriter(writer)));
 114  0
     }
 115  
 
 116  
     @Override
 117  
     public void startCDATA() throws IOException
 118  
     {
 119  0
         if (!isDoubleBufferEnabled())
 120  
         {
 121  0
             super.startCDATA();
 122  
         }
 123  
         else
 124  
         {
 125  0
             _cdataDoubleBufferWriter.write("<![CDATA[");
 126  
         }
 127  0
         openDoubleBuffer();
 128  0
     }
 129  
 
 130  
     private void openDoubleBuffer()
 131  
     {
 132  0
         _doubleBuffer = new CDataEndEscapeFilterWriter(_cdataDoubleBufferWriter == null ? 
 133  
                 this.getWrapped() : _cdataDoubleBufferWriter );
 134  0
         _cdataDoubleBufferWriter = getWrapped().cloneWithWriter(_doubleBuffer);
 135  
 
 136  0
         StackEntry entry = new StackEntry(_cdataDoubleBufferWriter, _doubleBuffer);
 137  
 
 138  0
         _nestingStack.add(0, entry);
 139  0
     }
 140  
 
 141  
     @Override
 142  
     public void endCDATA() throws IOException
 143  
     {
 144  0
         closeDoubleBuffer(false);
 145  0
         if (isDoubleBufferEnabled())
 146  
         {
 147  0
             _cdataDoubleBufferWriter.write("]]>");
 148  
         }
 149  
         else
 150  
         {
 151  0
             super.endCDATA();
 152  
         }
 153  0
     }
 154  
 
 155  
     /**
 156  
      * Close double buffer condition
 157  
      * This does either a normal close or a force
 158  
      * close in case of a force close
 159  
      * the entire buffer  is pushed with the post processing
 160  
      * operations into the originating writer
 161  
      *
 162  
      * @param force if set to true the close is a forced close which in any condition
 163  
      *              immediately pushes the buffer content into our writer with a pre operation
 164  
      *              done upfront, in case of a false, the buffer is only swept out if our
 165  
      *              internal CDATA nesting counter is at the nesting depth 1
 166  
      * @throws IOException
 167  
      */
 168  
     private void closeDoubleBuffer(boolean force) throws IOException
 169  
     {
 170  0
         if (!isDoubleBufferEnabled())
 171  
         {
 172  0
             return;
 173  
         }
 174  
         /*
 175  
         * if a force close is issued we reset the condition
 176  
         * to 1 to reach the underlying closing block
 177  
         */
 178  
 
 179  0
         if (force)
 180  
         {
 181  0
             while (!_nestingStack.isEmpty())
 182  
             {
 183  0
                 popAndEncodeCurrentStackEntry();
 184  
 
 185  
             }
 186  
         }
 187  
         else
 188  
         {
 189  0
             popAndEncodeCurrentStackEntry();
 190  
         }
 191  0
     }
 192  
 
 193  
     private void popAndEncodeCurrentStackEntry() throws IOException
 194  
     {
 195  0
         _nestingStack.remove(0);
 196  0
         StackEntry parent = (_nestingStack.isEmpty()) ? null : _nestingStack.get(0);
 197  0
         if (parent != null)
 198  
         {
 199  0
             _cdataDoubleBufferWriter = parent.getWriter();
 200  0
             _doubleBuffer = parent.getDoubleBuffer();
 201  
         }
 202  
         else
 203  
         {
 204  0
             _cdataDoubleBufferWriter = null;
 205  0
             _doubleBuffer = null;
 206  
         }
 207  0
     }
 208  
 
 209  
     //--- we need to override ppr specifics to cover the case
 210  
 
 211  
     @Override
 212  
     public void endInsert() throws IOException
 213  
     {
 214  
         //we use a force close here to fix possible user CDATA corrections
 215  
         //under normal conditions the force close just processes the same
 216  
         //the underlying close cdata does, but nevertheless
 217  
         //it is better to have an additional layer of fixup
 218  0
         closeDoubleBuffer(true);
 219  0
         super.endInsert();
 220  0
     }
 221  
 
 222  
     @Override
 223  
     public void endUpdate() throws IOException
 224  
     {
 225  
         //we use a force close here to fix possible user CDATA corrections
 226  
         //under normal conditions the force close just processes the same
 227  
         //the underlying close cdata does, but nevertheless
 228  
         //it is better to have an additional layer of fixup
 229  0
         closeDoubleBuffer(true);
 230  0
         super.endUpdate();    //To change body of overridden methods use File | Settings | File Templates.
 231  0
     }
 232  
 
 233  
     @Override
 234  
     public void endExtension() throws IOException
 235  
     {
 236  
         //we use a force close here to fix possible user CDATA corrections
 237  
         //under normal conditions the force close just processes the same
 238  
         //the underlying close cdata does, but nevertheless
 239  
         //it is better to have an additional layer of fixup
 240  0
         closeDoubleBuffer(true);
 241  0
         super.endExtension();    //To change body of overridden methods use File | Settings | File Templates.
 242  0
     }
 243  
 
 244  
     @Override
 245  
     public void endEval() throws IOException
 246  
     {
 247  
         //we use a force close here to fix possible user CDATA corrections
 248  
         //under normal conditions the force close just processes the same
 249  
         //the underlying close cdata does, but nevertheless
 250  
         //it is better to have an additional layer of fixup
 251  0
         closeDoubleBuffer(true);
 252  0
         super.endEval();    //To change body of overridden methods use File | Settings | File Templates.
 253  0
     }
 254  
 
 255  
     @Override
 256  
     public void endError() throws IOException
 257  
     {
 258  
         //we use a force close here to fix possible user CDATA corrections
 259  
         //under normal conditions the force close just processes the same
 260  
         //the underlying close cdata does, but nevertheless
 261  
         //it is better to have an additional layer of fixup
 262  0
         closeDoubleBuffer(true);
 263  0
         super.endError();    //To change body of overridden methods use File | Settings | File Templates.
 264  0
     }
 265  
 
 266  
     //--- optional delegation method ---
 267  
 
 268  
     @Override
 269  
     public void endElement(String name) throws IOException
 270  
     {
 271  0
         if (isDoubleBufferEnabled())
 272  
         {
 273  0
             _cdataDoubleBufferWriter.endElement(name);
 274  
         }
 275  
         else
 276  
         {
 277  0
             super.endElement(name);
 278  
         }
 279  0
     }
 280  
 
 281  
     @Override
 282  
     public void writeComment(Object comment) throws IOException
 283  
     {
 284  0
         if (isDoubleBufferEnabled())
 285  
         {
 286  0
             _cdataDoubleBufferWriter.writeComment(comment);
 287  
         }
 288  
         else
 289  
         {
 290  0
             super.writeComment(comment);
 291  
         }
 292  0
     }
 293  
 
 294  
     private boolean isDoubleBufferEnabled()
 295  
     {
 296  0
         return !_nestingStack.isEmpty();
 297  
     }
 298  
 
 299  
     @Override
 300  
     public void startElement(String name, UIComponent component) throws IOException
 301  
     {
 302  0
         if (isDoubleBufferEnabled())
 303  
         {
 304  0
             _cdataDoubleBufferWriter.startElement(name, component);
 305  
         }
 306  
         else
 307  
         {
 308  0
             super.startElement(name, component);
 309  
         }
 310  0
     }
 311  
 
 312  
     @Override
 313  
     public void writeText(Object text, String property) throws IOException
 314  
     {
 315  0
         if (isDoubleBufferEnabled())
 316  
         {
 317  0
             _cdataDoubleBufferWriter.writeText(text, property);
 318  
         }
 319  
         else
 320  
         {
 321  0
             super.writeText(text, property);
 322  
         }
 323  0
     }
 324  
 
 325  
     @Override
 326  
     public void writeText(char[] text, int off, int len) throws IOException
 327  
     {
 328  0
         if (isDoubleBufferEnabled())
 329  
         {
 330  0
             _cdataDoubleBufferWriter.writeText(text, off, len);
 331  
         }
 332  
         else
 333  
         {
 334  0
             super.writeText(text, off, len);
 335  
         }
 336  0
     }
 337  
 
 338  
     @Override
 339  
     public void write(char[] cbuf, int off, int len) throws IOException
 340  
     {
 341  0
         if (isDoubleBufferEnabled())
 342  
         {
 343  0
             _cdataDoubleBufferWriter.write(cbuf, off, len);
 344  
         }
 345  
         else
 346  
         {
 347  0
             super.write(cbuf, off, len);
 348  
         }
 349  0
     }
 350  
 
 351  
     @Override
 352  
     public ResponseWriter cloneWithWriter(Writer writer)
 353  
     {
 354  0
         return super.cloneWithWriter(writer);
 355  
     }
 356  
 
 357  
     @Override
 358  
     public void writeURIAttribute(String name, Object value, String property) throws IOException
 359  
     {
 360  0
         if (isDoubleBufferEnabled())
 361  
         {
 362  0
             _cdataDoubleBufferWriter.writeURIAttribute(name, value, property);
 363  
         }
 364  
         else
 365  
         {
 366  0
             super.writeURIAttribute(name, value, property);
 367  
         }
 368  0
     }
 369  
 
 370  
     @Override
 371  
     public void close() throws IOException
 372  
     {
 373  
         //in case of a close
 374  
         //we have a user error of a final CDATA block
 375  
         //we do some error correction here
 376  
         //since a close is issued we do not care about
 377  
         //a proper closure of the cdata block here anymore
 378  0
         if (isDoubleBufferEnabled())
 379  
         {
 380  
             //we have to properly close all nested cdata stacks
 381  
             //end end our cdata block if open
 382  0
             closeDoubleBuffer(true);
 383  
 
 384  0
             super.endCDATA();
 385  
         }
 386  0
         super.close();
 387  0
     }
 388  
 
 389  
     @Override
 390  
     public void flush() throws IOException
 391  
     {
 392  0
         if (isDoubleBufferEnabled())
 393  
         {
 394  0
             _cdataDoubleBufferWriter.flush();
 395  
         }
 396  0
         super.flush();
 397  0
     }
 398  
 
 399  
     @Override
 400  
     public void writeAttribute(String name, Object value, String property) throws IOException
 401  
     {
 402  0
         if (isDoubleBufferEnabled())
 403  
         {
 404  0
             _cdataDoubleBufferWriter.writeAttribute(name, value, property);
 405  
         }
 406  
         else
 407  
         {
 408  0
             super.writeAttribute(name, value, property);
 409  
         }
 410  0
     }
 411  
 
 412  
     @Override
 413  
     public void writeText(Object object, UIComponent component, String string) throws IOException
 414  
     {
 415  0
         if (isDoubleBufferEnabled())
 416  
         {
 417  0
             _cdataDoubleBufferWriter.writeText(object, component, string);
 418  
         }
 419  
         else
 420  
         {
 421  0
             super.writeText(object, component, string);
 422  
         }
 423  0
     }
 424  
 
 425  
     @Override
 426  
     public Writer append(char c) throws IOException
 427  
     {
 428  0
         if (isDoubleBufferEnabled())
 429  
         {
 430  0
             _cdataDoubleBufferWriter.append(c);
 431  0
             return this;
 432  
         }
 433  
         else
 434  
         {
 435  0
             return super.append(c);
 436  
         }
 437  
     }
 438  
 
 439  
     @Override
 440  
     public Writer append(CharSequence csq, int start, int end) throws IOException
 441  
     {
 442  0
         if (isDoubleBufferEnabled())
 443  
         {
 444  0
             _cdataDoubleBufferWriter.append(csq, start, end);
 445  0
             return this;
 446  
         }
 447  
         else
 448  
         {
 449  0
             return super.append(csq, start, end);
 450  
         }
 451  
     }
 452  
 
 453  
     @Override
 454  
     public Writer append(CharSequence csq) throws IOException
 455  
     {
 456  0
         if (isDoubleBufferEnabled())
 457  
         {
 458  0
             _cdataDoubleBufferWriter.append(csq);
 459  0
             return this;
 460  
         }
 461  
         else
 462  
         {
 463  0
             return super.append(csq);
 464  
         }
 465  
     }
 466  
 
 467  
     @Override
 468  
     public void write(char[] cbuf) throws IOException
 469  
     {
 470  0
         if (isDoubleBufferEnabled())
 471  
         {
 472  0
             _cdataDoubleBufferWriter.write(cbuf);
 473  
         }
 474  
         else
 475  
         {
 476  0
             super.write(cbuf);
 477  
         }
 478  0
     }
 479  
 
 480  
     @Override
 481  
     public void write(int c) throws IOException
 482  
     {
 483  0
         if (isDoubleBufferEnabled())
 484  
         {
 485  0
             _cdataDoubleBufferWriter.write(c);
 486  
         }
 487  
         else
 488  
         {
 489  0
             super.write(c);
 490  
         }
 491  0
     }
 492  
 
 493  
     @Override
 494  
     public void write(String str, int off, int len) throws IOException
 495  
     {
 496  0
         if (isDoubleBufferEnabled())
 497  
         {
 498  0
             _cdataDoubleBufferWriter.write(str, off, len);
 499  
         }
 500  
         else
 501  
         {
 502  0
             super.write(str, off, len);
 503  
         }
 504  0
     }
 505  
 
 506  
     @Override
 507  
     public void write(String str) throws IOException
 508  
     {
 509  0
         if (isDoubleBufferEnabled())
 510  
         {
 511  0
             _cdataDoubleBufferWriter.write(str);
 512  
         }
 513  
         else
 514  
         {
 515  0
             super.write(str);
 516  
         }
 517  0
     }
 518  
 }