Arcade Village Blog

From applet to application (Part 4): cookies

2019-06-19 Java programming
We come to the last part of this series of articles giving leads to turn your applets into applications.
Browsers offered applets the ability to save data in the form of cookies. This is what we are going to study today.
Netscape proposed to communicate with the browser via the JSObject class. It allowed to call javascript functions or to access cookies.

ArcadeVillage's games used it to keep their best scores. I created a Cookie object that I add here as an indication :

import java.util.*;
import java.text.SimpleDateFormat;
import java.applet.Applet;
import netscape.javascript.JSObject;
import netscape.javascript.JSException;


/**
* Cookies
*
*
* The cookies class represent a persistent set of properties.

* The properties can be saved or loading by cookies on the page

* Each key is a String. The value is a string with a date associated

*
* @see Hashtable
* @see Properties
*
* @since JDK 1.1
*
*/

public class Cookies extends Hashtable
{

/**
*
* Default number of day to keep a cookie
*
*/
public int dd = 30;

protected class CookieValue
{
String value;
Calendar exp;
}
/**
*
* Creates an empty cookies list with no default values.
*
*/
public Cookies()
{
}

/**
*
* Creates an empty cookies list with a default number of day to keep the cookies.
* @param dd - the time in days to keep the cookie ( default is 30 ) .
*
*/

public Cookies( int dd )
{
super();
this.dd = dd;
}
/**
*
* Load the cookies list associated to the document where is the given applet.
* @param a - the applet.
*
*
*/

public synchronized void load( Applet a )
{
String key;
CookieValue cv;
//
// Now, we search all the cookies. Each cookies is separated by a ';'
//
int is = 0;
int i = 0;
try
{
JSObject win = JSObject.getWindow(a);
JSObject doc = (JSObject)win.getMember("document");
String s = ( String )doc.getMember("cookie" );
while ( is < s.length() )
{
// First we seach a =
cv = new CookieValue();
i = s.indexOf("=", is);
if ( i == -1 )
{
System.out.println("Error in cookie string :"+s);
return;
}
key = s.substring(is, i);
is = ++i;
// Now we search a ;
i = s.indexOf(";", is);
if ( i == -1 )
{
cv.value = s.substring(is);
is = s.length();
}
else
{
cv.value = s.substring(is,i);
is = i + 1; // Because the return string add a blank;
if ( s.charAt(is)==' ' ) is++;
}
put( key, cv );
}
}
catch( Exception e )
{
System.out.println("Load :"+e);
}
}
/**
*
* Returns an enumeration of all the keys in this property list, including the keys in the default property list.
* @return : an enumeration of all the keys in this property list, including the keys in the default property list.
*
* @see Enumeration
*
*/

public Enumeration propertyNames()
{
return this.keys();
}

/**
*
* Save the cookies list associated to the document where is the given applet.
* @param a - the applet.
*
*
*/
public synchronized void save( Applet a )
{
String s = "";
String sdate;
String k;
CookieValue cv;
SimpleDateFormat df = new SimpleDateFormat("EEE, d MMM yyyy hh:mm:ss", Locale.US);
//
// We take all the properties and create a cookie string
//
Enumeration e = this.keys();
while ( e.hasMoreElements() )
{
k = ( String )e.nextElement();
cv = ( CookieValue )get(k);
if ( s.length() != 0 ) s = s + ";";
sdate = cv.exp == null ? "" : "+'; expires="+df.format(cv.exp.getTime())+" UTC'";

s = s + "'"+k+"='+escape('"+cv.value+"')"+sdate;
}
try
{
JSObject win = JSObject.getWindow(a);
s = "document.cookie="+s+";";
win.eval(s);
}
catch( Exception ex )
{
System.out.println("Cookie : "+ex);
}


}

/**
*
* Searches for the property with the specified key in this property list. If the key is not found in this property list, the default property list, and its defaults, recursively, are then checked. The method returns null if the property is not found.
* @param key - the property key.
* @return the value in this property list with the specified key value.
*
*/

public String getProperty(String key)
{
CookieValue v = ( CookieValue )this.get(key);
return( v == null ? null : v.value );
}

/**
*
* Searches for the property with the specified key in this property list.

* If the key is not found in this property list, the default property list, and its defaults, recursively, are then checked.

* The method returns the default value argument if the property is not found.

* Parameters:
*
* @param key - the hashtable key.
* @param defaultvalue - a default value.
*
* @return the value in this property list with the specified key value.
*
*/

public String getProperty(String key,
String defaultValue)
{
String s = getProperty(key);
return( s == null ? defaultValue : s );
}

/**
*
* Put the property with the specified key in the property list.
* The expiration date of the property is the default expiration date
public void setProperty(String key, String val )
{
setProperty(key, val, dd );
}

/**
*
* Put the property with the specified key in the property list with the given
* expiration date
*
* @param key - the hashtable key.
* @param val - the value.
* @param nday - the number of day to keep the cookie.
*
*
*/
public void setProperty(String key, String val, int nday )
{
CookieValue v = new CookieValue();
v.value = val;
v.exp = Calendar.getInstance();
v.exp.add( Calendar.DAY_OF_MONTH, nday );
put( key, v );
}
}

My Cookies object was derived from Hashtable and allowed to associate values ​​with a key and to save or load these values ​​by JSObject.

The browser associated cookies to a web page.

When we work with applications, there is no browser, so it's up to us to specify where to store and retrieve cookies.

Most application systems offer disk space associated with the user. The path to this space is given by the code:
System.getProperty ( "user.home")

In replacement of the Hashtable object, I use, just as for the parameters, the Properties object which offers the possibility to save its values ​​in the form of an XML file.

The browser linked cookies to a URL. So I imitated its operation. My new object is created by specifying a storage directory.
Each file is stored in a different directory, subdirectory of the user directory.

The getPathName function of my object gives the name of the backup file.

private String getPathName()
{
String s = System.getProperty("user.home")+System.getProperty("file.separator")+"arcadevillage"+System.getProperty("file.separator")+this.dock;
s = this.majSeparator(s);
return s;
}

private String getFileName()
{
return this.getPathName()+System.getProperty("file.separator")+"acm.ini";
}


Les répertoires de sauvegarde son en plus fils d'un répertoire "arcadevillage". J'ai fait ce choix pour ne pas perturber d'éventuels autres programmes qui utiliseraient les mêmes noms de fichier. Ma ancienne classe Cookies est donc remplacée dans les applications par la classe AppletCookieManager.
Voici le code de cette nouvel classe.

package cccookie;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.URL;
import java.util.Properties;


public class AppletCookieMgr
{
private URL docbase;
private String dock; // Le répertoire où seront sauvés les cookies
private Properties params = new Properties();

public AppletCookieMgr( URL idocbase )
{
this.docbase = idocbase;
this.dock = this.docbase.getPath();
this.dock = this.majSeparator(this.dock);
this.dock = this.dock.replace(System.getProperty("user.dir"), "");
}

public AppletCookieMgr( String sdock )
{
this.dock = sdock;
}

public void load()
{
FileInputStream f;
try
{
f = new FileInputStream(this.getFileName());
// load a properties file
params.load(f);
f.close();
}
catch (Exception ex)
{
params.clear(); // Pas de fichier, on vide
}
}

private String majSeparator( String s )
{
s = s.replace("/", System.getProperty("file.separator"));
s = s.replace("", System.getProperty("file.separator"));
return s;
}
private String getPathName()
{
String s = System.getProperty("user.home")+System.getProperty("file.separator")+"arcadevillage"+System.getProperty("file.separator")+this.dock;
s = this.majSeparator(s);
return s;
}

private String getFileName()
{
return this.getPathName()+System.getProperty("file.separator")+"acm.ini";
}

public int getInteger( String name, int defvalue )
{
String s = getString( name );

return ( s == null ? defvalue : Integer.parseInt(s) );
}

public String getString( String name )
{
return params.getProperty(name, null);
}

public void setInteger( String name, int value )
{
setString(name, String.valueOf(value));
}

public void setString( String name, String value )
{
params.setProperty(name, value);
}

public void flush()
{
FileOutputStream f;
File rep=new File(getPathName());
if ( !rep.exists())
{ // Le répertoire doit être crée
if (!rep.mkdirs() )
{
System.out.println("Le répertoire "+getPathName()+" n'a pas été créé");
return;
}
}
try
{
f = new FileOutputStream(this.getFileName());
params.store(f, "cookie");
}
catch( Exception ex )
{
ex.printStackTrace();
}
}

}

We now have all the steps to transform an applet into an application without changing to much its code.