KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > source > impl > BlobSource


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.components.source.impl;
17
18 import java.io.ByteArrayInputStream JavaDoc;
19 import java.io.FilterInputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.net.MalformedURLException JavaDoc;
23 import java.sql.Blob JavaDoc;
24 import java.sql.Clob JavaDoc;
25 import java.sql.Connection JavaDoc;
26 import java.sql.ResultSet JavaDoc;
27 import java.sql.SQLException JavaDoc;
28 import java.sql.Statement JavaDoc;
29 import java.sql.Types JavaDoc;
30 import java.util.Iterator JavaDoc;
31
32 import org.apache.avalon.framework.logger.AbstractLogEnabled;
33 import org.apache.avalon.framework.service.ServiceManager;
34 import org.apache.avalon.framework.service.ServiceSelector;
35 import org.apache.avalon.framework.service.Serviceable;
36
37 import org.apache.avalon.excalibur.datasource.DataSourceComponent;
38
39 import org.apache.cocoon.CascadingIOException;
40
41 import org.apache.excalibur.source.Source;
42 import org.apache.excalibur.source.SourceException;
43 import org.apache.excalibur.source.SourceValidity;
44
45 /**
46  * A <code>Source</code> that takes its content in a single JDBC column. Any
47  * kind of column can be used (clob, blob, varchar, etc), but "Blob" means
48  * that the whole content is contained in a single column.
49  * <p>The URL syntax is "blob:/datasource/table/column[cond]", where :
50  * <ul>
51  * <li>"datasource" is the jdbc datasource to use (defined in cocoon.xonf)
52  * <li>"table" is the database table,
53  * <li>"column" is (you can guess, now :) the column in "table",
54  * <li>"cond" is the boolean condition used to select a particular record in
55  * the table.
56  * </ul>
57  * <p>For example, "<code>blob:/personel/people/photo[userid='foo']</code>"
58  * will fetch the first column returned by the statement "<code>SELECT photo
59  * from people where userid='foo'</code>" in the datasource "<code>personel</code>"
60  *
61  * @author <a HREF="mailto:sylvain@apache.org">Sylvain Wallez</a>
62  * @author <a HREF="mailto:stephan@apache.org">Stephan Michels</a>
63  * @version CVS $Id: BlobSource.java 124685 2005-01-08 22:20:56Z antonio $
64  */

65 public class BlobSource extends AbstractLogEnabled implements Source, Serviceable {
66
67     /** The ServiceManager instance */
68     private ServiceManager manager = null;
69
70     /**
71      * The system ID for this source
72      */

73     private String JavaDoc systemId;
74
75     private String JavaDoc datasourceName;
76
77     private String JavaDoc tableName;
78
79     private String JavaDoc columnName;
80
81     private String JavaDoc condition;
82
83     private final static String JavaDoc URL_PREFIX = "blob:/";
84     private final static int URL_PREFIX_LEN = URL_PREFIX.length();
85
86     /**
87      * Create a file source from a 'blob:' url and a component manager.
88      * <p>The url is of the form "blob:/datasource/table/column[condition]
89      */

90     public BlobSource(String JavaDoc url) throws MalformedURLException JavaDoc {
91
92         this.systemId = url;
93
94         // Parse the url
95
int start = URL_PREFIX_LEN;
96         int end;
97
98         // Datasource
99
end = url.indexOf('/', start);
100         if (end == -1) {
101             throw new MalformedURLException JavaDoc("Malformed blob source (cannot find datasource) : " + url);
102         }
103
104         this.datasourceName = url.substring(start, end);
105
106         // Table
107
start = end + 1;
108         end = url.indexOf('/', start);
109         if (end == -1) {
110             throw new MalformedURLException JavaDoc("Malformed blob source (cannot find table name) : " + url);
111         }
112
113         this.tableName = url.substring(start, end);
114
115         // Column
116
start = end + 1;
117         end = url.indexOf('[', start);
118         if (end == -1) {
119             this.columnName = url.substring(start);
120         } else {
121             this.columnName = url.substring(start, end);
122
123             // Condition
124
start = end + 1;
125             end = url.length() - 1;
126             if (url.charAt(end) != ']') {
127                 throw new MalformedURLException JavaDoc("Malformed url for a blob source (cannot find condition) : " + url);
128             } else {
129                 this.condition = url.substring(start, end);
130             }
131         }
132     }
133
134     /**
135      * Set the current <code>ServiceManager</code> instance used by this
136      * <code>Serviceable</code>.
137      */

138     public void service(ServiceManager manager) {
139         this.manager = manager;
140     }
141
142     /**
143      * Return the protocol
144      */

145     public String JavaDoc getScheme() {
146         return URL_PREFIX;
147     }
148
149     /**
150      * Return the unique identifer for this source
151      */

152     public String JavaDoc getURI() {
153         return this.systemId;
154     }
155
156     /**
157      * Get the input stream for this source.
158      */

159     public InputStream JavaDoc getInputStream() throws IOException JavaDoc, SourceException {
160         if (getLogger().isDebugEnabled()) {
161             getLogger().debug("Opening stream for datasource " + this.datasourceName +
162                 ", table " + this.tableName + ", column " + this.columnName +
163                 (this.condition == null ? ", no condition" : ", condition " + this.condition)
164             );
165         }
166
167         Connection JavaDoc cnx = null;
168         Statement JavaDoc stmt = null;
169
170         try {
171             cnx = getConnection();
172             stmt = cnx.createStatement();
173
174             StringBuffer JavaDoc selectBuf = new StringBuffer JavaDoc("SELECT ").append(this.columnName).
175                 append(" FROM ").append(this.tableName);
176
177             if (this.condition != null) {
178                 selectBuf.append(" WHERE ").append(this.condition);
179             }
180
181             String JavaDoc select = selectBuf.toString();
182             if (getLogger().isDebugEnabled()) {
183                 getLogger().debug("Executing statement " + select);
184             }
185
186             ResultSet JavaDoc rs = stmt.executeQuery(select);
187             rs.next();
188
189             int colType = rs.getMetaData().getColumnType(1);
190
191             switch(colType) {
192                 case Types.BLOB :
193                     Blob JavaDoc blob = rs.getBlob(1);
194                     return new JDBCInputStream(blob.getBinaryStream(), cnx);
195                 //break;
196

197                 case Types.CLOB :
198                     Clob JavaDoc clob = rs.getClob(1);
199                     return new JDBCInputStream(clob.getAsciiStream(), cnx);
200                 //break;
201

202                 default :
203                     String JavaDoc value = rs.getString(1);
204                     return new ByteArrayInputStream JavaDoc(value.getBytes());
205             }
206         } catch(SQLException JavaDoc sqle) {
207             String JavaDoc msg = "Cannot retrieve content from " + this.systemId;
208             getLogger().error(msg, sqle);
209             // IOException would be more adequate, but ProcessingException is cascaded...
210
throw new SourceException(msg, sqle);
211         } finally {
212             try {
213                 if (stmt != null) {
214                     stmt.close();
215                 }
216                 if (cnx != null) {
217                     cnx.close();
218                 }
219             } catch(SQLException JavaDoc sqle2) {
220                 // PITA
221
throw new SourceException("Cannot close connection", sqle2);
222             }
223         }
224     }
225
226     /**
227      * Get the Validity object. This can either wrap the last modification
228      * date or the expires information or...
229      * If it is currently not possible to calculate such an information
230      * <code>null</code> is returned.
231      */

232     public SourceValidity getValidity() {
233         return null;
234     }
235     
236     /**
237      * Refresh the content of this object after the underlying data
238      * content has changed.
239      */

240     public void refresh() {
241     }
242     
243     /**
244      *
245      * @see org.apache.excalibur.source.Source#exists()
246      */

247     public boolean exists() {
248         // FIXME
249
return true;
250     }
251     
252     /**
253      * The mime-type of the content described by this object.
254      * If the source is not able to determine the mime-type by itself
255      * this can be <code>null</code>.
256      */

257     public String JavaDoc getMimeType() {
258         return null;
259     }
260     
261     /**
262      * Return the content length of the content or -1 if the length is
263      * unknown
264      */

265     public long getContentLength() {
266         return -1;
267     }
268
269     /**
270      * Get the last modification date.
271      * @return The last modification in milliseconds since January 1, 1970 GMT
272      * or 0 if it is unknown
273      */

274     public long getLastModified() {
275         return 0;
276     }
277
278     /**
279      * Get the value of a parameter.
280      * Using this it is possible to get custom information provided by the
281      * source implementation, like an expires date, HTTP headers etc.
282      */

283     public String JavaDoc getParameter(String JavaDoc name) {
284         return null;
285     }
286
287     /**
288      * Get the value of a parameter.
289      * Using this it is possible to get custom information provided by the
290      * source implementation, like an expires date, HTTP headers etc.
291      */

292     public long getParameterAsLong(String JavaDoc name) {
293         return 0;
294     }
295
296     /**
297      * Get parameter names
298      * Using this it is possible to get custom information provided by the
299      * source implementation, like an expires date, HTTP headers etc.
300      */

301     public Iterator JavaDoc getParameterNames() {
302         return new EmptyIterator();
303     }
304
305     static class EmptyIterator implements Iterator JavaDoc {
306         public boolean hasNext() { return false; }
307         public Object JavaDoc next() { return null; }
308         public void remove() {}
309     }
310
311     private Connection JavaDoc getConnection() throws SourceException {
312
313         ServiceSelector selector = null;
314         DataSourceComponent datasource = null;
315
316         try {
317             try {
318                 selector = (ServiceSelector)this.manager.lookup(DataSourceComponent.ROLE + "Selector");
319
320                 datasource = (DataSourceComponent)selector.select(this.datasourceName);
321
322             } catch(Exception JavaDoc e) {
323                 String JavaDoc msg = "Cannot get datasource '" + this.datasourceName + "'";
324                 getLogger().error(msg);
325                 throw new SourceException(msg, e);
326             }
327
328             try {
329                 return datasource.getConnection();
330             } catch(Exception JavaDoc e) {
331                 String JavaDoc msg = "Cannot get connection for datasource '" + this .datasourceName + "'";
332                 getLogger().error(msg);
333                 throw new SourceException(msg, e);
334             }
335
336         } finally {
337             if (datasource != null) {
338                 selector.release(datasource);
339             }
340         }
341     }
342
343     /**
344      * An OutputStream that will close the connection that created it on
345      * close.
346      */

347     private class JDBCInputStream extends FilterInputStream JavaDoc {
348
349         Connection JavaDoc cnx;
350
351         private final void closeCnx() throws IOException JavaDoc {
352             if (this.cnx != null) {
353                 try {
354                     Connection JavaDoc tmp = cnx;
355                     cnx = null;
356                     tmp.close();
357                 } catch(Exception JavaDoc e) {
358                     String JavaDoc msg = "Error closing the connection for " + BlobSource.this.getURI();
359                     BlobSource.this.getLogger().warn(msg, e);
360                     throw new CascadingIOException(msg + " : " + e.getMessage(), e);
361                 }
362             }
363         }
364
365         public JDBCInputStream(InputStream JavaDoc stream, Connection JavaDoc cnx) {
366             super(stream);
367             this.cnx = cnx;
368         }
369
370         public int read() throws IOException JavaDoc {
371             try {
372                 int result = in.read();
373                 if (result == -1) {
374                     closeCnx();
375                 }
376                 return result;
377             } catch(IOException JavaDoc e) {
378                 closeCnx();
379                 throw e;
380             }
381         }
382
383         public int read(byte[] b) throws IOException JavaDoc {
384             try {
385                 int result = in.read(b);
386                 if (result == -1) {
387                     closeCnx();
388                 }
389                 return result;
390             } catch(IOException JavaDoc e) {
391                 closeCnx();
392                 throw e;
393             }
394         }
395
396         public int read(byte[] b, int off, int len) throws IOException JavaDoc {
397             try {
398                 int result = in.read(b, off, len);
399                 if (result == -1) {
400                     closeCnx();
401                 }
402                 return result;
403             } catch(IOException JavaDoc e) {
404                 closeCnx();
405                 throw e;
406             }
407         }
408
409         public void close() throws IOException JavaDoc {
410             super.close();
411             closeCnx();
412         }
413     }
414 }
415
416
Popular Tags