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