Coverage Report - com.jcabi.email.Envelope
 
Classes in this File Line Coverage Branch Coverage Complexity
Envelope
N/A
N/A
2.294
Envelope$1
100%
2/2
N/A
2.294
Envelope$Constant
60%
6/10
0%
0/12
2.294
Envelope$Constant$1
100%
2/2
N/A
2.294
Envelope$Draft
0%
0/11
0%
0/12
2.294
Envelope$MIME
84%
21/25
25%
4/16
2.294
Envelope$Safe
47%
9/19
16%
3/18
2.294
Envelope$Strict
56%
9/16
16%
3/18
2.294
 
 1  
 /**
 2  
  * Copyright (c) 2014-2017, jcabi.com
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions
 7  
  * are met: 1) Redistributions of source code must retain the above
 8  
  * copyright notice, this list of conditions and the following
 9  
  * disclaimer. 2) Redistributions in binary form must reproduce the above
 10  
  * copyright notice, this list of conditions and the following
 11  
  * disclaimer in the documentation and/or other materials provided
 12  
  * with the distribution. 3) Neither the name of the jcabi.com nor
 13  
  * the names of its contributors may be used to endorse or promote
 14  
  * products derived from this software without specific prior written
 15  
  * permission.
 16  
  *
 17  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 19  
  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 20  
  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 21  
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 22  
  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24  
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 25  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 26  
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 27  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 28  
  * OF THE POSSIBILITY OF SUCH DAMAGE.
 29  
  */
 30  
 package com.jcabi.email;
 31  
 
 32  
 import com.google.common.cache.Cache;
 33  
 import com.google.common.cache.CacheBuilder;
 34  
 import com.jcabi.aspects.Immutable;
 35  
 import com.jcabi.aspects.Loggable;
 36  
 import com.jcabi.immutable.Array;
 37  
 import com.jcabi.log.Logger;
 38  
 import java.io.IOException;
 39  
 import java.util.Properties;
 40  
 import java.util.concurrent.Callable;
 41  
 import java.util.concurrent.ExecutionException;
 42  
 import java.util.concurrent.TimeUnit;
 43  
 import javax.mail.Flags;
 44  
 import javax.mail.Message;
 45  
 import javax.mail.MessagingException;
 46  
 import javax.mail.Multipart;
 47  
 import javax.mail.Session;
 48  
 import javax.mail.internet.InternetAddress;
 49  
 import javax.mail.internet.MimeMessage;
 50  
 import javax.mail.internet.MimeMultipart;
 51  
 import lombok.EqualsAndHashCode;
 52  
 import lombok.ToString;
 53  
 
 54  
 /**
 55  
  * Envelope.
 56  
  *
 57  
  * <p>The best way is to use {@link com.jcabi.email.Envelope.MIME}, but
 58  
  * you can easily create your own implementation of it.
 59  
  *
 60  
  * <p>It is recommended to always wrap your envelope into
 61  
  * {@link com.jcabi.email.Envelope.Safe}.
 62  
  *
 63  
  * @author Yegor Bugayenko (yegor@teamed.io)
 64  
  * @version $Id: a1f5c0329b2d3b009e4301aaf21c9773a180e42f $
 65  
  * @since 1.0
 66  
  */
 67  
 @Immutable
 68  
 public interface Envelope {
 69  
 
 70  
     /**
 71  
      * Empty (always returns an empty MIME message).
 72  
      * @since 1.5
 73  
      */
 74  1
     Envelope EMPTY = new Envelope() {
 75  
         @Override
 76  
         public Message unwrap() {
 77  7
             return new MimeMessage(
 78  
                 Session.getDefaultInstance(new Properties())
 79  
             );
 80  
         }
 81  
     };
 82  
 
 83  
     /**
 84  
      * Get a message out of it.
 85  
      * @return Message to send
 86  
      * @throws IOException If fails
 87  
      */
 88  
     Message unwrap() throws IOException;
 89  
 
 90  
     /**
 91  
      * Default envelope.
 92  
      */
 93  
     @Immutable
 94  0
     @ToString
 95  0
     @EqualsAndHashCode(of = "encs")
 96  
     @Loggable(Loggable.DEBUG)
 97  
     final class MIME implements Envelope {
 98  
         /**
 99  
          * List of stamps.
 100  
          */
 101  
         private final transient Array<Stamp> stamps;
 102  
         /**
 103  
          * List of enclosures.
 104  
          */
 105  
         private final transient Array<Enclosure> encs;
 106  
         /**
 107  
          * Ctor.
 108  
          * @since 1.3
 109  
          */
 110  
         public MIME() {
 111  7
             this(new Array<Stamp>(), new Array<Enclosure>());
 112  7
         }
 113  
         /**
 114  
          * Ctor.
 115  
          * @param env Original envelope
 116  
          * @since 1.5
 117  
          */
 118  
         public MIME(final Envelope env) {
 119  2
             this(
 120  
                 Envelope.MIME.class.cast(env).stamps,
 121  
                 Envelope.MIME.class.cast(env).encs
 122  
             );
 123  2
         }
 124  
         /**
 125  
          * Ctor.
 126  
          * @param stmps Stamps
 127  
          * @param list List of enclosures
 128  
          */
 129  
         public MIME(final Iterable<Stamp> stmps,
 130  33
             final Iterable<Enclosure> list) {
 131  33
             this.stamps = new Array<>(stmps);
 132  33
             this.encs = new Array<>(list);
 133  33
         }
 134  
         @Override
 135  
         public Message unwrap() throws IOException {
 136  7
             final Message msg = Envelope.EMPTY.unwrap();
 137  7
             final Multipart multi = new MimeMultipart("alternative");
 138  
             try {
 139  7
                 for (final Enclosure enc : this.encs) {
 140  6
                     multi.addBodyPart(enc.part());
 141  6
                 }
 142  7
                 for (final Stamp stamp : this.stamps) {
 143  18
                     stamp.attach(msg);
 144  18
                 }
 145  7
                 msg.setContent(multi);
 146  0
             } catch (final MessagingException ex) {
 147  0
                 throw new IOException(ex);
 148  7
             }
 149  7
             return msg;
 150  
         }
 151  
         /**
 152  
          * With this stamp.
 153  
          * @param stamp Stamp
 154  
          * @return MIME envelope
 155  
          * @since 1.3
 156  
          */
 157  
         public Envelope.MIME with(final Stamp stamp) {
 158  18
             return new Envelope.MIME(
 159  
                 this.stamps.with(stamp),
 160  
                 this.encs
 161  
             );
 162  
         }
 163  
         /**
 164  
          * With this enclosure.
 165  
          * @param enc Enclosure
 166  
          * @return MIME envelope
 167  
          * @since 1.3
 168  
          */
 169  
         public Envelope.MIME with(final Enclosure enc) {
 170  6
             return new Envelope.MIME(
 171  
                 this.stamps,
 172  
                 this.encs.with(enc)
 173  
             );
 174  
         }
 175  
     }
 176  
 
 177  
     /**
 178  
      * Strict envelope that fails if message is not valid.
 179  
      * @since 1.3
 180  
      */
 181  
     @Immutable
 182  0
     @ToString
 183  0
     @EqualsAndHashCode(of = "origin")
 184  
     @Loggable(Loggable.DEBUG)
 185  
     final class Strict implements Envelope {
 186  
         /**
 187  
          * Origin env.
 188  
          */
 189  
         private final transient Envelope origin;
 190  
         /**
 191  
          * Ctor.
 192  
          * @param env Envelope
 193  
          */
 194  2
         public Strict(final Envelope env) {
 195  2
             this.origin = env;
 196  2
         }
 197  
         @Override
 198  
         public Message unwrap() throws IOException {
 199  2
             final Message msg = this.origin.unwrap();
 200  
             try {
 201  2
                 if (msg.getAllRecipients() == null) {
 202  0
                     throw new IllegalStateException(
 203  
                         "list of recipients is NULL"
 204  
                     );
 205  
                 }
 206  2
                 if (msg.getFrom() == null) {
 207  0
                     throw new IllegalStateException(
 208  
                         "list of senders is NULL"
 209  
                     );
 210  
                 }
 211  2
                 if (msg.getSubject() == null) {
 212  0
                     throw new IllegalStateException(
 213  
                         "subject is NULL"
 214  
                     );
 215  
                 }
 216  0
             } catch (final MessagingException ex) {
 217  0
                 throw new IOException(ex);
 218  2
             }
 219  2
             return msg;
 220  
         }
 221  
     }
 222  
 
 223  
     /**
 224  
      * Safe envelope that adds missing parts to the message.
 225  
      * @since 1.3
 226  
      */
 227  
     @Immutable
 228  0
     @ToString
 229  0
     @EqualsAndHashCode(of = "origin")
 230  
     @Loggable(Loggable.DEBUG)
 231  
     final class Safe implements Envelope {
 232  
         /**
 233  
          * Origin env.
 234  
          */
 235  
         private final transient Envelope origin;
 236  
         /**
 237  
          * Ctor.
 238  
          * @param env Envelope
 239  
          */
 240  2
         public Safe(final Envelope env) {
 241  2
             this.origin = env;
 242  2
         }
 243  
         @Override
 244  
         public Message unwrap() throws IOException {
 245  2
             final Message msg = this.origin.unwrap();
 246  
             try {
 247  2
                 if (msg.getAllRecipients() == null) {
 248  0
                     msg.addRecipient(
 249  
                         Message.RecipientType.TO,
 250  
                         new InternetAddress("to@example.com")
 251  
                     );
 252  0
                     Logger.warn(this, "recipients were NULL, fake one set");
 253  
                 }
 254  2
                 if (msg.getFrom() == null) {
 255  0
                     msg.setFrom(new InternetAddress("from@example.com"));
 256  0
                     Logger.warn(this, "senders were NULL, fake one set");
 257  
                 }
 258  2
                 if (msg.getSubject() == null) {
 259  0
                     msg.setSubject(this.getClass().getCanonicalName());
 260  0
                     Logger.warn(this, "subject was NULL, fake one set");
 261  
                 }
 262  0
             } catch (final MessagingException ex) {
 263  0
                 throw new IOException(ex);
 264  2
             }
 265  2
             return msg;
 266  
         }
 267  
     }
 268  
 
 269  
     /**
 270  
      * Envelope that always returns the same message (within one hour).
 271  
      * @since 1.4
 272  
      */
 273  
     @Immutable
 274  0
     @ToString
 275  0
     @EqualsAndHashCode(of = "origin")
 276  
     @Loggable(Loggable.DEBUG)
 277  1
     final class Constant implements Envelope {
 278  
         /**
 279  
          * Guava cache.
 280  
          */
 281  1
         private static final Cache<Envelope, Message> CACHE =
 282  
             CacheBuilder.newBuilder()
 283  
                 .expireAfterWrite(1L, TimeUnit.HOURS)
 284  
                 .build();
 285  
         /**
 286  
          * Origin env.
 287  
          */
 288  
         private final transient Envelope origin;
 289  
         /**
 290  
          * Ctor.
 291  
          * @param env Envelope
 292  
          */
 293  1
         public Constant(final Envelope env) {
 294  1
             this.origin = env;
 295  1
         }
 296  
         @Override
 297  
         public Message unwrap() throws IOException {
 298  
             try {
 299  2
                 return Envelope.Constant.CACHE.get(
 300  
                     this.origin,
 301  3
                     new Callable<Message>() {
 302  
                         @Override
 303  
                         public Message call() throws Exception {
 304  1
                             return Envelope.Constant.this.origin.unwrap();
 305  
                         }
 306  
                     }
 307  
                 );
 308  0
             } catch (final ExecutionException ex) {
 309  0
                 throw new IOException(ex);
 310  
             }
 311  
         }
 312  
     }
 313  
 
 314  
     /**
 315  
      * Envelope that adds DRAFT flag to the message.
 316  
      * @since 1.7
 317  
      */
 318  
     @Immutable
 319  0
     @ToString
 320  0
     @EqualsAndHashCode(of = "env")
 321  
     @Loggable(Loggable.DEBUG)
 322  
     final class Draft implements Envelope {
 323  
         /**
 324  
          * Origin env.
 325  
          */
 326  
         private final transient Envelope env;
 327  
         /**
 328  
          * Ctor.
 329  
          * @param origin Envelope
 330  
          */
 331  0
         public Draft(final Envelope origin) {
 332  0
             this.env = origin;
 333  0
         }
 334  
         @Override
 335  
         public Message unwrap() throws IOException {
 336  0
             final Message msg = this.env.unwrap();
 337  
             try {
 338  0
                 msg.setFlag(Flags.Flag.DRAFT, true);
 339  0
             } catch (final MessagingException ex) {
 340  0
                 throw new IOException(ex);
 341  0
             }
 342  0
             return msg;
 343  
         }
 344  
     }
 345  
 
 346  
 }