Project Report: fawkez

Packagesummary org.jcoderz.phoenix.servlet

org.jcoderz.phoenix.servlet.PingServlet

LineHitsNoteSource
1  /*
2   * $Id: PingServlet.java 1011 2008-06-16 17:57:36Z amandel $
3   *
4   * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5   *
6   * Redistribution and use in source and binary forms, with or without
7   * modification, are permitted provided that the following conditions are
8   * met:
9   *
10   *    * Redistributions of source code must retain the above copyright
11   *      notice, this list of conditions and the following disclaimer.
12   *    * Redistributions in binary form must reproduce the above
13   *      copyright notice, this list of conditions and the following
14   *      disclaimer in the documentation and/or other materials
15   *      provided with the distribution.
16   *    * Neither the name of the jCoderZ.org Project nor the names of
17   *      its contributors may be used to endorse or promote products
18   *      derived from this software without specific prior written
19   *      permission.
20   *
21   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32   */
33  package org.jcoderz.phoenix.servlet;
34  
35  import java.awt.Color;
36  import java.io.DataInputStream;
37  import java.io.IOException;
38  import java.io.PrintWriter;
39  import java.io.StringWriter;
40  import java.net.InetAddress;
41  import java.net.Socket;
42  import java.net.UnknownHostException;
43  import java.text.DateFormat;
44  import java.text.SimpleDateFormat;
45  import java.util.Collections;
46  import java.util.ConcurrentModificationException;
47  import java.util.Date;
48  import java.util.HashMap;
49  import java.util.Iterator;
50  import java.util.Locale;
51  import java.util.Map;
52  import java.util.TimeZone;
53  
54  import javax.servlet.http.HttpServlet;
55  import javax.servlet.http.HttpServletRequest;
56  import javax.servlet.http.HttpServletResponse;
57  
58  import org.jcoderz.commons.util.IoUtil;
59  
60  /**
61   * This servlet sends a simple ping to the given host and returns a
62   * 1 pixel image with a color representing the host status.
63   *
64   * @web.servlet name="ping"
65   * @web.servlet-mapping url-pattern="/ping/*"
66   *
67   * @author Andreas Mandel
68   */
690 public final class PingServlet
70        extends HttpServlet
71  {
72     /**
73      * Timezone used by the protocol.
74      */
750    public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("UTC");
76  
77     /** The format used to write date values. */
78     public static final String DATE_TIME_FORMAT
79           = "yyyy-MM-dd HH:mm:ss.SSS";
80  
81     private static final int HOST_NOT_REACHED = -1;
82     private static final int UNKNOWN_HOST = -2;
83  
84     private static final int ILLEGAL_ARGUMENT = -3;
85     private static final int HOST_REFUSED = -4;
86  
87     private static final long serialVersionUID = 1L;
88  
89     private static final int MAX_PING_CACHE_SIZE = 1000;
900    private static final Map PING_CACHE
91           = Collections.synchronizedMap(new HashMap());
920    private static long sLastCacheUpdate = 0;
930    private static boolean sUpdateInProgreess = false;
94  
95     private static final long VALIDITY_TIME = 10 * 60 * 1000;
96  
97     /**
98      * If a value in the cache in not used for this period of time it is
99      * removed from the cache.
100      */
101     private static final long MAX_IDLE_TIME = 7 * 24 * 60 * 60 * 1000;
102  
103     private static final int SOCKET_TIMEOUT = 1000;
104  
105     private static final long FAST_RESPONSE = 5;
106  
107     private static final int COLOR_OFFSET_RED = 13;
108     private static final int COLOR_OFFSET_GREEN = COLOR_OFFSET_RED + 1;
109     private static final int COLOR_OFFSET_BLUE = COLOR_OFFSET_GREEN + 1;
110     private static final int BG_OFFSET_RED = COLOR_OFFSET_BLUE + 1;
111     private static final int BG_OFFSET_GREEN = BG_OFFSET_RED + 1;
112     private static final int BG_OFFSET_BLUE = BG_OFFSET_GREEN + 1;
113  
1140    private static final byte [] IMAGE_DATA
115           = {
116              'G', 'I', 'F', '8', '7', 'a', //
117              1, 0, // logical width
118              1, 0, // logical height
119              (byte) 0x80, //
120              2, // BG color index
121              0, //
122              0, 0, 0, // COLOR 1 (used)
123              0, 0, 0, // COLOR 2 (unused)
124              44, // IMAGE
125              0, 0, // Left POS
126              0, 0, // Right POS
127              1, 0, // WIDTH
128              1, 0, // HEIGHT
129              0,
130              2, 2, 68, 01, 0, 59
131        };
132  
133     private static final int ECHO_PORT = 22;
134  
135     protected void doPost (HttpServletRequest req, HttpServletResponse rsp)
136           throws IOException
137     {
1380       doGet(req, rsp);
1390    }
140  
141     protected void doGet (HttpServletRequest req, HttpServletResponse rsp)
142           throws IOException
143     {
1440       String hostname = req.getParameter("host");
145  
1460       final String clear = req.getParameter("clear");
1470       if (clear != null && clear.length() > 0)
148        {
1490          PING_CACHE.clear();
150        }
151  
1520       if (hostname == null || hostname.length() == 0)
153        {
1540          hostname = req.getPathInfo();
1550          if (hostname != null && hostname.charAt(0) == '/')
156           {
1570             hostname = hostname.substring(1);
158           }
159        }
160  
1610       if (hostname == null || hostname.length() == 0)
162        {
1630          dumpCacheInfo(rsp);
164        }
165        else
166        {
1670          sendAnswer(check(hostname), rsp);
168        }
169  
1700       updateCache();
1710    }
172  
173     private void dumpCacheInfo (HttpServletResponse rsp)
174           throws IOException
175     {
1760       rsp.setContentType("text/plain");
177  
1780       final StringWriter sw = new StringWriter();
1790       final PrintWriter data = new PrintWriter(sw);
1800       synchronized (PING_CACHE)
181        {
1820          data.println("Last Update: " + dateToString(sLastCacheUpdate));
1830          data.println("Update in progress: " + sUpdateInProgreess);
1840          data.println("Number of stored results: " + PING_CACHE.size());
1850          data.println("Cached Data:");
1860          data.println("---");
1870       }
1880       final PrintWriter out = rsp.getWriter();
1890       out.print(sw.getBuffer().toString());
190  
191        try
192        {
1930          final Iterator i = PING_CACHE.values().iterator();
194  
1950          while (i.hasNext())
196           {
1970             final PingResult result = (PingResult) i.next();
1980             out.println(result);
1990          }
2000          out.println("---");
201        }
2020       catch (ConcurrentModificationException ex)
203        {
2040          out.println("Uups... somebody updated the cache... try again!");
2050       }
2060    }
207  
208     private void updateCache ()
209     {
210        try
211        {
2120          final long now = System.currentTimeMillis();
213           final long lastUpdate;
214           final boolean alreadyUpdating;
2150          synchronized (PING_CACHE)
216           {
2170             lastUpdate = sLastCacheUpdate;
2180             alreadyUpdating = sUpdateInProgreess;
2190             sUpdateInProgreess = true;
2200          }
221  
2220          if (lastUpdate + VALIDITY_TIME < now && !alreadyUpdating)
223           {
2240             final Iterator i = PING_CACHE.values().iterator();
225  
2260             while (i.hasNext())
227              {
2280                final PingResult result = (PingResult) i.next();
2290                if (result.getLastUsed() < (now - MAX_IDLE_TIME))
230                 {
2310                   i.remove();
232                 }
2330                else if (result.getExpires() < now)
234                 {
2350                   new Thread()
2360                   {
237                       public void run ()
238                       {
2390                         check(result.getHostname()); // update entry not list?
2400                      };
241                    } .start();
242                 }
2430             }
244  
2450             synchronized (PING_CACHE)
246              {
2470                sLastCacheUpdate = now;
2480             }
249           }
250        }
2510       catch (ConcurrentModificationException ex)
252        {
253           // we will catch up next time
254        }
255        finally
256        {
2570          synchronized (PING_CACHE)
258           {
2590             sUpdateInProgreess = false;
2600          }
2610       }
2620    }
263  
264  
265     private void sendAnswer (PingResult result, HttpServletResponse rsp)
266           throws IOException
267     {
2680       rsp.setContentType("image/gif");
2690       rsp.setContentLength(IMAGE_DATA.length);
2700       rsp.setHeader("Cache-Control", "public");
271  
2720       final byte[] responseData = new byte[IMAGE_DATA.length];
2730       System.arraycopy(IMAGE_DATA, 0, responseData, 0, IMAGE_DATA.length);
274  
2750       if (result != null)
276        {
2770          rsp.setDateHeader("Last-Modified", result.getLastModified());
2780          rsp.setDateHeader("Expires", result.getExpires());
2790          final Color color = resultToColor(result.getResult());
280  
2810          responseData[COLOR_OFFSET_RED] = (byte) color.getRed();
2820          responseData[COLOR_OFFSET_GREEN] = (byte) color.getGreen();
2830          responseData[COLOR_OFFSET_BLUE] = (byte) color.getBlue();
2840          responseData[BG_OFFSET_RED] = (byte) color.getRed();
2850          responseData[BG_OFFSET_GREEN] = (byte) color.getGreen();
2860          responseData[BG_OFFSET_BLUE] = (byte) color.getBlue();
2870          rsp.setHeader("response-value", String.valueOf(result.getResult()));
2880          rsp.setHeader("response-host", result.getHostname());
289        }
290  
2910       rsp.getOutputStream().write(responseData);
2920    }
293  
294  
295     private Color resultToColor (long result)
296     {
297        final Color responseColor;
2980       if (result == UNKNOWN_HOST)
299        {
3000          responseColor = Color.RED;
301        }
3020       else if (result == ILLEGAL_ARGUMENT)
303        {
3040          responseColor = Color.ORANGE;
305        }
3060       else if (result == HOST_REFUSED)
307        {
3080          responseColor = Color.YELLOW;
309        }
3100       else if (result < 0)
311        {
3120          responseColor = Color.BLACK;
313        }
3140       else if (result <= FAST_RESPONSE)
315        {
3160          responseColor = Color.GREEN.brighter();
317        }
3180       else if (result <= (FAST_RESPONSE + FAST_RESPONSE))
319        {
3200          responseColor = Color.GREEN;
321        }
3220       else if (result <= (FAST_RESPONSE + FAST_RESPONSE + FAST_RESPONSE))
323        {
3240          responseColor = Color.GREEN.darker();
325        }
326        else
327        {
3280          responseColor = Color.GREEN.darker().darker();
329        }
3300       return responseColor;
331     }
332  
333     protected PingResult check (String host)
334     {
3350       final long now = System.currentTimeMillis();
336  
3370       PingResult result = (PingResult) PING_CACHE.get(host);
3380       if (result == null)
339        {
3400           result = new PingResult(host, ping(host));
3410           PING_CACHE.put(host, result);
342        }
343        else
344        {
345              // only one update per host
3460             synchronized (result)
347              {
3480                if (result.getExpires() < now)
349                 { // this might take some time!
3500                   result.setResult(ping(host));
351                 }
3520             }
353        }
354  
355        // just to be save against memory attacks
3560       if (PING_CACHE.size() > MAX_PING_CACHE_SIZE)
357        {
3580          PING_CACHE.clear();
359        }
360  
3610       return result;
362     }
363  
364  
365     protected static long ping (String hostname)
366     {
367        final long result;
3680       if (hostname == null || hostname.length() == 0)
369        {
3700          result = ILLEGAL_ARGUMENT;
371        }
372        else
373        {
3740          InetAddress host = null;
375           try
376           {
3770             host = InetAddress.getByName(hostname);
378           }
3790          catch (UnknownHostException ex)
380           {
381              // hmmm
3820          }
383  
3840          if (host == null)
385           {
3860             result = UNKNOWN_HOST;
387           }
388           else
389           {
3900             result = ping(host);
391           }
392        }
3930       return result;
394     }
395  
396  
397     protected static long ping (InetAddress host)
398     {
3990       final long timer = System.currentTimeMillis();
4000       DataInputStream dis = null;
4010       Socket t = null;
4020       long result = -1;
403        try
404        {
4050          t = new Socket(host, ECHO_PORT);
4060          t.setTcpNoDelay(true);
4070          t.setSoTimeout(SOCKET_TIMEOUT);
4080          dis = new DataInputStream(t.getInputStream());
4090          dis.readByte();
4100          result = System.currentTimeMillis() - timer;
411        }
4120       catch (IOException e)
413        {
4140          final String message = e.getMessage();
4150          if (message != null && message.indexOf("refused") != -1)
416           {
4170             result = HOST_REFUSED;
418           }
419           else
420           {
4210             result = HOST_NOT_REACHED;
422           }
423        }
424        finally
425        {
4260          IoUtil.close(dis);
4270          IoUtil.close(t);
4280       }
4290       return result;
430     }
431  
432     static String dateToString (long time)
433     {
4340       final DateFormat formater
435              = new SimpleDateFormat(DATE_TIME_FORMAT, Locale.US);
4360       formater.setTimeZone(TIME_ZONE);
4370       return formater.format(new Date(time));
438     }
439  
4400    private static class PingResult
441     {
442        private final String mHostname;
443        private long mResult;
444        private long mExpires;
445        private long mLastModified;
446        private long mLastUsed;
447  
448        PingResult (String hostname, long result)
4490       {
4500          final long now = System.currentTimeMillis();
4510          mExpires = now + VALIDITY_TIME;
4520          mLastModified = now;
4530          mHostname = hostname;
4540          mResult = result;
4550          mLastUsed = now;
4560       }
457  
458        /** {@inheritDoc} */
459        public synchronized String toString ()
460        {
4610(1)         final StringBuffer buffer = new StringBuffer();
4620          buffer.append("[PingResult: ");
4630          buffer.append(mHostname);
4640          buffer.append(" result: ");
4650          buffer.append(mResult);
4660          buffer.append(" expires: ");
4670          buffer.append(dateToString(mExpires));
4680          buffer.append(" lastModified: ");
4690          buffer.append(dateToString(mLastModified));
4700          buffer.append(" lastUsed: ");
4710          buffer.append(dateToString(mLastUsed));
4720          buffer.append(']');
4730          return buffer.toString();
474        }
475  
476        String getHostname ()
477        {
4780          return mHostname;
479        }
480  
481        synchronized long getExpires ()
482        {
4830          return mExpires;
484        }
485  
486        synchronized long getLastModified ()
487        {
4880          return mLastModified;
489        }
490  
491        synchronized void setResult (long result)
492        {
4930          final long now = System.currentTimeMillis();
4940          mExpires = now + VALIDITY_TIME;
4950          if (result != mResult)
496           {
4970             mLastModified = now;
4980             mResult = result;
499           }
5000       }
501  
502        synchronized long getResult ()
503        {
5040          mLastUsed = System.currentTimeMillis();
5050          return mResult;
506        }
507  
508        synchronized long getLastUsed ()
509        {
5100          return mLastUsed;
511        }
512     }
513  }

Findings in this File

i (1) 461 : 29 StringBuffer constructor is initialized with size 16, but has at least 59 characters appended.