001: package org.gomba;
002:
003: import java.io.IOException;
004: import java.io.InputStream;
005: import java.io.OutputStream;
006: import java.io.Reader;
007: import java.io.Writer;
008: import java.sql.Blob;
009: import java.sql.Clob;
010: import java.sql.ResultSet;
011: import java.sql.ResultSetMetaData;
012: import java.sql.Types;
013:
014: import javax.servlet.ServletConfig;
015: import javax.servlet.ServletException;
016: import javax.servlet.http.HttpServletRequest;
017: import javax.servlet.http.HttpServletResponse;
018:
019: import org.gomba.utils.xml.XMLTextReader;
020:
021: /**
022: * Render a single datum accessed via JDBC. The SQL in the <code>query</code>
023: * init-param should be a SELECT and must return a resultset. The field to
024: * render must be non-null. This servlet inherits the init-params of
025: * {@link org.gomba.AbstractServlet}, plus:
026: * <dl>
027: * <dt>column</dt>
028: * <dd>The result set column to render. This init-param is required only if the
029: * result set contains more than one column. (Optional)</dd>
030: * <dt>media-type</dt>
031: * <dd>The resource MIME content type. May contain ${} parameters. (Required)
032: * </dd>
033: * </dl>
034: * <p>
035: * This servlet can handle the following HTTP methods: GET, HEAD.
036: * </p>
037: *
038: * @author Flavio Tordini
039: * @version $Id: DatumServlet.java,v 1.1.1.1 2004/06/16 13:15:12 flaviotordini
040: * Exp $
041: */
042: public class DatumServlet extends SingleQueryServlet {
043:
044: /**
045: * The result set column name to render. May be null.
046: */
047: private String columnName;
048:
049: /**
050: * The resource MIME content type.
051: */
052: private Expression mediaTypeExpression;
053:
054: /**
055: * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
056: */
057: public void init(ServletConfig config) throws ServletException {
058: super .init(config);
059:
060: // name of the column to render
061: this .columnName = config.getInitParameter("column");
062:
063: // MIME
064: String mediaTypeString = config.getInitParameter("media-type");
065: if (mediaTypeString == null) {
066: throw new ServletException("Missing init-param: media-type");
067: }
068: try {
069: this .mediaTypeExpression = new Expression(mediaTypeString);
070: } catch (Exception e) {
071: throw new ServletException(
072: "Error parsing media type expression.", e);
073: }
074: }
075:
076: /**
077: * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
078: * javax.servlet.http.HttpServletResponse)
079: */
080: protected final void doGet(HttpServletRequest request,
081: HttpServletResponse response) throws ServletException,
082: IOException {
083: processRequest(request, response, true);
084: }
085:
086: /**
087: * @see javax.servlet.http.HttpServlet#doHead(javax.servlet.http.HttpServletRequest,
088: * javax.servlet.http.HttpServletResponse)
089: */
090: protected final void doHead(HttpServletRequest request,
091: HttpServletResponse response) throws ServletException,
092: IOException {
093: processRequest(request, response, false);
094: }
095:
096: /**
097: * @see org.gomba.AbstractServlet#doOutput(java.sql.ResultSet,
098: * javax.servlet.http.HttpServletResponse, ParameterResolver)
099: */
100: protected void doOutput(ResultSet resultSet,
101: HttpServletResponse response,
102: ParameterResolver parameterResolver) throws Exception {
103:
104: ResultSetMetaData rsmd = resultSet.getMetaData();
105: if (rsmd.getColumnCount() != 1 && this .columnName == null) {
106: throw new Exception(
107: "The result set contains more than one column. "
108: + "You need to set the 'column' init-param.");
109: }
110:
111: // mime type
112: final Object mediaType = this .mediaTypeExpression
113: .replaceParameters(parameterResolver);
114: if (!(mediaType instanceof java.lang.String)) {
115: throw new Exception(
116: "The media-type expression does not evaluate to "
117: + java.lang.String.class);
118: }
119: response.setContentType((String) mediaType);
120:
121: // find out the column index
122: final int columnIndex;
123: if (this .columnName != null) {
124: columnIndex = getColumnIndex(rsmd, this .columnName);
125: } else {
126: columnIndex = 1;
127: }
128:
129: final int columnType = rsmd.getColumnType(columnIndex);
130:
131: // render!
132: switch (columnType) {
133:
134: case Types.BLOB:
135: Blob blob = resultSet.getBlob(columnIndex);
136: if (blob == null) {
137: throw new Exception("BLOB value is null.");
138: }
139: streamBytes(blob.getBinaryStream(), response);
140: break;
141:
142: case Types.CLOB:
143: Clob clob = resultSet.getClob(columnIndex);
144: if (clob == null) {
145: throw new Exception("CLOB value is null.");
146: }
147: // wrap our reader in order to strip invalid XML chars
148: Reader reader1 = new XMLTextReader(clob
149: .getCharacterStream());
150: streamCharacters(reader1, response);
151: break;
152:
153: case Types.LONGVARCHAR:
154: case Types.VARCHAR:
155: case Types.CHAR:
156: Reader reader2 = resultSet.getCharacterStream(columnIndex);
157: if (reader2 == null) {
158: throw new Exception("LONGVARCHAR value is null.");
159: }
160: // wrap our reader in order to strip invalid XML chars
161: reader2 = new XMLTextReader(reader2);
162: streamCharacters(reader2, response);
163: break;
164:
165: case Types.LONGVARBINARY:
166: InputStream is = resultSet.getBinaryStream(columnIndex);
167: if (is == null) {
168: throw new Exception("LONGVARBINARY value is null.");
169: }
170: streamBytes(is, response);
171: break;
172:
173: default:
174: String value = resultSet.getObject(columnIndex).toString();
175: response.getWriter().write(value);
176: }
177:
178: }
179:
180: /**
181: * Get the index of a column in a resultset given its name.
182: */
183: protected static int getColumnIndex(ResultSetMetaData rsmd,
184: String columnName) throws Exception {
185: final int columnCount = rsmd.getColumnCount();
186: for (int i = 1; i <= columnCount; i++) {
187: // The set of columns begins with '1' rather than '0'
188: final String cn = rsmd.getColumnName(i);
189: if (cn.equalsIgnoreCase(columnName)) {
190: return i;
191: }
192: }
193: throw new Exception("Cannot find column named " + columnName);
194: }
195:
196: /**
197: * Stream unicode characters to the response body.
198: *
199: * @param reader
200: * The stream of charachters
201: * @param response
202: * The HTTP response
203: */
204: private static void streamCharacters(Reader reader,
205: HttpServletResponse response) throws IOException {
206: try {
207: char[] buffer = new char[response.getBufferSize()];
208: Writer writer = response.getWriter();
209: int length;
210: while ((length = reader.read(buffer)) >= 0) {
211: writer.write(buffer, 0, length);
212: }
213: } finally {
214: reader.close();
215: }
216: }
217:
218: /**
219: * Stream bytes to the response body.
220: *
221: * @param is
222: * The stream of bytes
223: * @param response
224: * The HTTP response
225: */
226: private static void streamBytes(InputStream is,
227: HttpServletResponse response) throws IOException {
228: try {
229: byte[] buffer = new byte[response.getBufferSize()];
230: OutputStream os = response.getOutputStream();
231: int length;
232: while ((length = is.read(buffer)) >= 0) {
233: os.write(buffer, 0, length);
234: }
235: } finally {
236: is.close();
237: }
238: }
239:
240: }
|