1 /************************************************************************
2 * Copyright (c) 2000-2006 The Apache Software Foundation. *
3 * All rights reserved. *
4 * ------------------------------------------------------------------- *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you *
6 * may not use this file except in compliance with the License. You *
7 * may obtain a copy of the License at: *
8 * *
9 * http://www.apache.org/licenses/LICENSE-2.0 *
10 * *
11 * Unless required by applicable law or agreed to in writing, software *
12 * distributed under the License is distributed on an "AS IS" BASIS, *
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14 * implied. See the License for the specific language governing *
15 * permissions and limitations under the License. *
16 ***********************************************************************/
17
18 package org.apache.james.mailrepository;
19
20 import org.apache.avalon.cornerstone.services.store.StreamRepository;
21 import org.apache.james.core.MimeMessageSource;
22 import org.apache.james.util.JDBCUtil;
23
24 import java.io.ByteArrayInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.SequenceInputStream;
28 import java.sql.Blob;
29 import java.sql.Connection;
30 import java.sql.PreparedStatement;
31 import java.sql.ResultSet;
32 import java.sql.SQLException;
33
34 /***
35 * This class points to a specific message in a repository. This will return an
36 * InputStream to the JDBC field/record, possibly sequenced with the file stream.
37 */
38 public class MimeMessageJDBCSource extends MimeMessageSource {
39
40 /***
41 * Whether 'deep debugging' is turned on.
42 */
43 private static final boolean DEEP_DEBUG = false;
44
45
46 JDBCMailRepository repository = null;
47 String key = null;
48 StreamRepository sr = null;
49
50 private long size = -1;
51
52 /***
53 * SQL used to retrieve the message body
54 */
55 String retrieveMessageBodySQL = null;
56
57 /***
58 * SQL used to retrieve the size of the message body
59 */
60 String retrieveMessageBodySizeSQL = null;
61
62 /***
63 * The JDBCUtil helper class
64 */
65 private static final JDBCUtil theJDBCUtil =
66 new JDBCUtil() {
67 protected void delegatedLog(String logString) {
68
69
70 }
71 };
72
73 /***
74 * Construct a MimeMessageSource based on a JDBC repository, a key, and a
75 * stream repository (where we might store the message body)
76 */
77 public MimeMessageJDBCSource(JDBCMailRepository repository,
78 String key, StreamRepository sr) throws IOException {
79 if (repository == null) {
80 throw new IOException("Repository is null");
81 }
82 if (key == null) {
83 throw new IOException("Message name (key) was not defined");
84 }
85 this.repository = repository;
86 this.key = key;
87 this.sr = sr;
88
89 retrieveMessageBodySQL =
90 repository.sqlQueries.getSqlString("retrieveMessageBodySQL", true);
91
92 retrieveMessageBodySizeSQL =
93 repository.sqlQueries.getSqlString("retrieveMessageBodySizeSQL");
94 }
95
96 /***
97 * Returns a unique String ID that represents the location from where
98 * this source is loaded. This will be used to identify where the data
99 * is, primarily to avoid situations where this data would get overwritten.
100 *
101 * @return the String ID
102 */
103 public String getSourceId() {
104 StringBuffer sourceIdBuffer =
105 new StringBuffer(128)
106 .append(repository.repositoryName)
107 .append("/")
108 .append(key);
109 return sourceIdBuffer.toString();
110 }
111
112 /***
113 * Return the input stream to the database field and then the file stream. This should
114 * be smart enough to work even if the file does not exist. This is to support
115 * a repository with the entire message in the database, which is how James 1.2 worked.
116 */
117 public synchronized InputStream getInputStream() throws IOException {
118 Connection conn = null;
119 PreparedStatement retrieveMessageStream = null;
120 ResultSet rsRetrieveMessageStream = null;
121 try {
122 conn = repository.getConnection();
123
124 byte[] headers = null;
125
126 long start = 0;
127 if (DEEP_DEBUG) {
128 start = System.currentTimeMillis();
129 System.out.println("starting");
130 }
131 retrieveMessageStream = conn.prepareStatement(retrieveMessageBodySQL);
132 retrieveMessageStream.setString(1, key);
133 retrieveMessageStream.setString(2, repository.repositoryName);
134 rsRetrieveMessageStream = retrieveMessageStream.executeQuery();
135
136 if (!rsRetrieveMessageStream.next()) {
137 throw new IOException("Could not find message");
138 }
139
140 String getBodyOption = repository.sqlQueries.getDbOption("getBody");
141 if (getBodyOption != null && getBodyOption.equalsIgnoreCase("useBlob")) {
142 Blob b = rsRetrieveMessageStream.getBlob(1);
143 headers = b.getBytes(1, (int)b.length());
144 } else {
145 headers = rsRetrieveMessageStream.getBytes(1);
146 }
147 if (DEEP_DEBUG) {
148 System.err.println("stopping");
149 System.err.println(System.currentTimeMillis() - start);
150 }
151
152 InputStream in = new ByteArrayInputStream(headers);
153 try {
154 if (sr != null) {
155 in = new SequenceInputStream(in, sr.get(key));
156 }
157 } catch (Exception e) {
158
159
160 }
161 return in;
162 } catch (SQLException sqle) {
163 throw new IOException(sqle.toString());
164 } finally {
165 theJDBCUtil.closeJDBCResultSet(rsRetrieveMessageStream);
166 theJDBCUtil.closeJDBCStatement(retrieveMessageStream);
167 theJDBCUtil.closeJDBCConnection(conn);
168 }
169 }
170
171 /***
172 * Runs a custom SQL statement to check the size of the message body
173 */
174 public synchronized long getMessageSize() throws IOException {
175 if (size != -1) return size;
176 if (retrieveMessageBodySizeSQL == null) {
177
178 System.err.println("no SQL statement to find size");
179 return size = super.getMessageSize();
180 }
181 Connection conn = null;
182 PreparedStatement retrieveMessageSize = null;
183 ResultSet rsRetrieveMessageSize = null;
184 try {
185 conn = repository.getConnection();
186
187 retrieveMessageSize = conn.prepareStatement(retrieveMessageBodySizeSQL);
188 retrieveMessageSize.setString(1, key);
189 retrieveMessageSize.setString(2, repository.repositoryName);
190 rsRetrieveMessageSize = retrieveMessageSize.executeQuery();
191
192 if (!rsRetrieveMessageSize.next()) {
193 throw new IOException("Could not find message");
194 }
195
196 size = rsRetrieveMessageSize.getLong(1);
197
198 InputStream in = null;
199 try {
200 if (sr != null) {
201 if (sr instanceof org.apache.james.mailrepository.filepair.File_Persistent_Stream_Repository) {
202 size += ((org.apache.james.mailrepository.filepair.File_Persistent_Stream_Repository) sr).getSize(key);
203 } else {
204 in = sr.get(key);
205 int len = 0;
206 byte[] block = new byte[1024];
207 while ((len = in.read(block)) > -1) {
208 size += len;
209 }
210 }
211 }
212 } catch (Exception e) {
213
214
215 } finally {
216 try {
217 if (in != null) {
218 in.close();
219 }
220 } catch (IOException ioe) {
221
222 }
223 }
224
225 return size;
226 } catch (SQLException sqle) {
227 throw new IOException(sqle.toString());
228 } finally {
229 theJDBCUtil.closeJDBCResultSet(rsRetrieveMessageSize);
230 theJDBCUtil.closeJDBCStatement(retrieveMessageSize);
231 theJDBCUtil.closeJDBCConnection(conn);
232 }
233 }
234
235 /***
236 * Check to see whether this is the same repository and the same key
237 */
238 public boolean equals(Object obj) {
239 if (obj instanceof MimeMessageJDBCSource) {
240
241
242 MimeMessageJDBCSource source = (MimeMessageJDBCSource)obj;
243 return ((source.key == key) || ((source.key != null) && source.key.equals(key))) &&
244 ((source.repository == repository) || ((source.repository != null) && source.repository.equals(repository)));
245 }
246 return false;
247 }
248
249 /***
250 * Provide a hash code that is consistent with equals for this class
251 *
252 * @return the hash code
253 */
254 public int hashCode() {
255 int result = 17;
256 if (key != null) {
257 result = 37 * key.hashCode();
258 }
259 if (repository != null) {
260 result = 37 * repository.hashCode();
261 }
262 return result;
263 }
264 }