Java, Only Java!

统计

留言簿(20)

积分与排名

好友空间

文档技巧

阅读排行榜

评论排行榜

Apusic上用JavaMail发邮件

Apusic上用JavaMail发邮件
0. JavaMail基本介绍
 JavaMail是属于J2EE框架中的一部分,主要是为简化Mail部分开发工作。使用JavaMail发送邮件需要以下步骤:
 1)初始化Session实例;
 在初始化Session实例中有两种方式:使用JNDI初始化和在代码中自行完成初始化。
 ★ 如果SMTP不需要认证,则不再做其他工作;
 ★ 如果SMTP需要认证,则确定在Session中提供嵌入认证信息,还是由3)Transport完成认证过程。
 2)初始化Message实例,填充相关信息;
 3)初始化Transport实例,连接到远程SMTP服务器,发送邮件。
 在初始化Transport实例时也有两种情况:
 ★ 如果SMTP不需要认证,可以直接调用send()函数发送邮件,应用服务器会在后台调用connect()函数完成谁,进行邮件发送;
 ★ 如果SMTP需要认证,需要调用connect()函数,并提供认证需要的用户名/密码,才可以正确发送邮件。
 
1. javax.mail.Session的初始化
1.1. 使用JNDI初始化(配置JavaMail的JNDI)
在Apusic的J2EE应用中找到apusic-application.xml文件,增加<mail-session>部分,示例如下:
<apusic-application>
 <module uri="web.war">
  <web />
 </module>
 <mail-session>
  <jndi-name>javamail/myMail</jndi-name>
  <property name="mail.transport.protocol" value="smtp" />
  <property name="mail.smtp.host" value="smtp.163.com" />
  <property name="mail.smtp.user" value="user" />
  <property name="mail.smtp.password" value="password" />
  <property name="mail.smtp.auth" value="true" />
  <property name="mail.from" value="user@163.com" />
 </mail-session>
</apusic-application>

1.1.1. 通过JNDI找到JavaMail
1.1.1.1. 使用远程访问获得JavaMail
 Hashtable env=new Hashtable();
 env.put(Context.INITIAL_CONTEXT_FACTORY,"com.apusic.naming.jndi.CNContextFactory");
 env.put(Context.PROVIDER_URL,"iiop://localhost:6888");
 //插入相关验证信息
 env.put(Context.SECURITY_CREDENTIALS,"admin" ) ;
 env.put(Context.SECURITY_PRINCIPAL,"admin");
 Context initCtx=new InitialContext(env);
 System.out.println(initCtx.PROVIDER_URL);
 //通过RMI 取得
 Session myMailSession = (Session) initCtx.lookup("javamail/myMailNoAuth");
1.1.1.2. 使用Apusic默认方式获得JavaMail
 Context initCtx = new InitialContext();
 Session myMailSession = (Session) initCtx.lookup("javamail/myMailNoAuth");
 System.out.println(myMailSession.getProperty("mail.smtp.host"));
 
1.1.2. 通过资源注入配置JavaMail
 @Resource(mappedName = "javamail/myMailAuth", type = javax.mail.Session.class, shareable = true, authenticationType = Resource.AuthenticationType.CONTAINER, description = "my email with auth")
 private Session myAuthMailSession;
或者
 @Resource(mappedName = "javamail/myMailAuth")
 private Session myAuthMailSession;

1.2. 在代码中初始化
1.2.1. 无须认证的初始化
 final Properties props = new Properties();
 props.put("mail.transport.protocol", "smtp");
 props.put("mail.smtp.auth", "false");
 props.put("mail.smtp.host", "localhost");
 Session myMailSession = Session.getInstance(props);
1.2.2. 需要认证的初始化(Session实例构造时提供认证支持)
 final Properties props = new Properties();
 props.put("mail.transport.protocol", "smtp");
 props.put("mail.smtp.auth", "true");
 props.put("mail.smtp.host", "smtp.163.com");
 Session myMailSession = Session.getInstance(props, new Authenticator() {
  public PasswordAuthentication getPasswordAuthentication() {
   return new PasswordAuthentication("user", "password");} });
1.2.3. 需要认证的初始化(Session配置参数提供认证支持)
 myMailSession.setPasswordAuthentication(
  new URLName(props.getProperty("mail.transport.protocol")+"://"+  
                 props.getProperty("mail.smtp.user")+"@"+  
                 props.getProperty("mail.smtp.host")),  
        new PasswordAuthentication(props.getProperty("mail.smtp.user"),  
                 props.getProperty("mail.smtp.password")));  

1.2.4. 需要认证的初始化(Transport配置参数提供认证支持)
  // 发送邮件
  javax.mail.Transport myTransport = myMailSession.getTransport("smtp");
  myTransport.connect("smtp.163.com", "user", "password");
  myTransport.sendMessage(msg, msg.getRecipients(Message.RecipientType.TO));
  myTransport.close();
   
1.4. 对Session调试的配置
● 可以在mail-session中加入<property name="mail.debug" value="true" />
● 可以在Session初始化前加入props.put("mail.debug", "true");
● 可以在Session初始化后,通过代码控制myMailSession.setDebug(true);

1.5. Properties的解释
◆ mail.store.protocol:用于检索邮件的协议
◆ mail.transport.protocol:用于传送邮件的协议
◆ mail.host:默认的主机名,默认是本地计算机。
◆ mail.user:默认的用户名。
◆ mail.from:默认的返回地址。
◆ mail.debug:是否输出DEBUG信息。默认为False。
◆ mail.protocol.host:指定协议的主机名,没有指定就用mail.host。例如:mail.smtp.host=smtp.163.com
◆ mail.protocol.user:指定协议的用户名,没有指定就用mail.user。例如:mail.smtp.user=user
◆ mail.protocol.from:指定协议的返回地址,没有指定就用mail.from。例如:mail.smtp.from=user@163.com
◆ mail.smtp.auth:如果是True,就会登录SMTP服务器,获得授权才能发送邮件。默认为False。


2. 通过JavaMail发送邮件
2.1. 通过无须认证的SMTP发邮件
  final Properties props = new Properties();
  props.put("mail.transport.protocol", "smtp");
  props.put("mail.smtp.auth", "false");
  props.put("mail.smtp.host", "localhost");
  try {
   Session myMailSession = Session.getInstance(props);
   myMailSession.setDebug(true); // 打开DEBUG模式
   Message msg = new MimeMessage(myMailSession);
   msg.setFrom(new InternetAddress("user@163.com"));
   msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("zhuyuanxiang@apusic.com"));
   msg.setContent("I have a email test!", "text/plain");
   msg.setSentDate(new java.util.Date());
   msg.setSubject("Test");
   msg.setText("This is a test!\n");
   System.out.println("1.Please wait for sending one...");

   // 发送邮件
   // javax.mail.Transport.send(msg); // 与下面四行的功能一样
   javax.mail.Transport myTransport = myMailSession.getTransport("smtp");
   myTransport.connect();
   myTransport.sendMessage(msg, msg.getRecipients(Message.RecipientType.TO));
   myTransport.close();
   
   System.out.println("2.Your message had send!");
  } catch (Exception error) {
   System.out.println("*.I am sorry to tell you the fail for " + error);
  }

注:测试可以使用Microsft IIS SMTP 服务,安装好启动服务后,还需要进入“IIS管理器”,增加一个“远程域”,对于“远程域”的“出站安全性”需要把用户名和密码加上(就是平时发邮件时登录用的用户名/密码),否则邮件发不出去。

2.2. 通过需要认证的SMTP发邮件
2.2.1. 需要认证的初始化(Session实例构造时提供认证支持)
 final Properties props = new Properties();
 props.put("mail.transport.protocol", "smtp");
 props.put("mail.smtp.auth", "true");
 props.put("mail.smtp.host", "smtp.163.com");

 try {
  Session myMailSession = Session.getInstance(props, new Authenticator() {
   public PasswordAuthentication getPasswordAuthentication() {
    return new PasswordAuthentication("user", "password");
   }
  });
  myMailSession.setDebug(true); // 打开DEBUG模式
  InternetAddress address = new InternetAddress("user@163.com");
  String message = "I have a email test!";
  Message msg = new MimeMessage(myMailSession);
  msg.setFrom(address);
  msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("zhuyuanxiang@apusic.com"));
  msg.setContent(message, "text/plain");
  msg.setSentDate(new java.util.Date());
  msg.setSubject("Test");
  msg.setText("This is a test!\n");
  out.println("1.Please wait for sending...");
  
  // 发送邮件
  javax.mail.Transport myTransport = myMailSession.getTransport("smtp");
  myTransport.connect();
  myTransport.sendMessage(msg, msg.getRecipients(Message.RecipientType.TO));
  myTransport.close();
  // javax.mail.Transport.send(msg); // 注释上四行,打开这行代码,功能一样
  out.println("2.Your message had send!");
 } catch (Exception error) {
  out.println("*.I am sorry to tell you the fail for " + error);
 }

2.2.2. 需要认证的初始化(Session配置参数提供认证支持)
 final Properties props = new Properties();
 props.put("mail.transport.protocol", "smtp");
 props.put("mail.smtp.auth", "true");
 props.put("mail.smtp.host", "smtp.163.com");

 try {
  Session myMailSession = Session.getInstance(props);
  myMailSession.setDebug(true); // 打开DEBUG模式
  myAuthMailSession.setPasswordAuthentication(
   new URLName("smtp://user@host", new PasswordAuthentication("user","password")); 
  InternetAddress address = new InternetAddress("user@163.com");
  String message = "I have a email test!";
  Message msg = new MimeMessage(myMailSession);
  msg.setFrom(address);
  msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("zhuyuanxiang@apusic.com"));
  msg.setContent(message, "text/plain");
  msg.setSentDate(new java.util.Date());
  msg.setSubject("Test");
  msg.setText("This is a test!\n");
  out.println("1.Please wait for sending...");
  
  // 发送邮件
  javax.mail.Transport myTransport = myMailSession.getTransport("smtp");
  myTransport.connect();
  myTransport.sendMessage(msg, msg.getRecipients(Message.RecipientType.TO));
  myTransport.close();
  // javax.mail.Transport.send(msg); // 注释上四行,打开这行代码,功能一样
  out.println("2.Your message had send!");
 } catch (Exception error) {
  out.println("*.I am sorry to tell you the fail for " + error);
 }

2.2.3. 需要认证的初始化(Transport配置参数提供认证支持)
  final Properties props = new Properties();
  props.put("mail.transport.protocol", "smtp");
  props.put("mail.smtp.auth", "true");

  try {
   Session myMailSession = Session.getInstance(props);
   myMailSession.setDebug(true); // 打开DEBUG模式
   Message msg = new MimeMessage(myMailSession);
   msg.setFrom(new InternetAddress("user@163.com"));
   msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("zhuyuanxiang@apusic.com"));
   msg.setContent("I have a email test!", "text/plain");
   msg.setSentDate(new java.util.Date());
   msg.setSubject("Test");
   msg.setText("This is a test!\n");
   System.out.println("1.Please wait for sending two...");

   // 发送邮件
   javax.mail.Transport myTransport = myMailSession.getTransport("smtp");
   myTransport.connect("smtp.163.com", "user", "password");
   myTransport.sendMessage(msg, msg.getRecipients(Message.RecipientType.TO));
   myTransport.close();
   // javax.mail.Transport.send(msg); // 这行不能使用。
   System.out.println("2.Your message had send!");
  } catch (Exception error) {
   System.out.println("*.I am sorry to tell you the fail for " + error);
  }
 }

注:资源注入的Session发送邮件时:
无须认证的SMTP,可以参考2.1.
需要认证的SMTP,可以参考2.2.1.

参考:
http://java.sun.com/products/javamail/javadocs/com/sun/mail/smtp/package-summary.html
http://commons.apache.org/email/userguide.html
http://www.blogjava.net/Unmi/archive/2007/06/07/124179.html
http://unmi.blogcn.com/diary,117630488.shtml


Apusic Stduio打包的示例工程。
http://zhuyuanxiang.javaeye.com/topics/download/d6f5c3a9-9cd9-30be-9b64-9e311900b304

备注:本文耗时1周完成。

posted on 2008-09-04 12:11 zYx.Tom 阅读(1644) 评论(7)  编辑  收藏 所属分类: 1.Java世界

评论

# re: Apusic上用JavaMail发邮件 2008-09-04 12:34 隔叶黄莺

说真的,Apusic 用处理 JavaMail 资源还稍欠了些,理想的状态应该是代码只要写成如下:

Session myMailSession = (Session)ctx.lookup("mail/mailSession");
Message msg = new MimeMessage(myMailSession);
......
Transport.send(msg);

然后要不要验证,由配置属性 mail.smtp.auth 来自行决定,Spring 是做到了这一点的。

要是像上面那样,代码基本完成了全过程,看是用不着在 Apusic 进行相关的配置的。我觉得这一点上 Apusic 应该要有所改进。  回复  更多评论   

# re: Apusic上用JavaMail发邮件 2008-09-04 12:48 朱远翔-Apusic技术顾问

@隔叶黄莺
Apusic也是这样处理的呀?
而且是否认证其实跟Apusic无关,这个是Sun提供的JavaMail包里面决定的。
个人感觉发送时登录过程,无论哪个应用服务器都需要按照上面的代码操作吧。
我对Spring的JavaMail了解不多,是否方便给出完整的代码进行讨论,谢谢。
  回复  更多评论   

# re: Apusic上用JavaMail发邮件 2008-09-04 14:19 隔叶黄莺

WAS5.1也是要自己登录,而且用户名和密码必须以额外的属性进行设置,然后在程序中取出来,所以我没去用 WAS 的 MailSession 资源配置。或是自己程序处理。
或是用 Spring 的配置,就比较轻松,Spring 会自己完成登录过程。

我之前写过一个 《JavaMail 发邮件需要 SMTP 验证的三种处理方式》(http://www.blogjava.net/Unmi/archive/2007/06/07/124179.html)时看过 Jakarta-commons-email(如 SimpleEmail 类) 包和 Spring 的 MailSender(如JavaMailSenderImple) 能够自动帮你完成验证过程。

关于 Jakarta-commons-email 自动完成验证发送邮件我之前也写过一篇,不过这里就不提了。列出如果要 Spring 自动完成验证时的 bean 配置如下:

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="${mail.host}"/>
<property name="username" value="${mail.username}"/>
<property name="password" value="${mail.password}"/>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.timeout">25000</prop>
</props>
</property>
</bean>

关键是这个 mail.smtp.auth 设置为 true,然后在代码中取到 mailSender 实例就能直接发送邮件,Spring 自动取到配置中的用户名和密码帮你登录验证,它采用的方法是:

Transport transport = mailSession.getTransport();
transport.connect(props.getProperty("mail.smtp.host"), props
.getProperty("mail.smtp.user"), props
.getProperty("mail.smtp.password"));

不好意思,占用了你太多的页面资源,仅供参考。  回复  更多评论   

# re: Apusic上用JavaMail发邮件 2008-09-04 17:17 朱远翔-Apusic技术顾问

@隔叶黄莺
非常感谢你的回复,我又去深入了解了一下JavaMail的实现,分析认为只要是通过J2EE标准的应用服务器(Websphere、Weblogic、Apusic)应该都按照上文或你写的《三种方式》来操作才能正确发送需要SMTP认证的邮件,而上文的目的就是想说清楚如何发送的机制。
但是,如果在代码中涉及太多与业务无关的对应用系统设计和开发都不够友好,因此,如果使用Spring可以利用Spring+JNDI来操作,如果不用Spring那可以使用Commons-email+JNDI来简化Email操作过程。
最后,非常感谢你对本文的关注,你的探讨也让我又有新的收获,希望有机会多多交流。

另:三种方式中第二种我没有验证成功,我反编译过源代码,也看过JavaMail提供的规范说明,个人觉得是不行的。如果你能够成功运行,请告诉我,谢谢。
第一种方法重载了Authenticator()类,Transport调用connect()函数时,会取得需要的用户名/密码。
第三种方法直接将用户名/密码传给Transport,而且传送使用的是sendMessage()函数,因此也能够成功。  回复  更多评论   

# re: Apusic上用JavaMail发邮件 2008-09-08 11:05 隔叶黄莺

谢谢你的提醒,原文的第二种方法确实有些问题,现已改正了,其实就是 URLName 要的参数形式是:smpt://user@mail.smtp.host

具体代码还是详见那篇日志。

的确,像 J2EE 标准应用服务器中配置了 MailSession,资源都是需要自己手工进行验证,而只有 Spring 简化的 Email 操作,不知为何。所以我基本不用应用服务器的 MailSession 资源。

其实我觉得应用服务器可以做得灵活一些,例如通过设置不同的属性来决定验证方式,因为有些转发协议是 smtps,验证方面我认为是与具体业务无关的,应该可由应用服务器来承担。  回复  更多评论   

# re: Apusic上用JavaMail发邮件 2008-09-16 10:11 朱远翔-Apusic技术顾问

@隔叶黄莺
从功能上说,由应用服务器完成验证更加合理,但是这样的操作会带来移植的问题,因为如果开发期采用了应用服务器提供的模式,那么移植到其他应用服务器时就会需要个性代码。
其实,这块的功能直接用Jakarta-commons-email就可以了,或者像Spring、Struts等框架自行提供,因为携带这些框架就不会给应用移植带来麻烦了。  回复  更多评论   

# re: Apusic上用JavaMail发邮件 2008-09-16 10:46 朱远翔-Apusic技术顾问

2008-09-16
我对文章进行了修改,受到“隔叶黄莺”文章的启发,现在已经将Session配置参数进行认证的方式加入其中。
另:Apusic Studio工程没有更新,如果自己修改代码测试Session配置参数进行认证失败,可以在此继续交流。
对“隔叶黄莺”提供的无私帮助在此表示感谢。  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航: