001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.net.smtp; 020 021import java.util.Date; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.Map; 025import java.util.Map.Entry; 026import java.util.Properties; 027import java.util.Stack; 028 029import javax.mail.Authenticator; 030import javax.mail.MessagingException; 031import javax.mail.NoSuchProviderException; 032import javax.mail.Session; 033import javax.mail.Transport; 034 035import lucee.commons.lang.ExceptionUtil; 036 037public class SMTPConnectionPool { 038 039 //private static final long MAX_AGE = 5*60*1000; 040 private static Map<String,Stack<SessionAndTransport>> sessions=new HashMap<String, Stack<SessionAndTransport>>(); 041 042 043 public static SessionAndTransport getSessionAndTransport(Properties props, String key, Authenticator auth,long lifeTimespan, long idleTimespan) throws MessagingException{ 044 045 // Session 046 SessionAndTransport sat=null; 047 Stack<SessionAndTransport> satStack = getSATStack(key); 048 sat=pop(satStack); 049 050 // when sat still valid return it 051 if(sat!=null) { 052 if(isValid(sat,lifeTimespan,idleTimespan) 053 ) { 054 return sat.touch(); 055 } 056 disconnect(sat.transport); 057 } 058 059 return new SessionAndTransport(key, props, auth, lifeTimespan,idleTimespan); 060 } 061 062 063 private static boolean isValid(SessionAndTransport sat, long lifeTimespan, long idleTimespan) { 064 065 return (idleTimespan<=0 || sat.lastAccess+idleTimespan>System.currentTimeMillis()) 066 && 067 (lifeTimespan<=0 || sat.created+lifeTimespan>System.currentTimeMillis()); 068 } 069 070 071 public static void releaseSessionAndTransport(SessionAndTransport sat) { 072 073 getSATStack(sat.key).add(sat.touch()); 074 } 075 076 public static String listSessions() { 077 Iterator<Entry<String, Stack<SessionAndTransport>>> it = sessions.entrySet().iterator(); 078 Entry<String, Stack<SessionAndTransport>> entry; 079 Stack<SessionAndTransport> stack; 080 StringBuilder sb=new StringBuilder(); 081 while(it.hasNext()){ 082 entry = it.next(); 083 sb.append(entry.getKey()).append('\n'); 084 stack = entry.getValue(); 085 if(stack.isEmpty()) continue; 086 listSessions(sb,stack); 087 } 088 return sb.toString(); 089 } 090 091 private static void listSessions(StringBuilder sb, Stack<SessionAndTransport> stack) { 092 Iterator<SessionAndTransport> it = stack.iterator(); 093 while(it.hasNext()){ 094 SessionAndTransport sat = it.next(); 095 sb.append("- "+sat.key+":"+new Date(sat.lastAccess)).append('\n'); 096 } 097 } 098 099 100 public static void closeSessions() { 101 Iterator<Entry<String, Stack<SessionAndTransport>>> it = sessions.entrySet().iterator(); 102 Entry<String, Stack<SessionAndTransport>> entry; 103 Stack<SessionAndTransport> oldStack; 104 Stack<SessionAndTransport> newStack; 105 while(it.hasNext()){ 106 entry = it.next(); 107 oldStack = entry.getValue(); 108 if(oldStack.isEmpty()) continue; 109 newStack=new Stack<SMTPConnectionPool.SessionAndTransport>(); 110 entry.setValue(newStack); 111 closeSessions(oldStack,newStack); 112 } 113 } 114 115 private static void closeSessions(Stack<SessionAndTransport> oldStack,Stack<SessionAndTransport> newStack) { 116 SessionAndTransport sat; 117 while((sat=pop(oldStack))!=null){ 118 if(!isValid(sat, sat.lifeTimespan, sat.idleTimespan)) { 119 disconnect(sat.transport); 120 } 121 else 122 newStack.add(sat); 123 } 124 } 125 126 static void disconnect(Transport transport) { 127 if(transport!=null && transport.isConnected()) { 128 try { 129 transport.close(); 130 } catch (MessagingException e) {} 131 } 132 } 133 134 135 136 137 138 139 140 141 142 143 144 145 146 private static synchronized Stack<SessionAndTransport> getSATStack(String key) { 147 Stack<SessionAndTransport> stack=sessions.get(key); 148 if(stack==null) { 149 stack=new Stack<SessionAndTransport>(); 150 sessions.put(key, stack); 151 } 152 return stack; 153 } 154 155 private static Session createSession(String key,Properties props, Authenticator auth) { 156 if(auth!=null)return Session.getInstance(props,auth); 157 return Session.getInstance(props); 158 } 159 160 161 private static SessionAndTransport pop(Stack<SessionAndTransport> satStack) { 162 try{ 163 return satStack.pop(); 164 } 165 catch(Throwable t){ 166 ExceptionUtil.rethrowIfNecessary(t); 167 } 168 return null; 169 } 170 171 172 public static class SessionAndTransport { 173 public final Session session; 174 public final Transport transport; 175 public final String key; 176 private long lastAccess; 177 public final long created; 178 public final long lifeTimespan; 179 public final long idleTimespan; 180 181 SessionAndTransport(String key, Properties props,Authenticator auth, long lifeTimespan, long idleTimespan) throws NoSuchProviderException { 182 this.key=key; 183 this.session=createSession(key, props, auth); 184 this.transport=session.getTransport("smtp"); 185 this.created=System.currentTimeMillis(); 186 this.lifeTimespan=lifeTimespan; 187 this.idleTimespan=idleTimespan; 188 touch(); 189 } 190 191 private SessionAndTransport touch() { 192 this.lastAccess=System.currentTimeMillis(); 193 return this; 194 } 195 } 196 197 198 199}