001: /*
002: * Copyright 2002 Sun Microsystems, Inc. All
003: * rights reserved. Use of this product is subject
004: * to license terms. Federal Acquisitions:
005: * Commercial Software -- Government Users
006: * Subject to Standard License Terms and
007: * Conditions.
008: *
009: * Sun, Sun Microsystems, the Sun logo, and Sun ONE
010: * are trademarks or registered trademarks of Sun Microsystems,
011: * Inc. in the United States and other countries.
012: */
013: package com.sun.portal.portlet.impl;
014:
015: import java.util.Enumeration;
016:
017: import java.io.PrintWriter;
018: import java.io.StringWriter;
019: import java.io.OutputStream;
020: import java.io.ByteArrayOutputStream;
021:
022: import javax.servlet.http.HttpServletRequest;
023: import javax.servlet.http.HttpServletResponse;
024:
025: import javax.portlet.RenderRequest;
026: import javax.portlet.RenderResponse;
027: import javax.portlet.PortletURL;
028:
029: import java.util.logging.Logger;
030:
031: import com.sun.portal.portletcontainercommon.PortletContainerRenderRequest;
032: import com.sun.portal.portletcontainercommon.PortletContainerRenderResponse;
033: import com.sun.portal.portletcontainercommon.PortletActions;
034:
035: import com.sun.portal.portletappengine.PortletAppEngineConstants;
036:
037: import com.sun.portal.portletcontainercommon.descriptor.PortletDescriptor;
038:
039: /**
040: * This class provides implementation of the RenderResponse interface.
041: */
042: public class RenderResponseImpl extends PortletResponseImpl implements
043: RenderResponse {
044: private RenderRequest _rReq;
045: private HttpServletResponse _res;
046: private PortletContainerRenderRequest _pContReq;
047: private PortletContainerRenderResponse _pContRes;
048: private PrintWriter _writer;
049: private StringWriter _stringWriter;
050: private ByteArrayOutputStream _output;
051: private String _contentType;
052: private PortletDescriptor _pDescriptor;
053: private boolean _gotWriter;
054: private boolean _gotOutputStream;
055: private boolean _committed;
056: private static Logger _logger;
057: private int _bufSize;
058:
059: /**
060: * Initialize the global variables.
061: * <P>
062: * @param req The <code>HttpServletRequest</code> of the PAE
063: * @param res The <code>HttpServletResponse</code> of the PAE
064: * @param rReq The <code>RenderRequest</code>
065: * @param pContReq The <code>PortletContainerRequest</code>
066: * @param pContRes The <code>PortletContainerResponse</code>
067: * @param pDescriptor The <code>PortletDescriptor</code> for the
068: * portlet
069: * @param writer The <code>StringWriter</code> for the output stream
070: */
071: void init(HttpServletRequest req, HttpServletResponse res,
072: RenderRequest rReq, PortletContainerRenderRequest pContReq,
073: PortletContainerRenderResponse pContRes,
074: StringWriter writer, ByteArrayOutputStream output,
075: PortletDescriptor pDescriptor, Logger logger) {
076:
077: super .init(req, res, rReq, pContReq, pContRes, logger);
078: _res = res;
079: _rReq = rReq;
080: _pContReq = pContReq;
081: _pContRes = pContRes;
082: _writer = new PrintWriter(writer);
083: _stringWriter = writer;
084: _output = output;
085: _pDescriptor = pDescriptor;
086: _gotWriter = false;
087: _gotOutputStream = false;
088: _committed = false;
089: _logger = logger;
090: _bufSize = PortletAppEngineConstants.INITIAL_BUFFER_SIZE;
091: }
092:
093: /**
094: * Clears the global variables.
095: */
096: void clear() {
097: super .clear();
098:
099: _writer = null;
100: _stringWriter = null;
101: _output = null;
102: _pDescriptor = null;
103: _gotWriter = false;
104: _gotOutputStream = false;
105: _committed = false;
106: _logger = null;
107: _bufSize = 0;
108: _contentType = null;
109: }
110:
111: /**
112: * Returns the MIME type that can be used to contribute
113: * markup to the portlet response.
114: * <p>
115: * If no content type was set previously using the {@link #setContentType} method
116: * this method retuns <code>null</code>.
117: *
118: * @see #setContentType
119: *
120: * @return the MIME type of the response, or <code>null</code>
121: * if no content type is set
122: */
123: public String getContentType() {
124: return _contentType;
125: }
126:
127: /**
128: * Creates a portlet URL targeting the portlet. If no portlet mode,
129: * window state or security modifier is set in the PortletURL the
130: * current values are preserved. If a request is triggered by the
131: * PortletURL, it results in a render request.
132: * <p>
133: * The returned URL can be further extended by adding
134: * portlet-specific parameters and portlet modes and window states.
135: * <p>
136: * The created URL will per default not contain any parameters
137: * of the current render request.
138: *
139: * @return a portlet render URL
140: */
141: public PortletURL createRenderURL() {
142: PortletURL url = (PortletURL) (new PortletURLImpl(_rReq,
143: _pContReq, PortletActions.RENDER));
144:
145: return url;
146: }
147:
148: /**
149: * Creates a portlet URL targeting the portlet. If no portlet mode,
150: * window state or security modifier is set in the PortletURL the
151: * current values are preserved. If a request is triggered by the
152: * PortletURL, it results in an action request.
153: * <p>
154: * The returned URL can be further extended by adding
155: * portlet-specific parameters and portlet modes and window states.
156: * <p>
157: * The created URL will per default not contain any parameters
158: * of the current render request.
159: *
160: * @return a portlet action URL
161: */
162: public PortletURL createActionURL() {
163:
164: PortletURL url = (PortletURL) (new PortletURLImpl(_rReq,
165: _pContReq, PortletActions.ACTION));
166: return url;
167: }
168:
169: /**
170: * The value returned by this method should be prefixed or appended to
171: * elements, such as JavaScript variables or function names, to ensure
172: * they are unique in the context of the portal page.
173: *
174: * @return the namespace
175: */
176:
177: public String getNamespace() {
178: String prefix = new String(_pContReq.getEntityID());
179: //Following line is added to fix CR 6211964
180: prefix = prefix.replace('/', '_');
181: return prefix.replace('|', '_');
182: }
183:
184: /**
185: * This method sets the title of the portlet.
186: * <p>
187: * The value can be a text String
188: *
189: * @param title portlet title as text String or resource URI
190: */
191: public void setTitle(String title) {
192:
193: _pContRes.setTitle(title);
194: }
195:
196: /**
197: * Sets the MIME type for the portlet response. The portlet must
198: * set the content type before calling {@link #getWriter} or
199: * {@link #getPortletOutputStream}.
200: * <p>
201: * Calling <code>setContentType</code> after <code>getWriter</code>
202: * or <code>getOutputStream</code> does not change the content type.
203: *
204: * @param type the content MIME type
205: *
206: * @throws java.lang.IllegalArgumentException
207: * if the given type is not in the list returned
208: * by <code>PortletRequest.getResponseContentTypes</code>
209: *
210: * @see PortletRequest#getResponseContentTypes
211: */
212: public void setContentType(String type) {
213:
214: //if _gotWriter or _gotOutputStream is true, return without setting the content type.
215: if (!_gotWriter || !_gotOutputStream) {
216:
217: if (type.indexOf(";") != -1) {
218: type = type.substring(0, type.indexOf(";"));
219: }
220: boolean found = false;
221: Enumeration e = _rReq.getResponseContentTypes();
222: while (e.hasMoreElements() && !found) {
223: String next = (String) e.nextElement();
224: if (type.equalsIgnoreCase(next)) {
225: found = true;
226: }
227: }
228: if (found) {
229: _contentType = type;
230: _res.setContentType(type);
231: } else {
232: throw new IllegalArgumentException(
233: "Unsupported response content type: " + type);
234: }
235: }
236: }
237:
238: /**
239: * Returns the name of the charset used for
240: * the MIME body sent in this response.
241: *
242: * <p>See RFC 2047 (http://ds.internic.net/rfc/rfc2045.txt)
243: * for more information about character encoding and MIME.
244: *
245: * @return a <code>String</code> specifying the
246: * name of the charset, for
247: * example, <code>ISO-8859-1</code>
248: *
249: */
250: public String getCharacterEncoding() {
251: return _res.getCharacterEncoding();
252: }
253:
254: /**
255: * Returns a PrintWriter object that can send character
256: * text to the portal.
257: * <p>
258: * Before calling this method the content type of the
259: * render response must be set using the {@link #setContentType}
260: * method.
261: * <p>
262: * Either this method or {@link #getPortletOutputStream} may be
263: * called to write the body, not both.
264: *
265: * @return a <code>PrintWriter</code> object that
266: * can return character data to the portal
267: *
268: * @exception java.io.IOException
269: * if an input or output exception occurred
270: * @exception java.lang.IllegalStateException
271: * if the <code>getPortletOutputStream</code> method
272: * has been called on this response,
273: * or if no content type was set using the
274: * <code>setContentType</code> method.
275: *
276: * @see #setContentType
277: * @see #getPortletOutputStream
278: */
279: public java.io.PrintWriter getWriter() throws java.io.IOException {
280:
281: if (_gotOutputStream) {
282: throw new IllegalStateException(
283: "RenderResponseImpl.getWriter: illegal getting writer since getPortletOutputStream() has already been called");
284: }
285: if (!isContentTypeValid()) {
286: throw new IllegalStateException(
287: "RenderResponseImpl.getWriter: contentType either contains wildcard or is not set.");
288: }
289: _gotWriter = true;
290: return _writer;
291: }
292:
293: /**
294: * Returns the locale assigned to the response.
295: *
296: * @return Locale of this response
297: */
298: public java.util.Locale getLocale() {
299: return _rReq.getLocale();
300: }
301:
302: /**
303: * Sets the preferred buffer size for the body of the response.
304: * The portlet container will use a buffer at least as large as
305: * the size requested.
306: *
307: * @param size the preferred buffer size
308: *
309: * @see #getBufferSize
310: * @see #flushBuffer
311: * @see #reset
312: */
313:
314: public void setBufferSize(int size) {
315: _bufSize = size;
316: }
317:
318: /**
319: * Returns the actual buffer size used for the response. If no buffering
320: * is used, this method returns 0.
321: * <p>
322: * @return the actual buffer size used
323: *
324: * @see #setBufferSize
325: * @see #flushBuffer
326: * @see #isCommitted
327: * @see #reset
328: */
329:
330: public int getBufferSize() {
331: return _bufSize;
332: }
333:
334: /**
335: * Forces any content in the buffer to be written to the client. A call
336: * to this method automatically commits the response.
337: *
338: * @exception java.io.IOException if an error occured when writing the output
339: *
340: * @see #setBufferSize
341: * @see #getBufferSize
342: * @see #isCommitted
343: * @see #reset
344: */
345:
346: public void flushBuffer() throws java.io.IOException {
347: _committed = true;
348: }
349:
350: /**
351: * Clears the content of the underlying buffer in the response without
352: * clearing properties set. If the response has been committed,
353: * this method throws an <code>IllegalStateException</code>.
354: *
355: * @exception IllegalStateException if this method is called after
356: * response is comitted
357: *
358: * @see #setBufferSize
359: * @see #getBufferSize
360: * @see #isCommitted
361: * @see #reset
362: */
363:
364: public void resetBuffer() {
365:
366: if (_committed) {
367: throw new IllegalStateException(
368: "RenderResponseImpl.resetBuffer: illegal resetting buffer: buffer has already commited.");
369: }
370:
371: if (_gotOutputStream) {
372: _output.reset();
373: }
374: if (_gotWriter) {
375: _stringWriter.getBuffer().setLength(0);
376: }
377:
378: }
379:
380: /**
381: * Returns a boolean indicating if the response has been
382: * committed.
383: *
384: * @return a boolean indicating if the response has been
385: * committed
386: *
387: * @see #setBufferSize
388: * @see #getBufferSize
389: * @see #flushBuffer
390: * @see #reset
391: */
392:
393: public boolean isCommitted() {
394: int size = 0;
395:
396: if (_gotOutputStream) {
397: size = _output.size();
398: } else if (_gotWriter) {
399: size = _stringWriter.getBuffer().length();
400: }
401:
402: if (size > PortletAppEngineConstants.INITIAL_BUFFER_SIZE) {
403: _committed = true;
404: }
405:
406: return _committed;
407: }
408:
409: /**
410: * Clears any data that exists in the buffer as well as the properties set.
411: * If the response has been committed, this method throws an
412: * <code>IllegalStateException</code>.
413: *
414: * @exception java.lang.IllegalStateException if the response has already been
415: * committed
416: *
417: * @see #setBufferSize
418: * @see #getBufferSize
419: * @see #flushBuffer
420: * @see #isCommitted
421: */
422:
423: public void reset() {
424: resetBuffer();
425: }
426:
427: /**
428: * Returns a <code>OutputStream</code> suitable for writing binary
429: * data in the response. The portlet container does not encode the
430: * binary data.
431: * <p>
432: * Before calling this method the content type of the
433: * render response must be set using the {@link #setContentType}
434: * method.
435: * <p>
436: * Calling <code>flush()</code> on the OutputStream commits the response.
437: * <p>
438: * Either this method or {@link #getWriter} may be called to write the body, not both.
439: *
440: * @return a <code>OutputStream</code> for writing binary data
441: *
442: * @exception java.lang.IllegalStateException if the <code>getWriter</code> method
443: * has been called on this response, or
444: * if no content type was set using the
445: * <code>setContentType</code> method.
446: *
447: * @exception IOException if an input or output exception occurred
448: *
449: * @see #setContentType
450: * @see #getWriter
451: */
452:
453: public OutputStream getPortletOutputStream()
454: throws java.io.IOException {
455:
456: if (_gotWriter) {
457: throw new IllegalStateException(
458: "RenderResponseImpl.getOutputStream: illegal getting output stream: getWriter() has already been called");
459: }
460:
461: if (!isContentTypeValid()) {
462: throw new IllegalStateException(
463: "RenderResponseImpl.getOutputStream: contentType either contains wildcard or is not set.");
464: }
465:
466: _gotOutputStream = true;
467: return (OutputStream) _output;
468: }
469:
470: /*
471: * <code>isContentTypeValid</code> is called by
472: * <code>getPortletOutputStream</code> and <code>getWriter</code>
473: * to see if the content type is set, if not then return false,
474: * otherwise return true.
475: */
476: private boolean isContentTypeValid() {
477: boolean valid = true;
478:
479: if (getContentType() == null) {
480: valid = false;
481: }
482: return valid;
483: }
484:
485: /**
486: * Sets a String property to be returned to the portal.
487: * <p>
488: * Properties can be used by portlets to provide vendor specific
489: * information to the portal.
490: *
491: * This method resets all properties previously added with the
492: * same key.
493: *
494: * @param key the key of the property to be returned to the portal
495: * @param value the value of the property to be returned to the portal
496: *
497: * @exception java.lang.IllegalArgumentException
498: * if key is <code>null</code>.
499: */
500: public void setProperty(String key, String value) {
501: if (key == null) {
502: throw new IllegalArgumentException(
503: "Property name should not be null.");
504: }
505:
506: if (key.equals(EXPIRATION_CACHE)) {
507: int cacheVal = _pContRes.getExpiration();
508: if (_pContRes.getExpiration() != PortletDescriptor.EXPIRATION_CACHE_NOT_DEFINED) {
509: try {
510: cacheVal = Integer.parseInt(value);
511: } catch (NumberFormatException ne) {
512: //cache value do not change
513: }
514:
515: _pContRes.setExpiration(cacheVal);
516: }
517: }
518: }
519:
520: }
|