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}