1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.james.jcr;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.ObjectInputStream;
24 import java.io.ObjectOutputStream;
25 import java.io.PipedInputStream;
26 import java.io.PipedOutputStream;
27 import java.io.Serializable;
28 import java.util.ArrayList;
29 import java.util.Calendar;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Date;
33 import java.util.Iterator;
34 import java.util.Properties;
35
36 import javax.jcr.Credentials;
37 import javax.jcr.Node;
38 import javax.jcr.NodeIterator;
39 import javax.jcr.PathNotFoundException;
40 import javax.jcr.Property;
41 import javax.jcr.PropertyIterator;
42 import javax.jcr.PropertyType;
43 import javax.jcr.Repository;
44 import javax.jcr.RepositoryException;
45 import javax.jcr.Session;
46 import javax.jcr.Value;
47 import javax.jcr.query.Query;
48 import javax.jcr.query.QueryManager;
49 import javax.mail.MessagingException;
50 import javax.mail.internet.MimeMessage;
51
52 import org.apache.commons.logging.Log;
53 import org.apache.commons.logging.LogFactory;
54 import org.apache.jackrabbit.util.ISO9075;
55 import org.apache.jackrabbit.util.Text;
56 import org.apache.james.core.MailImpl;
57 import org.apache.james.services.MailRepository;
58 import org.apache.mailet.Mail;
59 import org.apache.mailet.MailAddress;
60
61
62
63
64 public class JCRMailRepository extends AbstractJCRRepository implements MailRepository {
65
66
67
68
69 private static final Log LOGGER = LogFactory.getLog(JCRMailRepository.class.getName());
70
71
72
73
74 public JCRMailRepository() {
75 super(LOGGER);
76 this.path = "james/mail";
77 }
78
79
80
81
82
83
84
85
86
87
88
89 public JCRMailRepository(Repository repository, Credentials credentials, String workspace, String path, Log logger) {
90 super(repository, credentials, workspace, path, logger);
91 }
92
93
94
95
96
97 public JCRMailRepository(Repository repository, Log logger) {
98 super(repository, logger);
99 }
100
101
102
103 public Iterator list() throws MessagingException {
104 try {
105 Session session = login();
106 try {
107 Collection keys = new ArrayList();
108 QueryManager manager = session.getWorkspace().getQueryManager();
109 Query query = manager.createQuery(
110 "/jcr:root/" + path + "//element(*,james:mail)",
111 Query.XPATH);
112 NodeIterator iterator = query.execute().getNodes();
113 while (iterator.hasNext()) {
114 String name = iterator.nextNode().getName();
115 keys.add(Text.unescapeIllegalJcrChars(name));
116 }
117 return keys.iterator();
118 } finally {
119 session.logout();
120 }
121 } catch (RepositoryException e) {
122 throw new MessagingException("Unable to list messages", e);
123 }
124 }
125
126 public Mail retrieve(String key) throws MessagingException {
127 try {
128 Session session = login();
129 try {
130 String name = toSafeName(key);
131 QueryManager manager = session.getWorkspace().getQueryManager();
132 Query query = manager.createQuery(
133 "/jcr:root/" + path + "//element(" + name + ",james:mail)",
134 Query.XPATH);
135 NodeIterator iterator = query.execute().getNodes();
136 if (iterator.hasNext()) {
137 return getMail(iterator.nextNode());
138 } else {
139 return null;
140 }
141 } finally {
142 session.logout();
143 }
144 } catch (IOException e) {
145 throw new MessagingException(
146 "Unable to retrieve message: " + key, e);
147 } catch (RepositoryException e) {
148 throw new MessagingException(
149 "Unable to retrieve message: " + key, e);
150 }
151 }
152
153 public void store(Mail mail) throws MessagingException {
154 try {
155 Session session = login();
156 try {
157 String name = Text.escapeIllegalJcrChars(mail.getName());
158 final String xpath = "/jcr:root/" + path + "//element(" + name + ",james:mail)";
159 NodeIterator iterator = query(session, xpath);
160 if (iterator.hasNext()) {
161 while (iterator.hasNext()) {
162 setMail(iterator.nextNode(), mail);
163 }
164 } else {
165 Node parent = session.getRootNode().getNode(path);
166 Node node = parent.addNode(name, "james:mail");
167 Node resource = node.addNode("jcr:content", "nt:resource");
168 resource.setProperty("jcr:mimeType", "message/rfc822");
169 setMail(node, mail);
170 }
171 session.save();
172 logger.info("Mail " + mail.getName() + " stored in repository");
173 } finally {
174 session.logout();
175 }
176 } catch (IOException e) {
177 throw new MessagingException(
178 "Unable to store message: " + mail.getName(), e);
179 } catch (RepositoryException e) {
180 throw new MessagingException(
181 "Unable to store message: " + mail.getName(), e);
182 }
183 }
184
185 public void remove(String key) throws MessagingException {
186 try {
187 Session session = login();
188 try {
189 String name = ISO9075.encode(Text.escapeIllegalJcrChars(key));
190 QueryManager manager = session.getWorkspace().getQueryManager();
191 Query query = manager.createQuery(
192 "/jcr:root/" + path + "//element(" + name + ",james:mail)",
193 Query.XPATH);
194 NodeIterator nodes = query.execute().getNodes();
195 if (nodes.hasNext()) {
196 while (nodes.hasNext()) {
197 nodes.nextNode().remove();
198 }
199 session.save();
200 logger.info("Mail " + key + " removed from repository");
201 } else {
202 logger.warn("Mail " + key + " not found");
203 }
204 } finally {
205 session.logout();
206 }
207 } catch (RepositoryException e) {
208 throw new MessagingException("Unable to remove message: " + key, e);
209 }
210 }
211
212 public void remove(Mail mail) throws MessagingException {
213 remove(mail.getName());
214 }
215
216 public void remove(Collection mails) throws MessagingException {
217 try {
218 Session session = login();
219 try {
220 QueryManager manager = session.getWorkspace().getQueryManager();
221 Iterator iterator = mails.iterator();
222 while (iterator.hasNext()) {
223 Mail mail = (Mail) iterator.next();
224 try {
225 String name = ISO9075.encode(
226 Text.escapeIllegalJcrChars(mail.getName()));
227 Query query = manager.createQuery(
228 "/jcr:root/" + path + "//element(" + name + ",james:mail)",
229 Query.XPATH);
230 NodeIterator nodes = query.execute().getNodes();
231 while (nodes.hasNext()) {
232 nodes.nextNode().remove();
233 }
234 } catch (PathNotFoundException e) {
235 logger.warn("Mail " + mail.getName() + " not found");
236 }
237 }
238 session.save();
239 logger.info("Mail collection removed from repository");
240 } finally {
241 session.logout();
242 }
243 } catch (RepositoryException e) {
244 throw new MessagingException("Unable to remove messages", e);
245 }
246 }
247
248 public boolean lock(String key) throws MessagingException {
249 return false;
250 }
251
252 public boolean unlock(String key) throws MessagingException {
253 return false;
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267 private Mail getMail(Node node)
268 throws MessagingException, RepositoryException, IOException {
269 String name = Text.unescapeIllegalJcrChars(node.getName());
270 MailImpl mail = new MailImpl(
271 name, getSender(node), getRecipients(node),
272 getMessage(node));
273 mail.setState(getState(node));
274 mail.setLastUpdated(getLastUpdated(node));
275 mail.setErrorMessage(getError(node));
276 mail.setRemoteHost(getRemoteHost(node));
277 mail.setRemoteAddr(getRemoteAddr(node));
278 getAttributes(node, mail);
279 return mail;
280 }
281
282
283
284
285
286
287
288
289
290
291 private void setMail(Node node, Mail mail)
292 throws MessagingException, RepositoryException, IOException {
293 setState(node, mail.getState());
294 setLastUpdated(node, mail.getLastUpdated());
295 setError(node, mail.getErrorMessage());
296 setRemoteHost(node, mail.getRemoteHost());
297 setRemoteAddr(node, mail.getRemoteAddr());
298 setSender(node, mail.getSender());
299 setRecipients(node, mail.getRecipients());
300 setMessage(node, mail.getMessage());
301 setAttributes(node, mail);
302 }
303
304
305
306
307
308
309
310
311 private String getState(Node node) throws RepositoryException {
312 try {
313 return node.getProperty("james:state").getString();
314 } catch (PathNotFoundException e) {
315 return Mail.DEFAULT;
316 }
317 }
318
319
320
321
322
323
324
325
326 private void setState(Node node, String state) throws RepositoryException {
327 node.setProperty("james:state", state);
328 }
329
330
331
332
333
334
335
336
337 private Date getLastUpdated(Node node) throws RepositoryException {
338 try {
339 node = node.getNode("jcr:content");
340 } catch (PathNotFoundException e) {
341 node = node.getProperty("jcr:content").getNode();
342 }
343 return node.getProperty("jcr:lastModified").getDate().getTime();
344 }
345
346
347
348
349
350
351
352
353 private void setLastUpdated(Node node, Date updated)
354 throws RepositoryException {
355 try {
356 node = node.getNode("jcr:content");
357 } catch (PathNotFoundException e) {
358 node = node.getProperty("jcr:content").getNode();
359 }
360 Calendar calendar = Calendar.getInstance();
361 if (updated != null) {
362 calendar.setTime(updated);
363 }
364 node.setProperty("jcr:lastModified", calendar);
365 }
366
367
368
369
370
371
372
373
374 private String getError(Node node) throws RepositoryException {
375 try {
376 return node.getProperty("james:error").getString();
377 } catch (PathNotFoundException e) {
378 return null;
379 }
380 }
381
382
383
384
385
386
387
388
389 private void setError(Node node, String error) throws RepositoryException {
390 node.setProperty("james:error", error);
391 }
392
393
394
395
396
397
398
399
400 private String getRemoteHost(Node node) throws RepositoryException {
401 try {
402 return node.getProperty("james:remotehost").getString();
403 } catch (PathNotFoundException e) {
404 return null;
405 }
406 }
407
408
409
410
411
412
413
414
415 private void setRemoteHost(Node node, String host)
416 throws RepositoryException {
417 node.setProperty("james:remotehost", host);
418 }
419
420
421
422
423
424
425
426
427 private String getRemoteAddr(Node node) throws RepositoryException {
428 try {
429 return node.getProperty("james:remoteaddr").getString();
430 } catch (PathNotFoundException e) {
431 return null;
432 }
433 }
434
435
436
437
438
439
440
441
442 private void setRemoteAddr(Node node, String addr)
443 throws RepositoryException {
444 node.setProperty("james:remoteaddr", addr);
445 }
446
447
448
449
450
451
452
453
454
455 private MailAddress getSender(Node node)
456 throws MessagingException, RepositoryException {
457 try {
458 String sender = node.getProperty("james:sender").getString();
459 return new MailAddress(sender);
460 } catch (PathNotFoundException e) {
461 return null;
462 }
463 }
464
465
466
467
468
469
470
471
472
473 private void setSender(Node node, MailAddress sender)
474 throws MessagingException, RepositoryException {
475 node.setProperty("james:sender", sender.toString());
476 }
477
478
479
480
481
482
483
484
485
486 private Collection getRecipients(Node node)
487 throws MessagingException, RepositoryException {
488 try {
489 Value[] values = node.getProperty("james:recipients").getValues();
490 Collection recipients = new ArrayList(values.length);
491 for (int i = 0; i < values.length; i++) {
492 recipients.add(new MailAddress(values[i].getString()));
493 }
494 return recipients;
495 } catch (PathNotFoundException e) {
496 return Collections.EMPTY_LIST;
497 }
498 }
499
500
501
502
503
504
505
506
507
508 private void setRecipients(Node node, Collection recipients)
509 throws MessagingException, RepositoryException {
510 String[] values = new String[recipients.size()];
511 Iterator iterator = recipients.iterator();
512 for (int i = 0; iterator.hasNext(); i++) {
513 values[i] = iterator.next().toString();
514 }
515 node.setProperty("james:recipients", values);
516 }
517
518
519
520
521
522
523
524
525
526
527 private MimeMessage getMessage(Node node)
528 throws MessagingException, RepositoryException, IOException {
529 try {
530 node = node.getNode("jcr:content");
531 } catch (PathNotFoundException e) {
532 node = node.getProperty("jcr:content").getNode();
533 }
534
535 InputStream stream = node.getProperty("jcr:data").getStream();
536 try {
537 Properties properties = System.getProperties();
538 return new MimeMessage(
539 javax.mail.Session.getDefaultInstance(properties),
540 stream);
541 } finally {
542 stream.close();
543 }
544 }
545
546
547
548
549
550
551
552
553
554
555 private void setMessage(Node node, final MimeMessage message)
556 throws MessagingException, RepositoryException, IOException {
557 try {
558 node = node.getNode("jcr:content");
559 } catch (PathNotFoundException e) {
560 node = node.getProperty("jcr:content").getNode();
561 }
562
563 PipedInputStream input = new PipedInputStream();
564 final PipedOutputStream output = new PipedOutputStream(input);
565 new Thread() {
566 public void run() {
567 try {
568 message.writeTo(output);
569 } catch (Exception e) {
570 } finally {
571 try {
572 output.close();
573 } catch (IOException e) {
574 }
575 }
576 }
577 }.start();
578 node.setProperty("jcr:data", input);
579 }
580
581
582
583
584
585
586
587
588
589 private void getAttributes(Node node, Mail mail)
590 throws RepositoryException, IOException {
591 PropertyIterator iterator = node.getProperties("jamesattr:*");
592 while (iterator.hasNext()) {
593 Property property = iterator.nextProperty();
594 String name = Text.unescapeIllegalJcrChars(
595 property.getName().substring("jamesattr:".length()));
596 if (property.getType() == PropertyType.BINARY) {
597 InputStream input = property.getStream();
598 try {
599 ObjectInputStream stream = new ObjectInputStream(input);
600 mail.setAttribute(name, (Serializable) stream.readObject());
601 } catch (ClassNotFoundException e) {
602 throw new IOException(e.getMessage());
603 } finally {
604 input.close();
605 }
606 } else {
607 mail.setAttribute(name, property.getString());
608 }
609 }
610 }
611
612
613
614
615
616
617
618
619
620 private void setAttributes(Node node, Mail mail)
621 throws RepositoryException, IOException {
622 Iterator iterator = mail.getAttributeNames();
623 while (iterator.hasNext()) {
624 String name = (String) iterator.next();
625 Object value = mail.getAttribute(name);
626 name = "jamesattr:" + Text.escapeIllegalJcrChars(name);
627 if (value instanceof String || value == null) {
628 node.setProperty(name, (String) value);
629 } else {
630 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
631 ObjectOutputStream output = new ObjectOutputStream(buffer);
632 output.writeObject(value);
633 output.close();
634 node.setProperty(
635 name,
636 new ByteArrayInputStream(buffer.toByteArray()));
637 }
638 }
639 }
640
641 }