KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > quercus > lib > OutputModule


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.quercus.lib;
31
32 import com.caucho.quercus.annotation.Optional;
33 import com.caucho.quercus.env.*;
34 import com.caucho.quercus.module.AbstractQuercusModule;
35 import com.caucho.quercus.module.ModuleStartupListener;
36 import com.caucho.util.L10N;
37 import com.caucho.vfs.StreamImplOutputStream;
38 import com.caucho.vfs.TempStream;
39
40 import java.io.IOException JavaDoc;
41 import java.io.OutputStream JavaDoc;
42 import java.util.HashMap JavaDoc;
43 import java.util.Map JavaDoc;
44 import java.util.logging.Logger JavaDoc;
45 import java.util.zip.DeflaterOutputStream JavaDoc;
46 import java.util.zip.GZIPOutputStream JavaDoc;
47
48 /**
49  * PHP output routines.
50  */

51 public class OutputModule extends AbstractQuercusModule
52   implements ModuleStartupListener {
53   private static final L10N L = new L10N(OutputModule.class);
54   private static final Logger JavaDoc log
55     = Logger.getLogger(OutputModule.class.getName());
56   private static final StringValue HTTP_ACCEPT_ENCODING
57     = new StringValueImpl("HTTP_ACCEPT_ENCODING");
58
59   private static final HashMap JavaDoc<String JavaDoc,StringValue> _iniMap
60     = new HashMap JavaDoc<String JavaDoc,StringValue>();
61
62   // ob_gzhandler related variables/types
63
private enum Encoding {NONE, GZIP, DEFLATE};
64
65   private static class GZOutputPair {
66     public TempStream tempStream;
67     public OutputStream outputStream;
68   }
69
70   private static HashMap JavaDoc<Env,GZOutputPair> _gzOutputPairs
71     = new HashMap JavaDoc<Env,GZOutputPair>();
72
73
74   /**
75    * Returns the default php.ini values.
76    */

77   public Map JavaDoc<String JavaDoc,StringValue> getDefaultIni()
78   {
79     return _iniMap;
80   }
81
82   public void startup(Env env)
83   {
84     String JavaDoc handlerName = env.getConfigVar("output_handler").toString();
85
86     if (! "".equals(handlerName) && env.getFunction(handlerName) != null) {
87       Callback callback = env.createCallback(new StringValueImpl(handlerName));
88
89       ob_start(env, callback, 0, true);
90     } else if (env.getConfigVar("output_buffering").toBoolean()) {
91       ob_start(env, null, 0, true);
92     }
93
94     ob_implicit_flush(env, env.getConfigVar("output_buffering").toBoolean());
95   }
96
97   /**
98    * Flushes the original output buffer.
99    */

100   public Value flush(Env env)
101   {
102     try {
103       // XXX: conflicts with dragonflycms install
104
env.getOriginalOut().flushBuffer();
105     } catch (IOException JavaDoc e) {
106     }
107
108     return NullValue.NULL;
109   }
110
111   /**
112    * Clears the output buffer.
113    */

114   public static Value ob_clean(Env env)
115   {
116     OutputBuffer ob = env.getOutputBuffer();
117
118     if (ob != null) {
119       ob.clean();
120
121       return BooleanValue.TRUE;
122     }
123     else
124       return BooleanValue.FALSE;
125   }
126
127   /**
128    * Pops the output buffer, discarding the contents.
129    */

130   public static boolean ob_end_clean(Env env)
131   {
132     OutputBuffer ob = env.getOutputBuffer();
133
134     if (ob != null) {
135       ob.clean();
136
137       Callback callback = ob.getCallback();
138
139       if (callback != null) {
140         if (callback.getCallbackName().equals("ob_gzhandler"))
141           _gzOutputPairs.remove(env);
142
143         ob.setCallback(null);
144       }
145     }
146
147     return env.popOutputBuffer();
148   }
149
150   /**
151    * Pops the output buffer.
152    */

153   public static boolean ob_end_flush(Env env)
154   {
155     return env.popOutputBuffer();
156   }
157
158   /**
159    * Returns the contents of the output buffer, emptying it afterwards.
160    */

161   public static Value ob_get_clean(Env env)
162   {
163     OutputBuffer ob = env.getOutputBuffer();
164
165     if (ob != null) {
166       Value result = ob.getContents();
167
168       ob.clean();
169
170       return result;
171     }
172     else
173       return BooleanValue.FALSE;
174   }
175
176   /**
177    * Returns the contents of the current output buffer.
178    */

179   public static Value ob_get_contents(Env env)
180   {
181     OutputBuffer ob = env.getOutputBuffer();
182
183     if (ob != null)
184       return ob.getContents();
185     else
186       return BooleanValue.FALSE;
187   }
188
189   /**
190    * Pops the output buffer and returns the contents.
191    */

192   public static Value ob_get_flush(Env env)
193   {
194     OutputBuffer ob = env.getOutputBuffer();
195
196     Value result = BooleanValue.FALSE;
197     if (ob != null) {
198       result = ob.getContents();
199     }
200
201     env.popOutputBuffer();
202
203     return result;
204   }
205
206   /**
207    * Flushes this output buffer into the next one on the stack or
208    * to the default "output buffer" if no next output buffer exists.
209    * The callback associated with this buffer is also called with
210    * appropriate parameters.
211    */

212   public static Value ob_flush(Env env)
213   {
214     OutputBuffer ob = env.getOutputBuffer();
215
216     if (ob != null) {
217       ob.flush();
218
219       return BooleanValue.TRUE;
220     }
221     else
222       return BooleanValue.FALSE;
223   }
224
225   /**
226    * Pushes the output buffer
227    */

228   public static Value ob_get_length(Env env)
229   {
230     OutputBuffer ob = env.getOutputBuffer();
231
232     if (ob != null)
233       return LongValue.create(ob.getLength());
234     else
235       return BooleanValue.FALSE;
236   }
237
238   /**
239    * Gets the nesting level of the current output buffer
240    */

241   public static Value ob_get_level(Env env)
242   {
243     OutputBuffer ob = env.getOutputBuffer();
244
245     if (ob != null)
246       return LongValue.create(ob.getLevel());
247     else
248       return LongValue.ZERO;
249   }
250
251   /**
252    * Helper recursive function that ensures the handlers are listed
253    * in the correct order in the array.
254    */

255   private static void listHandlers(OutputBuffer ob, ArrayValue handlers)
256   {
257     if (ob == null)
258       return;
259
260     listHandlers(ob.getNext(), handlers);
261
262     Callback callback = ob.getCallback();
263
264     if (callback != null)
265       handlers.put(new StringValueImpl(callback.getCallbackName()));
266     else
267       handlers.put(new StringValueImpl("default output handler"));
268   }
269   
270   /**
271    * Returns a list of all the output handlers in use.
272    */

273   public static Value ob_list_handlers(Env env)
274   {
275     OutputBuffer ob = env.getOutputBuffer();
276     ArrayValue handlers = new ArrayValueImpl();
277
278     listHandlers(ob, handlers);
279
280     return handlers;
281   }
282
283   /**
284    * Inserts the common values for ob_get_status into an array. Used
285    * by getFullStatus() and ob_get_status().
286    */

287   private static void putCommonStatus(ArrayValue element, OutputBuffer ob,
288                                       Env env, boolean fullStatus)
289   {
290     LongValue type = LongValue.ONE;
291     Callback callback = ob.getCallback();
292
293     if (callback != null && callback.isInternal())
294       type = LongValue.ZERO;
295
296     element.put(new StringValueImpl("type"), type);
297
298     // the rewriter is a special case where it includes a field
299
// "buffer_size" right in the middle of the common elements,
300
// but only when called with full status. It appears always
301
// to be 0 and there is no interface to change this buffer_size
302
// and no indication of its meaning.
303
if (fullStatus && callback != null &&
304         callback == UrlRewriterCallback.getInstance(env))
305       element.put(new StringValueImpl("buffer_size"), LongValue.ZERO);
306
307     // Technically, there are supposed to be three possible values
308
// for status:
309
// 0 if the stream has never been flushed (PHP_OUTPUT_HANDLER_START)
310
// 1 if the stream has been flushed (PHP_OUTPUT_HANDLER_CONT)
311
// 2 if the stream was flushed at the end (PHP_OUTPUT_HANDLER_END)
312
// However, there is no way to access the buffer after it has ended,
313
// so the final case doesn't seem to be an issue! (Even calling
314
// ob_get_status() in the handler on a ob_end_flush() does not
315
// invoke this state.)
316
LongValue status = ob.haveFlushed() ? LongValue.ONE : LongValue.ZERO;
317     element.put(new StringValueImpl("status"), status);
318
319     StringValue name;
320
321     if (callback != null)
322       name = new StringValueImpl(callback.getCallbackName());
323     else
324       name = new StringValueImpl("default output handler".intern());
325
326     element.put(new StringValueImpl("name".intern()), name);
327
328     Value del = ob.getEraseFlag() ? BooleanValue.TRUE :
329       BooleanValue.FALSE;
330     element.put(new StringValueImpl("del"), del);
331   }
332
333   /**
334    * Gets the status for all the output buffers on the stack.
335    * Recursion ensures the results are ordered correctly in the array.
336    */

337   private static void getFullStatus(OutputBuffer ob, Env env, ArrayValue result)
338   {
339     if (ob == null)
340       return;
341
342     getFullStatus(ob.getNext(), env, result);
343
344     ArrayValue element = new ArrayValueImpl();
345
346     element.put(new StringValueImpl("chunk_size"),
347                 LongValue.create(ob.getChunkSize()));
348     
349     // XXX: Not sure why we even need to list a size -- PHP doesn't
350
// even seem to respect it. -1 => infinity?
351
// (Note: "size" == "capacity")
352
element.put(new StringValueImpl("size"), LongValue.create(-1));
353     element.put(new StringValueImpl("block_size"), LongValue.create(-1));
354
355     putCommonStatus(element, ob, env, true);
356    
357     result.put(element);
358   }
359
360   /**
361    * Gets the status of the current output buffer(s)
362    */

363   public static Value ob_get_status(Env env, @Optional boolean full_status)
364   {
365     if (full_status) {
366       OutputBuffer ob = env.getOutputBuffer();
367       ArrayValue result = new ArrayValueImpl();
368
369       getFullStatus(ob, env, result);
370
371       return result;
372     }
373
374     OutputBuffer ob = env.getOutputBuffer();
375     ArrayValue result = new ArrayValueImpl();
376
377     if (ob != null) {
378       result.put(new StringValueImpl("level"),
379                  LongValue.create(ob.getLevel()));
380
381       putCommonStatus(result, ob, env, false);
382     }
383
384     // returns an empty array when no output buffer exists
385
return result;
386   }
387
388   /**
389    * Makes the original "output buffer" flush on every write.
390    */

391   public static Value ob_implicit_flush(Env env, @Optional("true") boolean flag)
392   {
393     if (env.getOriginalOut() != null)
394       env.getOriginalOut().setImplicitFlush(flag);
395
396     return NullValue.NULL;
397   }
398
399   /**
400    * Pushes the output buffer
401    */

402   public static boolean ob_start(Env env,
403                                  @Optional Callback callback,
404                                  @Optional int chunkSize,
405                                  @Optional("true") boolean erase)
406   {
407     if (callback != null && callback.getCallbackName().equals("ob_gzhandler")) {
408       OutputBuffer ob = env.getOutputBuffer();
409
410       for (; ob != null; ob = ob.getNext()) {
411         Callback cb = ob.getCallback();
412
413         if (cb.getCallbackName().equals("ob_gzhandler")) {
414           env.warning(L.l("output handler 'ob_gzhandler' cannot be used twice"));
415           return false;
416         }
417       }
418     }
419     
420     env.pushOutputBuffer(callback, chunkSize, erase);
421
422     return true;
423   }
424
425   /**
426    * Pushes a new UrlRewriter callback onto the output buffer stack
427    * if one does not already exist.
428    */

429   public static UrlRewriterCallback pushUrlRewriter(Env env)
430   {
431     UrlRewriterCallback rewriter = UrlRewriterCallback.getInstance(env);
432
433     if (rewriter == null) {
434       OutputBuffer ob = env.getOutputBuffer();
435       rewriter = new UrlRewriterCallback(env);
436
437       // PHP installs the URL rewriter into the top output buffer if
438
// its callback is null
439
if (ob != null && ob.getCallback() == null)
440         ob.setCallback(rewriter);
441       else
442         ob_start(env, rewriter, 0, true);
443     }
444
445     return rewriter;
446   }
447
448   /**
449    * Adds a variable to the list for rewritten URLs.
450    */

451   public static boolean output_add_rewrite_var(Env env,
452                                                String JavaDoc name, String JavaDoc value)
453   {
454     UrlRewriterCallback rewriter = pushUrlRewriter(env);
455    
456     rewriter.addRewriterVar(name, value);
457
458     return true;
459   }
460
461   /**
462    * Clears the list of variables for rewritten URLs.
463    */

464   public static boolean output_reset_rewrite_vars(Env env)
465   {
466     UrlRewriterCallback rewriter = UrlRewriterCallback.getInstance(env);
467
468     rewriter.resetRewriterVars();
469
470     return true;
471   }
472
473   /**
474    * Output buffering compatible callback that automatically compresses
475    * the output. The output of this function depends on the value of
476    * state. Specifically, if the PHP_OUTPUT_HANDLER_START bit is on
477    * in the state field, the function supplies a header with the output
478    * and initializes a gzip/deflate stream which will be used for
479    * subsequent calls.
480    */

481   public static Value ob_gzhandler(Env env, StringValue buffer, int state)
482   {
483     Encoding encoding = Encoding.NONE;
484     Value _SERVER = env.getSpecialRef("_SERVER");
485
486     String JavaDoc [] acceptedList =
487       _SERVER.get(HTTP_ACCEPT_ENCODING).toString().split(",");
488
489     for (String JavaDoc accepted : acceptedList) {
490       accepted = accepted.trim();
491
492       if (accepted.equalsIgnoreCase("gzip")) {
493         encoding = Encoding.GZIP;
494         break;
495       } else if (accepted.equalsIgnoreCase("deflate")) {
496         encoding = Encoding.DEFLATE;
497         break;
498       }
499     }
500
501     if (encoding == Encoding.NONE)
502       return NullValue.NULL;
503
504     GZOutputPair pair = null;
505
506     if ((state & (1 << OutputBuffer.PHP_OUTPUT_HANDLER_START)) != 0) {
507       HttpModule.header(env, "Vary: Accept-Encoding", true, 0);
508
509       int encodingFlag = 0;
510
511       pair = new GZOutputPair();
512       pair.tempStream = new TempStream();
513       StreamImplOutputStream siout =
514         new StreamImplOutputStream(pair.tempStream);
515
516       try {
517         if (encoding == Encoding.GZIP) {
518           HttpModule.header(env, "Content-Encoding: gzip", true, 0);
519
520           pair.outputStream = new GZIPOutputStream JavaDoc(siout);
521         } else if (encoding == Encoding.DEFLATE) {
522           HttpModule.header(env, "Content-Encoding: deflate", true, 0);
523
524           pair.outputStream = new DeflaterOutputStream JavaDoc(siout);
525         }
526       } catch (IOException JavaDoc e) {
527         return NullValue.NULL;
528       }
529
530       _gzOutputPairs.put(env, pair);
531     } else {
532       pair = _gzOutputPairs.get(env);
533       
534       if (pair == null)
535         return NullValue.NULL;
536     }
537
538     try {
539       pair.outputStream.write(buffer.toString().getBytes());
540       pair.outputStream.flush();
541
542       if ((state & (1 << OutputBuffer.PHP_OUTPUT_HANDLER_END)) != 0) {
543         pair.outputStream.close();
544
545         _gzOutputPairs.remove(env);
546       }
547       
548     } catch (IOException JavaDoc e) {
549       return NullValue.NULL;
550     }
551
552     Value result = new TempBufferStringValue(pair.tempStream.getHead());
553
554     pair.tempStream.discard();
555
556     return result;
557   }
558
559   static {
560     addIni(_iniMap, "output_buffering", "0", PHP_INI_PERDIR);
561     addIni(_iniMap, "output_handler", "", PHP_INI_PERDIR);
562     addIni(_iniMap, "implicit_flush", "0", PHP_INI_ALL);
563   }
564 }
565
Popular Tags