001: /****************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one *
003: * or more contributor license agreements. See the NOTICE file *
004: * distributed with this work for additional information *
005: * regarding copyright ownership. The ASF licenses this file *
006: * to you under the Apache License, Version 2.0 (the *
007: * "License"); you may not use this file except in compliance *
008: * with the License. You may obtain a copy of the License at *
009: * *
010: * http://www.apache.org/licenses/LICENSE-2.0 *
011: * *
012: * Unless required by applicable law or agreed to in writing, *
013: * software distributed under the License is distributed on an *
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015: * KIND, either express or implied. See the License for the *
016: * specific language governing permissions and limitations *
017: * under the License. *
018: ****************************************************************/package org.apache.james.mailrepository;
019:
020: import org.apache.avalon.cornerstone.services.store.StreamRepository;
021: import org.apache.james.core.MimeMessageSource;
022: import org.apache.james.util.JDBCUtil;
023:
024: import java.io.ByteArrayInputStream;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.SequenceInputStream;
028: import java.sql.Blob;
029: import java.sql.Connection;
030: import java.sql.PreparedStatement;
031: import java.sql.ResultSet;
032: import java.sql.SQLException;
033:
034: /**
035: * This class points to a specific message in a repository. This will return an
036: * InputStream to the JDBC field/record, possibly sequenced with the file stream.
037: */
038: public class MimeMessageJDBCSource extends MimeMessageSource {
039:
040: /**
041: * Whether 'deep debugging' is turned on.
042: */
043: private static final boolean DEEP_DEBUG = false;
044:
045: //Define how to get to the data
046: JDBCMailRepository repository = null;
047: String key = null;
048: StreamRepository sr = null;
049:
050: private long size = -1;
051:
052: /**
053: * SQL used to retrieve the message body
054: */
055: String retrieveMessageBodySQL = null;
056:
057: /**
058: * SQL used to retrieve the size of the message body
059: */
060: String retrieveMessageBodySizeSQL = null;
061:
062: /**
063: * The JDBCUtil helper class
064: */
065: private static final JDBCUtil theJDBCUtil = new JDBCUtil() {
066: protected void delegatedLog(String logString) {
067: // No logging available at this point in the code.
068: // Therefore this is a noop method.
069: }
070: };
071:
072: /**
073: * Construct a MimeMessageSource based on a JDBC repository, a key, and a
074: * stream repository (where we might store the message body)
075: */
076: public MimeMessageJDBCSource(JDBCMailRepository repository,
077: String key, StreamRepository sr) throws IOException {
078: if (repository == null) {
079: throw new IOException("Repository is null");
080: }
081: if (key == null) {
082: throw new IOException("Message name (key) was not defined");
083: }
084: this .repository = repository;
085: this .key = key;
086: this .sr = sr;
087:
088: retrieveMessageBodySQL = repository.sqlQueries.getSqlString(
089: "retrieveMessageBodySQL", true);
090: // this is optional
091: retrieveMessageBodySizeSQL = repository.sqlQueries
092: .getSqlString("retrieveMessageBodySizeSQL");
093: }
094:
095: /**
096: * Returns a unique String ID that represents the location from where
097: * this source is loaded. This will be used to identify where the data
098: * is, primarily to avoid situations where this data would get overwritten.
099: *
100: * @return the String ID
101: */
102: public String getSourceId() {
103: StringBuffer sourceIdBuffer = new StringBuffer(128).append(
104: repository.repositoryName).append("/").append(key);
105: return sourceIdBuffer.toString();
106: }
107:
108: /**
109: * Return the input stream to the database field and then the file stream. This should
110: * be smart enough to work even if the file does not exist. This is to support
111: * a repository with the entire message in the database, which is how James 1.2 worked.
112: */
113: public synchronized InputStream getInputStream() throws IOException {
114: Connection conn = null;
115: PreparedStatement retrieveMessageStream = null;
116: ResultSet rsRetrieveMessageStream = null;
117: try {
118: conn = repository.getConnection();
119:
120: byte[] headers = null;
121:
122: long start = 0;
123: if (DEEP_DEBUG) {
124: start = System.currentTimeMillis();
125: System.out.println("starting");
126: }
127: retrieveMessageStream = conn
128: .prepareStatement(retrieveMessageBodySQL);
129: retrieveMessageStream.setString(1, key);
130: retrieveMessageStream.setString(2,
131: repository.repositoryName);
132: rsRetrieveMessageStream = retrieveMessageStream
133: .executeQuery();
134:
135: if (!rsRetrieveMessageStream.next()) {
136: throw new IOException("Could not find message");
137: }
138:
139: String getBodyOption = repository.sqlQueries
140: .getDbOption("getBody");
141: if (getBodyOption != null
142: && getBodyOption.equalsIgnoreCase("useBlob")) {
143: Blob b = rsRetrieveMessageStream.getBlob(1);
144: headers = b.getBytes(1, (int) b.length());
145: } else {
146: headers = rsRetrieveMessageStream.getBytes(1);
147: }
148: if (DEEP_DEBUG) {
149: System.err.println("stopping");
150: System.err.println(System.currentTimeMillis() - start);
151: }
152:
153: InputStream in = new ByteArrayInputStream(headers);
154: try {
155: if (sr != null) {
156: in = new SequenceInputStream(in, sr.get(key));
157: }
158: } catch (Exception e) {
159: //ignore this... either sr is null, or the file does not exist
160: // or something else
161: }
162: return in;
163: } catch (SQLException sqle) {
164: throw new IOException(sqle.toString());
165: } finally {
166: theJDBCUtil.closeJDBCResultSet(rsRetrieveMessageStream);
167: theJDBCUtil.closeJDBCStatement(retrieveMessageStream);
168: theJDBCUtil.closeJDBCConnection(conn);
169: }
170: }
171:
172: /**
173: * Runs a custom SQL statement to check the size of the message body
174: */
175: public synchronized long getMessageSize() throws IOException {
176: if (size != -1)
177: return size;
178: if (retrieveMessageBodySizeSQL == null) {
179: //There was no SQL statement for this repository... figure it out the hard way
180: System.err.println("no SQL statement to find size");
181: return size = super .getMessageSize();
182: }
183: Connection conn = null;
184: PreparedStatement retrieveMessageSize = null;
185: ResultSet rsRetrieveMessageSize = null;
186: try {
187: conn = repository.getConnection();
188:
189: retrieveMessageSize = conn
190: .prepareStatement(retrieveMessageBodySizeSQL);
191: retrieveMessageSize.setString(1, key);
192: retrieveMessageSize.setString(2, repository.repositoryName);
193: rsRetrieveMessageSize = retrieveMessageSize.executeQuery();
194:
195: if (!rsRetrieveMessageSize.next()) {
196: throw new IOException("Could not find message");
197: }
198:
199: size = rsRetrieveMessageSize.getLong(1);
200:
201: InputStream in = null;
202: try {
203: if (sr != null) {
204: if (sr instanceof org.apache.james.mailrepository.filepair.File_Persistent_Stream_Repository) {
205: size += ((org.apache.james.mailrepository.filepair.File_Persistent_Stream_Repository) sr)
206: .getSize(key);
207: } else {
208: in = sr.get(key);
209: int len = 0;
210: byte[] block = new byte[1024];
211: while ((len = in.read(block)) > -1) {
212: size += len;
213: }
214: }
215: }
216: } catch (Exception e) {
217: //ignore this... either sr is null, or the file does not exist
218: // or something else
219: } finally {
220: try {
221: if (in != null) {
222: in.close();
223: }
224: } catch (IOException ioe) {
225: // Ignored - no access to logger at this point in the code
226: }
227: }
228:
229: return size;
230: } catch (SQLException sqle) {
231: throw new IOException(sqle.toString());
232: } finally {
233: theJDBCUtil.closeJDBCResultSet(rsRetrieveMessageSize);
234: theJDBCUtil.closeJDBCStatement(retrieveMessageSize);
235: theJDBCUtil.closeJDBCConnection(conn);
236: }
237: }
238:
239: /**
240: * Check to see whether this is the same repository and the same key
241: */
242: public boolean equals(Object obj) {
243: if (obj instanceof MimeMessageJDBCSource) {
244: // TODO: Figure out whether other instance variables should be part of
245: // the equals equation
246: MimeMessageJDBCSource source = (MimeMessageJDBCSource) obj;
247: return ((source.key == key) || ((source.key != null) && source.key
248: .equals(key)))
249: && ((source.repository == repository) || ((source.repository != null) && source.repository
250: .equals(repository)));
251: }
252: return false;
253: }
254:
255: /**
256: * Provide a hash code that is consistent with equals for this class
257: *
258: * @return the hash code
259: */
260: public int hashCode() {
261: int result = 17;
262: if (key != null) {
263: result = 37 * key.hashCode();
264: }
265: if (repository != null) {
266: result = 37 * repository.hashCode();
267: }
268: return result;
269: }
270: }
|