View Javadoc

1   //$Id: ClientPool.java,v 1.6 2007/05/07 18:26:55 sjardine Exp $
2   //
3   //Copyright 2006 Steven Jardine <steve@mjnservices.com>
4   //Copyright 2006 MJN Services, Inc - http://www.mjnservices.com
5   //
6   //for information on the HylaFAX FAX server see
7   //http://www.hylafax.org/
8   //
9   //This library is free software; you can redistribute it and/or
10  //modify it under the terms of the GNU Library General Public
11  //License as published by the Free Software Foundation; either
12  //version 2 of the License, or (at your option) any later version.
13  //
14  //This library is distributed in the hope that it will be useful,
15  //but WITHOUT ANY WARRANTY; without even the implied warranty of
16  //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  //Library General Public License for more details.
18  //
19  //You should have received a copy of the GNU Library General Public
20  //License along with this library; if not, write to the Free
21  //Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  //
23  package gnu.hylafax.pool;
24  
25  import gnu.hylafax.Client;
26  
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.Iterator;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  
35  import EDU.oswego.cs.dl.util.concurrent.LinkedQueue;
36  
37  public class ClientPool implements gnu.hylafax.ClientPool {
38  
39      private final static Log log = LogFactory.getLog(ClientPool.class);
40  
41      private boolean blocked = false;
42  
43      private HashMap clientMap;
44  
45      private LinkedQueue clients;
46  
47      private ClientPoolConfiguration configuration;
48  
49      private ArrayList creationTimes;
50  
51      private boolean logClientCreationTimes = true;
52  
53      private Object mutex = new Object();
54  
55      private int size = 0;
56  
57      private boolean stopped = false;
58  
59      private int totalSize = 0;
60  
61      private HashSet workingClients;
62  
63      private HashSet workingClientsToClose;
64  
65      private int workingSize = 0;
66  
67      public ClientPool(ClientPoolConfiguration configuration) {
68  	this.configuration = configuration;
69  	clients = new LinkedQueue();
70  	clientMap = new HashMap();
71  	workingClients = new HashSet();
72  	workingClientsToClose = new HashSet();
73  	creationTimes = new ArrayList();
74      }
75  
76      private synchronized boolean addClient() throws ClientPoolException {
77  	log.debug("Trying To Create Client, Total Connections: " + totalSize
78  		+ ", Max Allowed: " + getConfiguration().getMaxPoolSize());
79  	boolean maximumCapacityReached = getConfiguration().getMaxPoolSize() <= totalSize
80  		&& getConfiguration().getMaxPoolSize() != 0
81  		&& getConfiguration().isPooling();
82  	if (maximumCapacityReached) {
83  	    log.debug("Maximum Clients Reached.");
84  	    return false;
85  	}
86  
87  	PooledClient client = createClient();
88  	log.debug("Client Created.");
89  
90  	put(client);
91  	clientMap.put(client, client);
92  	totalSize++;
93  
94  	return true;
95      }
96  
97      private PooledClient createClient() throws ClientPoolException {
98  	return openClient(new HylaFAXPooledClient(this));
99      }
100 
101     private void destroyClient(PooledClient client) throws ClientPoolException {
102 	try {
103 	    client.destroy();
104 	} catch (Exception e) {
105 	    throw new ClientPoolException("Could Not Destroy Client: "
106 		    + e.getMessage());
107 	} finally {
108 	    // need to do this even if destroying the client raises an exception
109 	    // (Thomas)
110 	    totalSize--;
111 	    workingClients.remove(client);
112 	    clientMap.remove(client);
113 	}
114     }
115 
116     public long getAverageClientCreationTime() {
117 	if (creationTimes.size() > 0) {
118 	    long average = 0;
119 	    for (int count = 0; count < creationTimes.size(); count++) {
120 		average += ((Long) creationTimes.get(count)).longValue();
121 	    }
122 	    return average / creationTimes.size();
123 	}
124 	return -1;
125     }
126 
127     public long getBlockingTimeout() {
128 	return getConfiguration().getBlockingTimeout();
129     }
130 
131     public Client getClient() throws ClientPoolException {
132 
133 	long startTime = System.currentTimeMillis();
134 
135 	log.debug("Wants A Client.");
136 
137 	PooledClient client = null;
138 
139 	try {
140 
141 	    synchronized (mutex) {
142 		if (clients.isEmpty()) {
143 		    while (client == null) {
144 			if (!keepBlocking(startTime)) {
145 			    ClientPoolException e = new ClientPoolException(
146 				    "Could Not Obtain Client During Blocking Timeout ("
147 					    + getConfiguration()
148 						    .getBlockingTimeout()
149 					    + " ms)");
150 			    throw e;
151 			}
152 
153 			boolean clientAdded = false;
154 			try {
155 			    clientAdded = addClient();
156 			} catch (ClientPoolException e) {
157 			    log.warn("Could Not Create Connection: "
158 				    + e.getMessage());
159 			}
160 
161 			if (!clientAdded) {
162 			    log.warn("Pool Is Empty And Will Block Here.");
163 			    blocked = true;
164 			}
165 
166 			client = (PooledClient) clients.poll(getConfiguration()
167 				.getRetryInterval());
168 			if (client == null)
169 			    log.warn("No Clients Available.");
170 			else if (!clientAdded)
171 			    log.info("Obtained Connection.");
172 		    }
173 
174 		} else {
175 		    client = (PooledClient) clients.take();
176 		}
177 	    }
178 	} catch (InterruptedException e) {
179 	    throw new ClientPoolException(
180 		    "Interrupted Thread and No Free Connection Available.");
181 	}
182 
183 	size--;
184 
185 	((HylaFAXPooledClient) client).setWorking(true);
186 	workingClients.add(client);
187 
188 	log.debug("Got Client.");
189 
190 	return client;
191     }
192 
193     public HashMap getClientMap() {
194 	return clientMap;
195     }
196 
197     public ClientPoolConfiguration getConfiguration() {
198 	return configuration;
199     }
200 
201     public int getMaxPoolSize() {
202 	return getConfiguration().getMaxPoolSize();
203     }
204 
205     public int getMinPoolSize() {
206 	return getConfiguration().getMinPoolSize();
207     }
208 
209     public long getNoopInterval() {
210 	return getConfiguration().getMaxNoopTime();
211     }
212 
213     public int getSize() {
214 	return size;
215     }
216 
217     public int getTotalSize() {
218 	return totalSize;
219     }
220 
221     public String getUserName() {
222 	return getConfiguration().getUserName();
223     }
224 
225     public int getWorkingSize() {
226 	return workingSize;
227     }
228 
229     public boolean isLogClientCreationTimes() {
230 	return logClientCreationTimes;
231     }
232 
233     public boolean isStopped() {
234 	return stopped;
235     }
236 
237     public boolean keepBlocking(long startTime) {
238 	return System.currentTimeMillis() - startTime < getConfiguration()
239 		.getBlockingTimeout();
240     }
241 
242     PooledClient openClient(HylaFAXPooledClient client)
243 	    throws ClientPoolException {
244 	try {
245 	    long startTime = System.currentTimeMillis();
246 
247 	    ClientPoolConfiguration config = getConfiguration();
248 
249 	    if (config.getHost() != null && config.getPort() != -1)
250 		client.poolOpen(config.getHost(), config.getPort());
251 	    else if (config.getHost() != null)
252 		client.poolOpen(config.getHost());
253 	    else
254 		client.poolOpen();
255 
256 	    if (config.getUserName() != null)
257 		client.poolUser(config.getUserName());
258 
259 	    if (config.getPassword() != null)
260 		client.poolPass(config.getPassword());
261 
262 	    if (config.getAdminPassword() != null)
263 		client.poolAdmin(config.getAdminPassword());
264 
265 	    if (config.getTimeZone() != null)
266 		client.poolTzone(config.getTimeZone());
267 
268 	    if (isLogClientCreationTimes())
269 		creationTimes.add(new Long(System.currentTimeMillis()
270 			- startTime));
271 
272 	    client.setPassive(true);
273 	    client.start();
274 	    return client;
275 	} catch (Exception e) {
276 	    throw new ClientPoolException(e.getMessage());
277 	}
278     }
279 
280     public void put(PooledClient client) throws ClientPoolException {
281 	try {
282 	    if (!client.isValid())
283 		workingClientsToClose.add(client);
284 	    ((HylaFAXPooledClient) client).setWorking(false);
285 
286 	    /* stop the client so that GC can destroy the object. (Thomas) */
287 	    //((HylaFAXPooledClient) client).stop();
288 
289 	    if (blocked)
290 		log.warn("Will Be Unblocked");
291 
292 	    if (getConfiguration().isPooling()) {
293 		if (workingClientsToClose.remove(client)) {
294 		    destroyClient(client);
295 		    addClient();
296 		} else {
297 		    clients.put(client);
298 		    size++;
299 		}
300 	    }
301 	    blocked = false;
302 
303 	    // Desctoy client if pooling is not enabled.
304 	    if (!getConfiguration().isPooling())
305 		destroyClient(client);
306 
307 	    log.debug("Released Client.");
308 
309 	} catch (InterruptedException e) {
310 	    log.warn("Was Interrupted.", e);
311 	    destroyClient(client);
312 	} finally {
313 	    // need to remove the client from this list even if an error
314 	    // occured. (Thomas)
315 	    workingClients.remove(client);
316 	}
317     }
318 
319     public void restart() {
320 	// Flag working clients for destruction when returned to the stack.
321 	workingClientsToClose.addAll(workingClients);
322 
323 	// Close all free clients
324 	while (getSize() > 0)
325 	    try {
326 		PooledClient client = (PooledClient) getClient();
327 		destroyClient(client);
328 	    } catch (ClientPoolException e) {
329 		log.warn("Could Not Close Connection.", e);
330 	    }
331 	// Create enough clients to restore the stack to minPoolSize.
332 	while (getTotalSize() < getConfiguration().getMinPoolSize())
333 	    try {
334 		addClient();
335 	    } catch (Exception e) {
336 		log.warn("Could Not Add Connection.", e);
337 	    }
338     }
339 
340     public void setBlockingTimeout(long blockingTimeout) {
341 	getConfiguration().setBlockingTimeout(blockingTimeout);
342     }
343 
344     public void setClientMap(HashMap clientMap) {
345 	this.clientMap = clientMap;
346     }
347 
348     public void setConfiguration(ClientPoolConfiguration configuration) {
349 	this.configuration = configuration;
350     }
351 
352     public void setLogClientCreationTimes(boolean logClientCreationTimes) {
353 	this.logClientCreationTimes = logClientCreationTimes;
354     }
355 
356     public void setMaxPoolSize(int maxPoolSize) {
357 	getConfiguration().setMaxPoolSize(maxPoolSize);
358     }
359 
360     public void setMinPoolSize(int minPoolSize) {
361 	getConfiguration().setMinPoolSize(minPoolSize);
362     }
363 
364     public void setNoopInterval(long noopInterval) {
365 	getConfiguration().setMaxNoopTime(noopInterval);
366     }
367 
368     public void setPassword(String password) {
369 	getConfiguration().setPassword(password);
370     }
371 
372     public void setSize(int size) {
373 	this.size = size;
374     }
375 
376     public void setTotalSize(int totalSize) {
377 	this.totalSize = totalSize;
378     }
379 
380     public void setUserName(String userName) {
381 	getConfiguration().setUserName(userName);
382     }
383 
384     public void setWorkingSize(int workingSize) {
385 	this.workingSize = workingSize;
386     }
387 
388     public void start() throws ClientPoolException {
389 	stopped = false;
390 	for (int i = 0; i < getConfiguration().getMinPoolSize(); i++) {
391 	    addClient();
392 	}
393     }
394 
395     public void stop() {
396 	stopped = true;
397 	// Close all working connections.
398 	Iterator iter = workingClients.iterator();
399 	while (iter.hasNext()) {
400 	    try {
401 		PooledClient item = (PooledClient) iter.next();
402 		destroyClient(item);
403 	    } catch (ClientPoolException e) {
404 		log.warn("Could Not Close Connection.", e);
405 	    }
406 	}
407 
408 	// Close all free connections
409 	while (getSize() > 0)
410 	    try {
411 		PooledClient item = (PooledClient) getClient();
412 		destroyClient(item);
413 	    } catch (ClientPoolException e) {
414 		log.warn("Could Not Close Connection.", e);
415 	    }
416 	totalSize = 0;
417     }
418 
419 }