Continually-running Applet has memory leaks - how do I plug them?

Discussion in 'Java' started by Phil Powell, Jan 24, 2004.

  1. Phil Powell

    Phil Powell Guest

    import java.io.*;
    import java.net.*;
    import java.awt.*;
    import java.util.*;
    import java.applet.*;
    import java.awt.event.*;


    /**
    * @version JDK 1.3
    * @author Phil Powell
    */

    /*---------------------------------------------------------------------------------------------
    * ParseNews will receive a Phil-homegrown XML-like script from a PHP
    file, parse through it,
    * and will display each news item within the applet. The "View News"
    button, upon being
    * pressed, will perform an AppletContext showDocument action to go to
    the URL formed by the
    * passed ID per Vector iteration. News items will continually scroll
    from bottom to top and
    * then revert back to the first one
    *
    * Created 2/1/2003
    *--------------------------------------------------------------------------------------------*/


    public class ParseNews extends Applet {

    // INITIALIZE VARS AND VAR SCOPE
    protected Vector parseVector = new Vector();
    protected Thread scrollThread = null;
    protected ActionListener al = null;
    private int xpos = 0, ypos = 0, index = 0;
    private boolean hasStartedScroll = false, hasAddedAction = false;
    private Button newsButton = new Button("View News");
    private Panel scrollPanel, buttonPanel;

    /*----------------------------------------------------------------------------------
    * Vector method parser will return a vector of a 3-item array
    containing the id,
    * shortdescription and ranking of what was passed from the PHP
    script
    *
    *----------------------------------------------------------------------------------*/

    private Vector parser(String stuff) {
    // STUFF
    }

    /*------------------------------------------------------------------------------------------
    * Vector method sortedVector will sort the vector created by parser
    into numerical order
    * according to the ranking value found as the third element of each
    3-item array found in
    * each vector element
    *
    *------------------------------------------------------------------------------------------*/

    private Vector sortedVector(Vector vectorToSort) {
    // STUFF
    }

    // BUBBLE SORT INTEGER ARRAY METHOD - IN LIEU OF M$ NOT ALLOWING FOR
    JAVA.UTIL.ARRAYS - GRRRR
    private int[] mySortedArray(int[] origArray) {
    // STUFF
    }


    /*------------------------------------------------------------------------------------------
    * unescapeHTML String method will convert ASCII characters to their
    "funky" equivalent.
    *
    * originally created by Rene Gagnon at
    http://www.rgagnon.com/javadetails/java-0307.html
    * Modified by Phil Powell on 2/2/2003
    *
    *-----------------------------------------------------------------------------------------*/

    public static String unescapeHTML(String schtuff) {
    String[][] asciiArray =
    {{"&lt;", "<"}, {"&gt;", ">" }, {"&amp;", "&" }, {"&quot;", "\""},
    {"&agrave;", "à"},
    {"&Agrave;", "À"}, {"&acirc;", "â"}, {"&auml;", "ä"}, {"&Auml;",
    "Ä"}, {"&Acirc;", "Â"},
    {"&aring;", "å"}, {"&Aring;", "Å"}, {"&aelig;", "æ"}, {"&AElig;",
    "Æ"}, {"&ccedil;", "ç"},
    {"&Ccedil;", "Ç"}, {"&eacute;", "é"}, {"&Eacute;", "É"},
    {"&egrave;", "è"},
    {"&Egrave;", "È"}, {"&ecirc;", "ê"}, {"&Ecirc;", "Ê"}, {"&euml;",
    "ë"}, {"&Euml;", "Ë"},
    {"&iuml;", "ï"}, {"&Iuml;", "Ï"}, {"&ocirc;", "ô"}, {"&Ocirc;",
    "Ô"}, {"&ouml;", "ö"},
    {"&Ouml;", "Ö"}, {"&oslash;", "ø"}, {"&Oslash;", "Ø"}, {"&szlig;",
    "ß"},
    {"&ugrave;", "ù"}, {"&Ugrave;", "Ù"}, {"&ucirc;", "û"},
    {"&Ucirc;", "Û"},
    {"&uuml;", "ü"}, {"&Uuml;", "Ü"}, {"&nbsp;", " "}, {"&reg;",
    "\u00a9"}, {"&copy;", "\u00ae"},
    {"&euro;", "\u20a0"}
    };
    int flag = -1, ampIndex = -1;
    if (schtuff.indexOf("&") >= 0) {
    if (schtuff.indexOf(";") > schtuff.indexOf("&")) {
    String asciiStuff = schtuff.substring(schtuff.indexOf("&"),
    schtuff.indexOf(";") + 1);
    ampIndex = schtuff.indexOf("&");
    while (asciiStuff.substring(1, asciiStuff.length()).indexOf("&")
    >= 0) {

    ampIndex = schtuff.indexOf("&", ampIndex + 1);
    asciiStuff = asciiStuff.substring(1, asciiStuff.length()).trim();
    }
    for (int j = 0; j < asciiArray.length; j++) {
    if (asciiArray[j][0].equals(asciiStuff)) {
    flag = j;
    break;
    }
    }
    if (flag >= 0) {
    schtuff = schtuff.substring(0, ampIndex) + asciiArray[flag][1] +
    schtuff.substring(schtuff.indexOf(";") + 1,
    schtuff.length());
    return unescapeHTML(schtuff); // RECURSIVE
    }
    }
    return schtuff;
    } else {
    return schtuff.trim();
    }
    }

    // INITIALIZE APPLET, RETRIEVE VALUES FROM PHP SCRIPT AND SORT INTO
    VECTOR

    public void init() {
    String stuff = null;
    try {
    URL url = new URL(getParameter("url"));
    URLConnection conn = url.openConnection();
    conn.setDoInput(true);
    conn.setDoOutput(false);
    conn.setUseCaches(false);
    conn.setDefaultUseCaches(false);
    conn.setRequestProperty("Content-type", "text/html");
    BufferedReader fromURL = new BufferedReader(new
    InputStreamReader(conn.getInputStream()));
    stuff = fromURL.readLine();
    // CLOSE CONNECTION
    fromURL.close();
    fromURL = null;
    conn = null;
    } catch (Exception e) {
    parseVector.addElement(new String[]{"0", "Could not connect to
    retrieve news", "0"});
    }
    if (stuff != null && stuff != "") {
    parseVector = parser(stuff);
    } else if (parseVector.size() == 0) {
    parseVector.addElement(new String[]{"0", "No current news found",
    "0"});
    }

    setBackground(Color.black);
    setForeground(Color.white);
    setFont(new Font("Garamond", Font.BOLD, 10));
    scrollPanel = new Panel();
    scrollPanel.setVisible(false);
    buttonPanel = new Panel();
    buttonPanel.setBackground(Color.black);
    buttonPanel.setForeground(Color.white);
    newsButton.setBackground(Color.red);
    newsButton.setForeground(Color.white);
    // ONLY ADD THE "VIEW NEWS" BUTTON IF THERE IS NEWS TO VIEW!
    if (((String[])parseVector.elementAt(0))[0] != "0") {
    buttonPanel.add(newsButton);
    } else {
    buttonPanel.setVisible(false);
    }
    add(scrollPanel);
    add(buttonPanel);

    scrollThread = new Thread(new ScrollNews());
    scrollThread.start();

    }

    /*------------------------------------------------------------------------------------------
    * Reinitialize value of the ActionListener object for the "View
    News" button, along with
    * display news item, with the "scrolling" effect handled by the
    runnable class ScrollNews
    *-----------------------------------------------------------------------------------------*/

    public void paint(Graphics g) {

    FontMetrics fm = g.getFontMetrics();
    ((String[])parseVector.elementAt(index))[1] =
    unescapeHTML(((String[])parseVector.elementAt(index))[1]);
    xpos = (getSize().width -
    fm.stringWidth(((String[])parseVector.elementAt(index))[1])) / 2;
    if (ypos <= 0) {
    g.drawString("", 0, 0); // CLEAR THE APPLET
    ypos = getSize().height;
    hasAddedAction = false;
    if (((String[])parseVector.elementAt(0))[0] != "0")
    newsButton.removeActionListener(al);
    if (index < parseVector.size() - 1 && hasStartedScroll) {
    index++;
    } else if (index > 0) {
    index = 0;
    hasStartedScroll = false;
    }
    }
    if (((String[])parseVector.elementAt(0))[0] != "0" &&
    !hasAddedAction) {
    hasAddedAction = true;
    al = new ViewNews("http://valsignalandet.com/cgi-bin/cgiwrap/ppowell/news.cgi?id="
    +
    ((String[])parseVector.elementAt(index))[0]);
    newsButton.addActionListener(al);
    }

    g.drawString(((String[])parseVector.elementAt(index))[1], xpos,
    ypos);
    hasStartedScroll = true;

    }



    class ScrollNews implements Runnable {

    public void run() {
    while (true) {
    ypos = ypos - 1;
    ParseNews.this.repaint();
    try {
    Thread.sleep(50);
    } catch (InterruptedException e) {}
    }
    }

    }

    class ViewNews implements ActionListener {

    private String urlString = "";

    public ViewNews(String urlString) {
    this.urlString = urlString;
    }

    public void actionPerformed(ActionEvent e) {
    try {
    ParseNews.this.getAppletContext().showDocument(new
    URL(urlString));
    } catch (Exception oops) {
    System.out.println("could not go to " + urlString);
    oops.printStackTrace();
    }
    }

    }

    }


    The following applet I wrote had run just fine on my website until
    sometime today, when it suddenly started running at an extremely slow
    rate. I tried everything I could think of, including garbage
    collection, to alleviate the problem, to no avail.

    I am suspecting a memory leak somewhere but I cannot discern where.
    Can anyone with more Java knowledge out there discern this for me or
    at least tell me what steps I should be taking in my code to prevent
    memory leaks for a continually-running applet?

    Thanx
    Phil
     
    Phil Powell, Jan 24, 2004
    #1
    1. Advertising

  2. Phil Powell

    tollek Guest

    do you realy think that anyone would like to read and think over so long
    code?

    memory leak? you should check the expression 'garbage collector'

    /tollek
     
    tollek, Jan 25, 2004
    #2
    1. Advertising

  3. "Phil Powell" ...
    | import java.io.*;

    The code you supplied does not compile.
    Besides the lines that wrap, there are
    three methods that are not implemented.

    You should also take efforts to ensure
    that your code is the smallest compilable
    example that demonstrates the problem.

    | * originally created by Rene Gagnon at
    | http://www.rgagnon.com/javadetails/java-0307.html
    | * Modified by Phil Powell on 2/2/2003

    Perhaps you should go back to the working
    original, and start afresh.

    .....
    | The following applet I wrote had run just fine on my website until
    | sometime today, when it suddenly started running at an extremely slow
    | rate. I tried everything I could think of, including garbage
    | collection, to alleviate the problem, to no avail.
    |
    | I am suspecting a memory leak somewhere but I cannot discern where.

    What exactly makes you suspect that?
    System monitors, or wild waving
    about of arms?

    | Can anyone with more Java knowledge out there discern this for me

    Have you tried Elance?

    --
    Andrew Thompson
    * http://www.PhySci.org/ PhySci software suite
    * http://www.1point1C.org/ 1.1C - Superluminal!
    * http://www.AThompson.info/andrew/ personal site
     
    Andrew Thompson, Jan 25, 2004
    #3
  4. Phil Powell

    Filip Larsen Guest

    Phil Powell wrote

    > The following applet I wrote had run just fine on my website
    > until sometime today, when it suddenly started running at an
    > extremely slow rate.


    I cannot help you with that for the reasons other people have already
    stated

    However, I couldn't help noticing your "unescapeHTML" method. That
    implementation is very inefficient, searching multiple times for the
    same character and copying the whole string on every replace. You may
    want to consider using something that expand entities in "one pass", for
    instance

    import java.util.HashMap;
    import java.util.Map;

    public class EntityReplacer {

    /**
    * Add entity value to the map.
    * @param entity entity without ampersand and semicolon
    * @param value value of the entity
    */
    public void addEntity(String entity, String value) {
    entities.put(entity,value);
    }

    /**
    * Expand known entities in an xml string.
    * Unknown entities are not expanded.
    */
    public String expandEntities(String xml) {
    StringBuffer result = null;
    while (true) {
    int start = xml.indexOf('&');
    int end = xml.indexOf(';',start);
    if (start < 0 || end < 0) {
    break;
    }
    if (result == null) {
    result = new StringBuffer();
    }
    result.append(xml.substring(0,start));
    String entity = xml.substring(start+1,end);
    result.append(replace(entity));
    xml = xml.substring(end+1);
    }
    if (result == null) {
    return xml;
    }
    result.append(xml);
    return result.toString();
    }

    private static final Map standardEntities = new HashMap();
    static {
    standardEntities.put("amp","&");
    standardEntities.put("lt","<");
    standardEntities.put("gt",">");
    // and so on ..
    }

    private Map entities = new HashMap(standardEntities);

    private String replace(String entity) {
    String value = (String) entities.get(entity);
    return value != null ? value : '&'+entity+';';
    }
    }


    If you are using Java 1.1 or earlier you have to use Hashtable instead
    of Map and HashMap. For illustration, I have added a method to add
    custom entities to the entity map; you can of course add other methods
    for entity management if needed.


    Regards,
    --
    Filip Larsen
     
    Filip Larsen, Jan 25, 2004
    #4
  5. Phil Powell

    Mark Haase Guest

    In article <>,
    (Phil Powell) wrote:

    > or
    > at least tell me what steps I should be taking in my code to prevent
    > memory leaks for a continually-running applet?


    Don't lose pointers to objects. Set them to null when you're done with
    them.

    Keep in mind that Java eventually finds all orphaned objects and
    releases them, so a memory leak is pretty difficult to cause -- a true
    memory leak would be a problem with java.

    Having said that, GC in java takes a finite time to run and only runs
    once every so often. So its totally possible that if you're running
    through some sort of loop where you continually create objects very
    fast, or --worse yet-- create complex object graphs at very high speed,
    then you can definitely outrun the GC and use up your memory. At that
    point, the application will likely get very slow, and you will also get
    OutOfMemoryErrors.

    --
    |\/| /| |2 |<
    mehaase(at)sas(dot)upenn(dot)edu
     
    Mark Haase, Jan 26, 2004
    #5
  6. Phil Powell

    ketil v Guest

    Phil Powell skrev:
    <snip>
    > The following applet I wrote had run just fine on my website until
    > sometime today, when it suddenly started running at an extremely slow
    > rate. I tried everything I could think of, including garbage
    > collection, to alleviate the problem, to no avail.
    >
    > I am suspecting a memory leak somewhere but I cannot discern where.
    > Can anyone with more Java knowledge out there discern this for me or
    > at least tell me what steps I should be taking in my code to prevent
    > memory leaks for a continually-running applet?
    >
    > Thanx
    > Phil


    I've had similar problems on the earlier versions of Java 1.3.1 and earlier.
    solved it by simply upgrading to Java 1.4.2 - it got a better system for
    handling such issues.

    I know this solution sounds simple, but look at SUNs changelog for the
    different Java's and you will find that they have done a lot of work on
    this.

    --
    Ketil V.

    Liker RFC 1855 (og Pizza)
     
    ketil v, Jan 26, 2004
    #6
  7. In comp.lang.java.programmer Phil Powell <> wrote:

    : I am suspecting a memory leak somewhere but I cannot discern where.
    Get a profiler and find out?

    : Can anyone with more Java knowledge out there discern this for me or
    : at least tell me what steps I should be taking in my code to prevent
    : memory leaks for a continually-running applet?

    make sure you close all streams, resultsets...
    make sure you dispose/flush all windows, graphics and images when you
    are finished with them.

    Make sure you dont continously put new things into static hash/list.

    /robo
     
    Robert Olofsson, Jan 26, 2004
    #7
  8. Phil Powell

    Phil Powell Guest

    "Andrew Thompson" <> wrote in message news:<%gNQb.27030$>...
    > "Phil Powell" ...
    > | import java.io.*;
    >
    > The code you supplied does not compile.
    > Besides the lines that wrap, there are
    > three methods that are not implemented.
    >
    > You should also take efforts to ensure
    > that your code is the smallest compilable
    > example that demonstrates the problem.
    >
    > | * originally created by Rene Gagnon at
    > | http://www.rgagnon.com/javadetails/java-0307.html
    > | * Modified by Phil Powell on 2/2/2003
    >
    > Perhaps you should go back to the working
    > original, and start afresh.
    >
    > ....
    > | The following applet I wrote had run just fine on my website until
    > | sometime today, when it suddenly started running at an extremely slow
    > | rate. I tried everything I could think of, including garbage
    > | collection, to alleviate the problem, to no avail.
    > |
    > | I am suspecting a memory leak somewhere but I cannot discern where.
    >
    > What exactly makes you suspect that?
    > System monitors, or wild waving
    > about of arms?
    >
    > | Can anyone with more Java knowledge out there discern this for me
    >
    > Have you tried Elance?


    Are you always this helpful or am I just the lucky one to be so berated?

    Phil
     
    Phil Powell, Jan 31, 2004
    #8
  9. Phil Powell

    Phil Powell Guest

    "Filip Larsen" <> wrote in message news:<bv0f2o$285o$>...
    > Phil Powell wrote
    >
    > > The following applet I wrote had run just fine on my website
    > > until sometime today, when it suddenly started running at an
    > > extremely slow rate.

    >
    > I cannot help you with that for the reasons other people have already
    > stated
    >
    > However, I couldn't help noticing your "unescapeHTML" method. That
    > implementation is very inefficient, searching multiple times for the
    > same character and copying the whole string on every replace. You may
    > want to consider using something that expand entities in "one pass", for
    > instance
    >
    > import java.util.HashMap;
    > import java.util.Map;
    >
    > public class EntityReplacer {
    >
    > /**
    > * Add entity value to the map.
    > * @param entity entity without ampersand and semicolon
    > * @param value value of the entity
    > */
    > public void addEntity(String entity, String value) {
    > entities.put(entity,value);
    > }
    >
    > /**
    > * Expand known entities in an xml string.
    > * Unknown entities are not expanded.
    > */
    > public String expandEntities(String xml) {
    > StringBuffer result = null;
    > while (true) {
    > int start = xml.indexOf('&');
    > int end = xml.indexOf(';',start);
    > if (start < 0 || end < 0) {
    > break;
    > }
    > if (result == null) {
    > result = new StringBuffer();
    > }
    > result.append(xml.substring(0,start));
    > String entity = xml.substring(start+1,end);
    > result.append(replace(entity));
    > xml = xml.substring(end+1);
    > }
    > if (result == null) {
    > return xml;
    > }
    > result.append(xml);
    > return result.toString();
    > }
    >
    > private static final Map standardEntities = new HashMap();
    > static {
    > standardEntities.put("amp","&");
    > standardEntities.put("lt","<");
    > standardEntities.put("gt",">");
    > // and so on ..
    > }
    >
    > private Map entities = new HashMap(standardEntities);
    >
    > private String replace(String entity) {
    > String value = (String) entities.get(entity);
    > return value != null ? value : '&'+entity+';';
    > }
    > }
    >
    >
    > If you are using Java 1.1 or earlier you have to use Hashtable instead
    > of Map and HashMap. For illustration, I have added a method to add
    > custom entities to the entity map; you can of course add other methods
    > for entity management if needed.
    >
    >
    > Regards,


    Takk så meget.. I wound up trying a trick by changing "unescapeHTML"
    from "public static String" to "public synchronized static String" and
    for a couple of days it worked, suddenly only to lock up people's
    resources again no matter what browser they use.

    I am studying the class you wrote and with my limited Java knowledge
    will take a while to absorb but I'll see what happens, thanx!

    Phil
     
    Phil Powell, Jan 31, 2004
    #9
  10. Phil Powell

    Phil Powell Guest

    ketil v <> wrote in message news:<>...
    > Phil Powell skrev:
    > <snip>
    > > The following applet I wrote had run just fine on my website until
    > > sometime today, when it suddenly started running at an extremely slow
    > > rate. I tried everything I could think of, including garbage
    > > collection, to alleviate the problem, to no avail.
    > >
    > > I am suspecting a memory leak somewhere but I cannot discern where.
    > > Can anyone with more Java knowledge out there discern this for me or
    > > at least tell me what steps I should be taking in my code to prevent
    > > memory leaks for a continually-running applet?
    > >
    > > Thanx
    > > Phil

    >
    > I've had similar problems on the earlier versions of Java 1.3.1 and earlier.
    > solved it by simply upgrading to Java 1.4.2 - it got a better system for
    > handling such issues.
    >
    > I know this solution sounds simple, but look at SUNs changelog for the
    > different Java's and you will find that they have done a lot of work on
    > this.


    That would be an impractical solution, because this is an applet, and
    that would mean millions of users out there would have to self-upgrade
    to Java 1.4.2 to view my site :(

    I changed one of my recursive methods to a synchronized static
    recursive method and it worked for a couple of days.. and that was it.
    :(

    Phil
     
    Phil Powell, Jan 31, 2004
    #10
  11. "Phil Powell" ...
    > "Andrew Thompson" ...
    > > "Phil Powell" ...
    > > | import java.io.*;
    > >
    > > The code you supplied does not compile.

    ...
    > > | * originally created by Rene Gagnon at
    > > | http://www.rgagnon.com/javadetails/java-0307.html

    ....
    > > Perhaps you should go back to the working
    > > original, and start afresh.
    > > ....
    > > | I am suspecting a memory leak somewhere but I cannot discern where.
    > >
    > > What exactly makes you suspect that?

    ....
    > > | Can anyone with more Java knowledge out there discern this for me
    > >
    > > Have you tried Elance?

    >
    > Are you always this helpful or am I just the lucky one to be so berated?


    Berated?! You obviously have not read
    many of my posts if you think I was
    berating you.

    The same could be said of your sarcastic
    question 'always this helpful?' If you'd read
    my posts you would have seen that my
    responses cover a wide range of responses,
    depending on the quality of the question.

    I note that you still have not bothered to
    supply compilable code, and if you cannot
    recognise the good bits of advice as such,
    I see no point in wasting more time on you.
    There are others more deserving.

    Good luck.

    --
    Andrew Thompson
    * http://www.PhySci.org/ Open-source software suite
    * http://www.PhySci.org/codes/ Web & IT Help
    * http://www.1point1C.org/ Science & Technology
     
    Andrew Thompson, Jan 31, 2004
    #11
  12. Phil Powell

    Phil Powell Guest

    "Andrew Thompson" <> wrote in message news:<YtVSb.37580$>...
    > "Phil Powell" ...
    > > "Andrew Thompson" ...
    > > > "Phil Powell" ...
    > > > | import java.io.*;
    > > >
    > > > The code you supplied does not compile.

    > ..
    > > > | * originally created by Rene Gagnon at
    > > > | http://www.rgagnon.com/javadetails/java-0307.html

    > ...
    > > > Perhaps you should go back to the working
    > > > original, and start afresh.
    > > > ....
    > > > | I am suspecting a memory leak somewhere but I cannot discern where.
    > > >
    > > > What exactly makes you suspect that?

    > ...
    > > > | Can anyone with more Java knowledge out there discern this for me
    > > >
    > > > Have you tried Elance?

    > >
    > > Are you always this helpful or am I just the lucky one to be so berated?

    >
    > Berated?! You obviously have not read
    > many of my posts if you think I was
    > berating you.
    >
    > The same could be said of your sarcastic
    > question 'always this helpful?' If you'd read
    > my posts you would have seen that my
    > responses cover a wide range of responses,
    > depending on the quality of the question.
    >
    > I note that you still have not bothered to
    > supply compilable code, and if you cannot
    > recognise the good bits of advice as such,
    > I see no point in wasting more time on you.
    > There are others more deserving.
    >
    > Good luck.


    You wasted my time by forcing me to read this. I could easily supply
    compilable code and in fact, will do so now, not for your benefit but
    hopefully someone more helpful and more understanding of the needs of
    others rather than your superior intellect.

    Please don't waste your time (or mine) replying. Spend it more
    worthily by understanding how to interact with other humans.

    no one deserves your drivel.

    Phil

    import java.io.*;
    import java.net.*;
    import java.awt.*;
    import java.util.*;
    import java.applet.*;
    import java.awt.event.*;


    /**
    * @version JDK 1.3
    * @author Phil Powell
    */

    /*---------------------------------------------------------------------------------------------
    * ParseNews will receive a Phil-homegrown XML-like script from a PHP
    file, parse through it,
    * and will display each news item within the applet. The "View News"
    button, upon being
    * pressed, will perform an AppletContext showDocument action to go to
    the URL formed by the
    * passed ID per Vector iteration. News items will continually scroll
    from bottom to top and
    * then revert back to the first one
    *
    * Created 2/1/2003
    *--------------------------------------------------------------------------------------------*/


    public class ParseNews extends Applet {

    // INITIALIZE VARS AND VAR SCOPE
    protected Vector parseVector = new Vector();
    protected Thread scrollThread = null;
    protected ActionListener al = null;
    private int xpos = 0, ypos = 0, index = 0;
    private boolean hasStartedScroll = false, hasAddedAction = false;
    private Button newsButton = new Button("View News");
    private Panel scrollPanel, buttonPanel;

    /*----------------------------------------------------------------------------------
    * Vector method parser will return a vector of a 3-item array
    containing the id,
    * shortdescription and ranking of what was passed from the PHP
    script
    *
    *----------------------------------------------------------------------------------*/

    private Vector parser(String stuff) {
    Vector parsedXHTML = new Vector();
    String idSection = "", sdSection = "", rankingSection = "";
    boolean hasNews = true;
    // CHECK TO SEE IF THERE IS ANYTHING FROM THE PHP SCRIPT - IF NOT NO
    NEWS.XML FOUND
    if (stuff.length() > 0 && (stuff.indexOf("<ID>") < 0 ||
    stuff.indexOf("<SHORTDESCRIPTION>") < 0 || stuff.indexOf("<RANKING>")
    < 0))
    hasNews = false;
    while (stuff.length() > 0 && hasNews) {
    String[] rowArray = new String[3];
    idSection = stuff.substring(stuff.indexOf("<ID>") + 4,
    stuff.indexOf("</ID>"));
    sdSection = stuff.substring(stuff.indexOf("<SHORTDESCRIPTION>") +
    18, stuff.indexOf("</SHORTDESCRIPTION>"));
    rankingSection = stuff.substring(stuff.indexOf("<RANKING>") + 9,
    stuff.indexOf("</RANKING>"));
    if (stuff.indexOf("</RANKING>") + 10 < stuff.length()) {
    stuff = stuff.substring(stuff.indexOf("</RANKING>") + 10,
    stuff.length()).trim();
    } else {
    stuff = "";
    }
    rowArray[0] = idSection;
    rowArray[1] = sdSection;
    rowArray[2] = rankingSection;
    parsedXHTML.addElement(rowArray);
    }
    if (!hasNews) parsedXHTML.addElement(new String[]{"0", "No news file
    found", "0"});

    if (((String[])parsedXHTML.elementAt(0))[0] != "0")
    parsedXHTML = sortedVector(parsedXHTML); // SORT INTO ORDER OF
    RANKING
    return parsedXHTML;
    }

    /*------------------------------------------------------------------------------------------
    * Vector method sortedVector will sort the vector created by parser
    into numerical order
    * according to the ranking value found as the third element of each
    3-item array found in
    * each vector element
    *
    *------------------------------------------------------------------------------------------*/

    private Vector sortedVector(Vector vectorToSort) {
    Vector mySortedVector = new Vector();
    int[] rankingArray = new int[vectorToSort.size()];
    for (int i = 0; i < vectorToSort.size(); i++) {
    rankingArray =
    Integer.parseInt(((String[])vectorToSort.elementAt(i))[2]);
    }

    // YOUR OWN SORT
    rankingArray = mySortedArray(rankingArray);
    for (int i = 0; i < rankingArray.length; i++) {
    for (int j = 0; j < vectorToSort.size(); j++) {
    if (Integer.parseInt(((String[])vectorToSort.elementAt(j))[2]) ==
    rankingArray) {
    mySortedVector.addElement(vectorToSort.elementAt(j));
    break;
    }
    }
    }
    if (mySortedVector.size() > 0) {
    return mySortedVector;
    } else {
    return vectorToSort;
    }
    }

    // BUBBLE SORT INTEGER ARRAY METHOD - IN LIEU OF M$ NOT ALLOWING FOR
    JAVA.UTIL.ARRAYS - GRRRR
    private int[] mySortedArray(int[] origArray) {
    int intTemp;
    for (int i = 0; i < origArray.length - 1; i++) {
    for (int j = i + 1; j < origArray.length; j++) {
    if (origArray > origArray[j]) {
    intTemp = origArray;
    origArray = origArray[j];
    origArray[j] = intTemp;
    }
    }
    }
    return origArray;
    }


    /*------------------------------------------------------------------------------------------
    * unescapeHTML String method will convert ASCII characters to their
    "funky" equivalent.
    *
    * originally created by Rene Gagnon at
    http://www.rgagnon.com/javadetails/java-0307.html
    * Modified by Phil Powell on 2/2/2003
    *
    *-----------------------------------------------------------------------------------------*/

    public static String unescapeHTML(String schtuff) {
    String[][] asciiArray =
    {{"&lt;", "<"}, {"&gt;", ">" }, {"&amp;", "&" }, {"&quot;", "\""},
    {"&agrave;", "à"},
    {"&Agrave;", "À"}, {"&acirc;", "â"}, {"&auml;", "ä"}, {"&Auml;",
    "Ä"}, {"&Acirc;", "Â"},
    {"&aring;", "å"}, {"&Aring;", "Å"}, {"&aelig;", "æ"}, {"&AElig;",
    "Æ"}, {"&ccedil;", "ç"},
    {"&Ccedil;", "Ç"}, {"&eacute;", "é"}, {"&Eacute;", "É"},
    {"&egrave;", "è"},
    {"&Egrave;", "È"}, {"&ecirc;", "ê"}, {"&Ecirc;", "Ê"}, {"&euml;",
    "ë"}, {"&Euml;", "Ë"},
    {"&iuml;", "ï"}, {"&Iuml;", "Ï"}, {"&ocirc;", "ô"}, {"&Ocirc;",
    "Ô"}, {"&ouml;", "ö"},
    {"&Ouml;", "Ö"}, {"&oslash;", "ø"}, {"&Oslash;", "Ø"}, {"&szlig;",
    "ß"},
    {"&ugrave;", "ù"}, {"&Ugrave;", "Ù"}, {"&ucirc;", "û"},
    {"&Ucirc;", "Û"},
    {"&uuml;", "ü"}, {"&Uuml;", "Ü"}, {"&nbsp;", " "}, {"&reg;",
    "\u00a9"}, {"&copy;", "\u00ae"},
    {"&euro;", "\u20a0"}
    };
    int flag = -1, ampIndex = -1;
    if (schtuff.indexOf("&") >= 0) {
    if (schtuff.indexOf(";") > schtuff.indexOf("&")) {
    String asciiStuff = schtuff.substring(schtuff.indexOf("&"),
    schtuff.indexOf(";") + 1);
    ampIndex = schtuff.indexOf("&");
    while (asciiStuff.substring(1, asciiStuff.length()).indexOf("&")
    >= 0) {

    ampIndex = schtuff.indexOf("&", ampIndex + 1);
    asciiStuff = asciiStuff.substring(1, asciiStuff.length()).trim();
    }
    for (int j = 0; j < asciiArray.length; j++) {
    if (asciiArray[j][0].equals(asciiStuff)) {
    flag = j;
    break;
    }
    }
    if (flag >= 0) {
    schtuff = schtuff.substring(0, ampIndex) + asciiArray[flag][1] +
    schtuff.substring(schtuff.indexOf(";") + 1,
    schtuff.length());
    return unescapeHTML(schtuff); // RECURSIVE
    }
    }
    return schtuff;
    } else {
    return schtuff.trim();
    }
    }

    // INITIALIZE APPLET, RETRIEVE VALUES FROM PHP SCRIPT AND SORT INTO
    VECTOR

    public void init() {
    String stuff = null;
    try {
    URL url = new URL(getParameter("url"));
    URLConnection conn = url.openConnection();
    conn.setDoInput(true);
    conn.setDoOutput(false);
    conn.setUseCaches(false);
    conn.setDefaultUseCaches(false);
    conn.setRequestProperty("Content-type", "text/html");
    BufferedReader fromURL = new BufferedReader(new
    InputStreamReader(conn.getInputStream()));
    stuff = fromURL.readLine();
    // CLOSE CONNECTION
    fromURL.close();
    fromURL = null;
    conn = null;
    } catch (Exception e) {
    parseVector.addElement(new String[]{"0", "Could not connect to
    retrieve news", "0"});
    }
    if (stuff != null && stuff != "") {
    parseVector = parser(stuff);
    } else if (parseVector.size() == 0) {
    parseVector.addElement(new String[]{"0", "No current news found",
    "0"});
    }

    setBackground(Color.black);
    setForeground(Color.white);
    setFont(new Font("Garamond", Font.BOLD, 10));
    scrollPanel = new Panel();
    scrollPanel.setVisible(false);
    buttonPanel = new Panel();
    buttonPanel.setBackground(Color.black);
    buttonPanel.setForeground(Color.white);
    newsButton.setBackground(Color.red);
    newsButton.setForeground(Color.white);
    // ONLY ADD THE "VIEW NEWS" BUTTON IF THERE IS NEWS TO VIEW!
    if (((String[])parseVector.elementAt(0))[0] != "0") {
    buttonPanel.add(newsButton);
    } else {
    buttonPanel.setVisible(false);
    }
    add(scrollPanel);
    add(buttonPanel);

    scrollThread = new Thread(new ScrollNews());
    scrollThread.start();

    }

    /*------------------------------------------------------------------------------------------
    * Reinitialize value of the ActionListener object for the "View
    News" button, along with
    * display news item, with the "scrolling" effect handled by the
    runnable class ScrollNews
    *-----------------------------------------------------------------------------------------*/

    public void paint(Graphics g) {

    FontMetrics fm = g.getFontMetrics();
    //((String[])parseVector.elementAt(index))[1] =
    // unescapeHTML(((String[])parseVector.elementAt(index))[1]);

    System.out.println(((String[])parseVector.elementAt(index))[1]);

    ((String[])parseVector.elementAt(index))[1] =
    java.net.URLEncoder.encode(((String[])parseVector.elementAt(index))[1]);
    xpos = (getSize().width -
    fm.stringWidth(((String[])parseVector.elementAt(index))[1])) / 2;
    if (ypos <= 0) {
    g.drawString("", 0, 0); // CLEAR THE APPLET
    ypos = getSize().height;
    hasAddedAction = false;
    if (((String[])parseVector.elementAt(0))[0] != "0")
    newsButton.removeActionListener(al);
    if (index < parseVector.size() - 1 && hasStartedScroll) {
    index++;
    } else if (index > 0) {
    index = 0;
    hasStartedScroll = false;
    }
    }
    if (((String[])parseVector.elementAt(0))[0] != "0" &&
    !hasAddedAction) {
    hasAddedAction = true;
    al = new ViewNews("http://valsignalandet.com/cgi-bin/cgiwrap/ppowell/news.cgi?id="
    +
    ((String[])parseVector.elementAt(index))[0]);
    newsButton.addActionListener(al);
    }

    g.drawString(((String[])parseVector.elementAt(index))[1], xpos,
    ypos);
    hasStartedScroll = true;

    }



    class ScrollNews implements Runnable {

    public void run() {
    while (true) {
    ypos = ypos - 1;
    ParseNews.this.repaint();
    try {
    Thread.sleep(50);
    } catch (InterruptedException e) {}
    System.gc(); // GARBAGE COLLECTION - MIGHT HELP WITH POTENTIAL
    MEMORY LEAK
    }
    }

    }

    class ViewNews implements ActionListener {

    private String urlString = "";

    public ViewNews(String urlString) {
    this.urlString = urlString;
    }

    public void actionPerformed(ActionEvent e) {
    try {
    ParseNews.this.getAppletContext().showDocument(new
    URL(urlString));
    } catch (Exception oops) {
    System.out.println("could not go to " + urlString);
    oops.printStackTrace();
    }
    }

    }

    }
     
    Phil Powell, Feb 1, 2004
    #12
  13. Phil Powell

    Phil Powell Guest

    Mark Haase <> wrote in message news:<>...
    > In article <>,
    > (Phil Powell) wrote:
    >
    > > or
    > > at least tell me what steps I should be taking in my code to prevent
    > > memory leaks for a continually-running applet?

    >
    > Don't lose pointers to objects. Set them to null when you're done with
    > them.
    >
    > Keep in mind that Java eventually finds all orphaned objects and
    > releases them, so a memory leak is pretty difficult to cause -- a true
    > memory leak would be a problem with java.
    >
    > Having said that, GC in java takes a finite time to run and only runs
    > once every so often. So its totally possible that if you're running
    > through some sort of loop where you continually create objects very
    > fast, or --worse yet-- create complex object graphs at very high speed,
    > then you can definitely outrun the GC and use up your memory. At that
    > point, the application will likely get very slow, and you will also get
    > OutOfMemoryErrors.


    Following is the entire compilable code (per requests):

    import java.io.*;
    import java.net.*;
    import java.awt.*;
    import java.util.*;
    import java.applet.*;
    import java.awt.event.*;


    /**
    * @version JDK 1.3
    * @author Phil Powell
    */

    /*---------------------------------------------------------------------------------------------
    * ParseNews will receive a Phil-homegrown XML-like script from a PHP
    file, parse through it,
    * and will display each news item within the applet. The "View News"
    button, upon being
    * pressed, will perform an AppletContext showDocument action to go to
    the URL formed by the
    * passed ID per Vector iteration. News items will continually scroll
    from bottom to top and
    * then revert back to the first one
    *
    * Created 2/1/2003
    *--------------------------------------------------------------------------------------------*/


    public class ParseNews extends Applet {

    // INITIALIZE VARS AND VAR SCOPE
    protected Vector parseVector = new Vector();
    protected Thread scrollThread = null;
    protected ActionListener al = null;
    private int xpos = 0, ypos = 0, index = 0;
    private boolean hasStartedScroll = false, hasAddedAction = false;
    private Button newsButton = new Button("View News");
    private Panel scrollPanel, buttonPanel;

    /*----------------------------------------------------------------------------------
    * Vector method parser will return a vector of a 3-item array
    containing the id,
    * shortdescription and ranking of what was passed from the PHP
    script
    *
    *----------------------------------------------------------------------------------*/

    private Vector parser(String stuff) {
    Vector parsedXHTML = new Vector();
    String idSection = "", sdSection = "", rankingSection = "";
    boolean hasNews = true;
    // CHECK TO SEE IF THERE IS ANYTHING FROM THE PHP SCRIPT - IF NOT NO
    NEWS.XML FOUND
    if (stuff.length() > 0 && (stuff.indexOf("<ID>") < 0 ||
    stuff.indexOf("<SHORTDESCRIPTION>") < 0 || stuff.indexOf("<RANKING>")
    < 0))
    hasNews = false;
    while (stuff.length() > 0 && hasNews) {
    String[] rowArray = new String[3];
    idSection = stuff.substring(stuff.indexOf("<ID>") + 4,
    stuff.indexOf("</ID>"));
    sdSection = stuff.substring(stuff.indexOf("<SHORTDESCRIPTION>") +
    18, stuff.indexOf("</SHORTDESCRIPTION>"));
    rankingSection = stuff.substring(stuff.indexOf("<RANKING>") + 9,
    stuff.indexOf("</RANKING>"));
    if (stuff.indexOf("</RANKING>") + 10 < stuff.length()) {
    stuff = stuff.substring(stuff.indexOf("</RANKING>") + 10,
    stuff.length()).trim();
    } else {
    stuff = "";
    }
    rowArray[0] = idSection;
    rowArray[1] = sdSection;
    rowArray[2] = rankingSection;
    parsedXHTML.addElement(rowArray);
    }
    if (!hasNews) parsedXHTML.addElement(new String[]{"0", "No news file
    found", "0"});

    if (((String[])parsedXHTML.elementAt(0))[0] != "0")
    parsedXHTML = sortedVector(parsedXHTML); // SORT INTO ORDER OF
    RANKING
    return parsedXHTML;
    }

    /*------------------------------------------------------------------------------------------
    * Vector method sortedVector will sort the vector created by parser
    into numerical order
    * according to the ranking value found as the third element of each
    3-item array found in
    * each vector element
    *
    *------------------------------------------------------------------------------------------*/

    private Vector sortedVector(Vector vectorToSort) {
    Vector mySortedVector = new Vector();
    int[] rankingArray = new int[vectorToSort.size()];
    for (int i = 0; i < vectorToSort.size(); i++) {
    rankingArray =
    Integer.parseInt(((String[])vectorToSort.elementAt(i))[2]);
    }

    // YOUR OWN SORT
    rankingArray = mySortedArray(rankingArray);
    for (int i = 0; i < rankingArray.length; i++) {
    for (int j = 0; j < vectorToSort.size(); j++) {
    if (Integer.parseInt(((String[])vectorToSort.elementAt(j))[2]) ==
    rankingArray) {
    mySortedVector.addElement(vectorToSort.elementAt(j));
    break;
    }
    }
    }
    if (mySortedVector.size() > 0) {
    return mySortedVector;
    } else {
    return vectorToSort;
    }
    }

    // BUBBLE SORT INTEGER ARRAY METHOD - IN LIEU OF M$ NOT ALLOWING FOR
    JAVA.UTIL.ARRAYS - GRRRR
    private int[] mySortedArray(int[] origArray) {
    int intTemp;
    for (int i = 0; i < origArray.length - 1; i++) {
    for (int j = i + 1; j < origArray.length; j++) {
    if (origArray > origArray[j]) {
    intTemp = origArray;
    origArray = origArray[j];
    origArray[j] = intTemp;
    }
    }
    }
    return origArray;
    }


    /*------------------------------------------------------------------------------------------
    * unescapeHTML String method will convert ASCII characters to their
    "funky" equivalent.
    *
    * originally created by Rene Gagnon at
    http://www.rgagnon.com/javadetails/java-0307.html
    * Modified by Phil Powell on 2/2/2003
    *
    *-----------------------------------------------------------------------------------------*/

    public static String unescapeHTML(String schtuff) {
    String[][] asciiArray =
    {{"&lt;", "<"}, {"&gt;", ">" }, {"&amp;", "&" }, {"&quot;", "\""},
    {"&agrave;", "à"},
    {"&Agrave;", "À"}, {"&acirc;", "â"}, {"&auml;", "ä"}, {"&Auml;",
    "Ä"}, {"&Acirc;", "Â"},
    {"&aring;", "å"}, {"&Aring;", "Å"}, {"&aelig;", "æ"}, {"&AElig;",
    "Æ"}, {"&ccedil;", "ç"},
    {"&Ccedil;", "Ç"}, {"&eacute;", "é"}, {"&Eacute;", "É"},
    {"&egrave;", "è"},
    {"&Egrave;", "È"}, {"&ecirc;", "ê"}, {"&Ecirc;", "Ê"}, {"&euml;",
    "ë"}, {"&Euml;", "Ë"},
    {"&iuml;", "ï"}, {"&Iuml;", "Ï"}, {"&ocirc;", "ô"}, {"&Ocirc;",
    "Ô"}, {"&ouml;", "ö"},
    {"&Ouml;", "Ö"}, {"&oslash;", "ø"}, {"&Oslash;", "Ø"}, {"&szlig;",
    "ß"},
    {"&ugrave;", "ù"}, {"&Ugrave;", "Ù"}, {"&ucirc;", "û"},
    {"&Ucirc;", "Û"},
    {"&uuml;", "ü"}, {"&Uuml;", "Ü"}, {"&nbsp;", " "}, {"&reg;",
    "\u00a9"}, {"&copy;", "\u00ae"},
    {"&euro;", "\u20a0"}
    };
    int flag = -1, ampIndex = -1;
    if (schtuff.indexOf("&") >= 0) {
    if (schtuff.indexOf(";") > schtuff.indexOf("&")) {
    String asciiStuff = schtuff.substring(schtuff.indexOf("&"),
    schtuff.indexOf(";") + 1);
    ampIndex = schtuff.indexOf("&");
    while (asciiStuff.substring(1, asciiStuff.length()).indexOf("&")
    >= 0) {

    ampIndex = schtuff.indexOf("&", ampIndex + 1);
    asciiStuff = asciiStuff.substring(1, asciiStuff.length()).trim();
    }
    for (int j = 0; j < asciiArray.length; j++) {
    if (asciiArray[j][0].equals(asciiStuff)) {
    flag = j;
    break;
    }
    }
    if (flag >= 0) {
    schtuff = schtuff.substring(0, ampIndex) + asciiArray[flag][1] +
    schtuff.substring(schtuff.indexOf(";") + 1,
    schtuff.length());
    return unescapeHTML(schtuff); // RECURSIVE
    }
    }
    return schtuff;
    } else {
    return schtuff.trim();
    }
    }

    // INITIALIZE APPLET, RETRIEVE VALUES FROM PHP SCRIPT AND SORT INTO
    VECTOR

    public void init() {
    String stuff = null;
    try {
    URL url = new URL(getParameter("url"));
    URLConnection conn = url.openConnection();
    conn.setDoInput(true);
    conn.setDoOutput(false);
    conn.setUseCaches(false);
    conn.setDefaultUseCaches(false);
    conn.setRequestProperty("Content-type", "text/html");
    BufferedReader fromURL = new BufferedReader(new
    InputStreamReader(conn.getInputStream()));
    stuff = fromURL.readLine();
    // CLOSE CONNECTION
    fromURL.close();
    fromURL = null;
    conn = null;
    } catch (Exception e) {
    parseVector.addElement(new String[]{"0", "Could not connect to
    retrieve news", "0"});
    }
    if (stuff != null && stuff != "") {
    parseVector = parser(stuff);
    } else if (parseVector.size() == 0) {
    parseVector.addElement(new String[]{"0", "No current news found",
    "0"});
    }

    setBackground(Color.black);
    setForeground(Color.white);
    setFont(new Font("Garamond", Font.BOLD, 10));
    scrollPanel = new Panel();
    scrollPanel.setVisible(false);
    buttonPanel = new Panel();
    buttonPanel.setBackground(Color.black);
    buttonPanel.setForeground(Color.white);
    newsButton.setBackground(Color.red);
    newsButton.setForeground(Color.white);
    // ONLY ADD THE "VIEW NEWS" BUTTON IF THERE IS NEWS TO VIEW!
    if (((String[])parseVector.elementAt(0))[0] != "0") {
    buttonPanel.add(newsButton);
    } else {
    buttonPanel.setVisible(false);
    }
    add(scrollPanel);
    add(buttonPanel);

    scrollThread = new Thread(new ScrollNews());
    scrollThread.start();

    }

    /*------------------------------------------------------------------------------------------
    * Reinitialize value of the ActionListener object for the "View
    News" button, along with
    * display news item, with the "scrolling" effect handled by the
    runnable class ScrollNews
    *-----------------------------------------------------------------------------------------*/

    public void paint(Graphics g) {

    FontMetrics fm = g.getFontMetrics();
    //((String[])parseVector.elementAt(index))[1] =
    // unescapeHTML(((String[])parseVector.elementAt(index))[1]);

    System.out.println(((String[])parseVector.elementAt(index))[1]);

    ((String[])parseVector.elementAt(index))[1] =
    java.net.URLEncoder.encode(((String[])parseVector.elementAt(index))[1]);
    xpos = (getSize().width -
    fm.stringWidth(((String[])parseVector.elementAt(index))[1])) / 2;
    if (ypos <= 0) {
    g.drawString("", 0, 0); // CLEAR THE APPLET
    ypos = getSize().height;
    hasAddedAction = false;
    if (((String[])parseVector.elementAt(0))[0] != "0")
    newsButton.removeActionListener(al);
    if (index < parseVector.size() - 1 && hasStartedScroll) {
    index++;
    } else if (index > 0) {
    index = 0;
    hasStartedScroll = false;
    }
    }
    if (((String[])parseVector.elementAt(0))[0] != "0" &&
    !hasAddedAction) {
    hasAddedAction = true;
    al = new ViewNews("http://valsignalandet.com/cgi-bin/cgiwrap/ppowell/news.cgi?id="
    +
    ((String[])parseVector.elementAt(index))[0]);
    newsButton.addActionListener(al);
    }

    g.drawString(((String[])parseVector.elementAt(index))[1], xpos,
    ypos);
    hasStartedScroll = true;

    }



    class ScrollNews implements Runnable {

    public void run() {
    while (true) {
    ypos = ypos - 1;
    ParseNews.this.repaint();
    try {
    Thread.sleep(50);
    } catch (InterruptedException e) {}
    System.gc(); // GARBAGE COLLECTION - MIGHT HELP WITH POTENTIAL
    MEMORY LEAK
    }
    }

    }

    class ViewNews implements ActionListener {

    private String urlString = "";

    public ViewNews(String urlString) {
    this.urlString = urlString;
    }

    public void actionPerformed(ActionEvent e) {
    try {
    ParseNews.this.getAppletContext().showDocument(new
    URL(urlString));
    } catch (Exception oops) {
    System.out.println("could not go to " + urlString);
    oops.printStackTrace();
    }
    }

    }

    }


    ----

    I don't know if that will help or not, but it's worth a try. I tried
    every trick I know, to no avail at this point. Do note the line
    "java.net.URLEncoder.encode()"

    Phil
     
    Phil Powell, Feb 1, 2004
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. CW
    Replies:
    6
    Views:
    2,954
    Shiva
    Oct 11, 2004
  2. ©®
    Replies:
    6
    Views:
    349
    Toby A Inkster
    Sep 23, 2003
  3. Replies:
    19
    Views:
    566
    Alf P. Steinbach
    Jan 30, 2008
  4. Replies:
    5
    Views:
    311
    Diez B. Roggisch
    Aug 2, 2008
  5. George Maney
    Replies:
    1
    Views:
    302
    Lucien
    Jan 22, 2004
Loading...

Share This Page