001    /****************************************************************
002     * Licensed to the Apache Software Foundation (ASF) under one   *
003     * or more contributor license agreements.  See the NOTICE file *
004     * distributed with this work for additional information        *
005     * regarding copyright ownership.  The ASF licenses this file   *
006     * to you under the Apache License, Version 2.0 (the            *
007     * "License"); you may not use this file except in compliance   *
008     * with the License.  You may obtain a copy of the License at   *
009     *                                                              *
010     *   http://www.apache.org/licenses/LICENSE-2.0                 *
011     *                                                              *
012     * Unless required by applicable law or agreed to in writing,   *
013     * software distributed under the License is distributed on an  *
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015     * KIND, either express or implied.  See the License for the    *
016     * specific language governing permissions and limitations      *
017     * under the License.                                           *
018     ****************************************************************/
019    
020    package org.apache.james.jspf.executor;
021    
022    import java.util.ArrayList;
023    import java.util.Iterator;
024    import java.util.List;
025    
026    import org.apache.james.jspf.core.Logger;
027    import org.apache.james.jspf.core.SPFSession;
028    
029    
030    /**
031     * A Blocking version of SPFResult which block until the SPFResult is fully set
032     *
033     */
034    public class FutureSPFResult extends SPFResult {
035        
036        private boolean isReady;
037        private List<IFutureSPFResultListener> listeners;
038        private int waiters;
039        private final Logger log;
040        
041        public FutureSPFResult() {
042            this.log = null;
043            isReady = false;
044        }
045        
046        public FutureSPFResult(Logger log) {
047            this.log = log;         
048            this.isReady = false;
049        }
050    
051            /**
052         * Set SPFResult using the given SPFsession
053         * 
054         * @param session 
055         * 
056         */
057        public void setSPFResult(SPFSession session) {
058            Iterator<IFutureSPFResultListener> listenerIt = null;
059            synchronized (this) {
060                if (!isReady) {
061                    setSPFSession(session);
062                    isReady = true;
063                    if (waiters > 0) {
064                        notifyAll();
065                    }
066                    if (listeners != null) {
067                        listenerIt = listeners.iterator();
068                        listeners = null;
069                    }
070                }
071            }
072            if (listenerIt != null) {
073                while (listenerIt.hasNext()) {
074                    IFutureSPFResultListener listener = listenerIt.next();
075                    try {
076                        listener.onSPFResult(this);
077                    } catch (Throwable e) {
078                        // catch exception. See JSPF-95
079                        if (log != null) {
080                            log.warn("An exception was thrown by the listener " + listener, e);
081                        }
082                    }
083                }
084                listenerIt = null;
085            }
086        }
087    
088        /**
089         * Waits until the SPFResult is set 
090         *
091         */
092        private synchronized void checkReady() {
093            while (!isReady) {
094                try {
095                    waiters++;
096                    wait();
097                } catch (InterruptedException e) {
098                    Thread.currentThread().interrupt();
099                } finally {
100                    waiters--;
101                }
102            }
103        }
104    
105        /**
106         * @see org.apache.james.jspf.executor.SPFResult#getExplanation()
107         */
108        public String getExplanation() {
109            checkReady();
110            return super.getExplanation();
111        }
112    
113        /**
114         * @see org.apache.james.jspf.executor.SPFResult#getHeader()
115         */
116        public String getHeader() {
117            checkReady();
118            return super.getHeader();
119        }
120    
121        /**
122         * @see org.apache.james.jspf.executor.SPFResult#getHeaderName()
123         */
124        public String getHeaderName() {
125            checkReady();
126            return super.getHeaderName();
127        }
128    
129        /**
130         * @see org.apache.james.jspf.executor.SPFResult#getHeaderText()
131         */
132        public String getHeaderText() {
133            checkReady();
134            return super.getHeaderText();
135        }
136    
137        /**
138         * @see org.apache.james.jspf.executor.SPFResult#getResult()
139         */
140        public String getResult() {
141            checkReady();
142            return super.getResult();
143        }
144    
145        /**
146         * Return true if the result was fully builded 
147         * 
148         * @return true or false
149         */
150        public synchronized boolean isReady() {
151            return isReady;
152        }
153    
154        /**
155         * Add a {@link IFutureSPFResultListener} which will get notified once {@link #isReady()} returns <code>true</code>
156         * 
157         * @param listener
158         */
159        public synchronized void addListener(IFutureSPFResultListener listener) {
160            if (!isReady) {
161                if (listeners == null) {
162                    listeners = new ArrayList<IFutureSPFResultListener>();
163                }
164                listeners.add(listener);
165            } else {
166                listener.onSPFResult(this);
167            }
168        }
169       
170        /**
171         * Remove a {@link IFutureSPFResultListener}
172         * 
173         * @param listener
174         */
175        public synchronized void removeListener(IFutureSPFResultListener listener) {
176            if (!isReady && listeners != null) {
177                listeners.remove(listener);
178            }
179        }
180        
181        
182        /**
183         * Listener which will get notified once a {@link FutureSPFResult#isReady()} returns <code>true</code>. So it will not block anymore
184         * 
185         *
186         */
187        public interface IFutureSPFResultListener {
188            
189            /**
190             * Get called once a {@link FutureSPFResult} is ready
191             * 
192             * @param result
193             */
194            void onSPFResult(FutureSPFResult result);
195        }
196    }