David Steinsland – informatikkstudent og webutvikler

Innlegg merket med «cache»

Lagre og hente frem objekter i Java (cache)

| Ingen kommentarer »

Javaprat

Noe ganske essensielt med et program er egenskapen å huske innstillinger til neste oppstart. Men hvordan skal vi få til dette? Skal vi bruke en database eller XML? Eller finnes det andre metoder?

I Java har vi en interface som heter «Serializable». Klasse som implementerer den, har muligheten til å bli lagret på disk til senere bruk ved bruk av ObjectOutputStream og ObjectInputStream.

Helt enkelt, kan vi gjøre slik:

MyObject myObj = new MyObject (); // MyObject implementerer Serializable

try
{
    FileOutputStream fos = new FileOutputStream ("myObject.ser");
    ObjectOutputStream oos = new ObjectOutputStream(fos);

    oos.writeObject(myObj);

    oos.flush();
    oos.close();
}
catch (Exception e)
{
    e.printStackTrace();
}

Objektet vårt er nå lagret i filen myObject.ser, og vi kan hente det frem igjen slik:

try
{
    FileInputStream fis = new FileInputStream("myObject.ser");
    ObjectInputStream ois = new ObjectInputStream(fis);

    // myObj er nå av samme instans som i eksempelet ovenfor
    MyObject myObj = (MyObject) ois.readObject();

    ois.close();
}
catch (Exception e)
{
    e.printStackTrace();
}

Som du kanskje ser, så er det ikke så veldig komplisert. Men hva dersom applikasjonen inneholder mange flere objekter? Å lagre alle objektene manuelt er både tidkrevende og unødvendig.

Vi kan nemlig anvende oss av programmeringsparadigmet Registry. Essensielt er det en klasse som inneholder alle andre objekter som brukes i en applikasjon. Registeret anvender også gjerne Singleton-mønsteret slik at objektene kan nås overalt.

Når vi har et register trenger vi bare lagre register-objektet. Alle objekter som måtte være i registeret vil da også bli lagret.

Et eksempel på et slik register:

class Registry implements Serializable
{
	private static final long serialVersionUID = 7422025143560909873L;
	private static final Registry instance = new Registry();

	private HashMap registry = new HashMap();

	private Registry ()
	{ }

	public static Registry getInstance ()
	{
		return instance;
	}

	public void addObject (String k, Object v)
	{
		this.registry.put (k, v);
	}

	public Object getObject (String k)
	{
		return this.registry.get (k);
	}
}

Du har kanskje lagt merke til datafeltet serialVersionUID, og lurer på hva det er?

serialVersionUID er en universell versjonsindikator for en klasse som støtter serial-handling. Deserialiseringa bruker dette ID-nummeret for å sikre at klassen er i samsvar med det serialiserte objektet.

I praksis betyr dette at Java bruker serienummeret til å sjekke om det objektet du henter frem fra filen er i samsvar med det objektet du la inn. Det høres kanskje litt diffus ut fortsatt, men ta følgende scenario:

- Du oppretter et objekt av typen MyObject (som i kode #1), og lagrer dette til en fil
- I mellomtiden endrer du på klassestrukturen til MyObject, og legger til flere datafelt
- Når du henter frem objektet igjen, etter klasseendringene, hva tror du skjer?

Java kjenner nemlig ikke igjen klassene, og tror de er forskjellige. Du vil derfor få en feilmelding om at det er en ugyldig klasse. Dersom du hadde implementert serialVersionUID, hadde Java forstått at objektene likevel var like.

Verdien er unik per klasse, og blir generert av Java selv. For å finne ut hvilket nummer du skal legge inn i hver klasse, må du ta ibruk serialver-programmet (følger med Java-installasjonen).

Du åpner opp kommandolinjen og endrer mappe til hvor klassefilene dine ligger. Deretter utfører du kommandoen:

serialver Registry

Bytt ut Registry med hva klassenavnet er i ditt tilfelle.

Et eksempel på bruk

class Program
{
    public static void main (String[] args)
    {
        Registry reg = Registry.getInstance();

        reg.addObject ("myObj", new MyObject ("Something"));
        reg.addObject ("foo", new MyObject ("bar"));

        // lagre objektene ved avslutning
        try
        {
            FileOutputStream fos = new FileOutputStream ("registry.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            oos.writeObject(reg);

            oos.flush();
            oos.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        // ... så kan vi hente dem frem igjen
        try
        {
            FileInputStream fis = new FileInputStream("registry.ser");
            ObjectInputStream ois = new ObjectInputStream(fis);

            reg = (Registry) ois.readObject();
            ois.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        // do something..
        MyObject myObj = (MyObject) reg.getObjet ("myObj");
    }
}

Oppsummering

  • Alle klasser som skal lagres implementere Serializable, og bør inneholdet  serialVersionUID
  • Et felles register med alle objekter vil spare deg for tid og ressurser, for eksempel ved lagring av objekter

Cache PHP-sider: kort og enkelt

| Ingen kommentarer »

PHP tips og triks

Når man skriver nettsider med PHP er det mye som kan være med på å dra opp lastetiden. For ikke å snakke om alle CSS- og JavaScript-filer som også må lastes ned. Hva kan man så gjøre?

Om du jobber med databaser kan du for eksempel skru på MySQL Query Cacher, samtidig som du kan mellomlagre resultatet i HTML-, JSON eller XML-format. Men alt dette krever igrunn litt arbeid, samt at noen koder her og der må endres.

$lastModified = filemtime (__FILE__);
$etagFile = md5_file (__FILE__);
$ifModifiedSince = isset ($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : FALSE;
$etagHeader = isset ($_SERVER['HTTP_IF_NONE_MATCH'] ? trim ($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;

header("Last-Modified: " . gmdate("D, d M Y H:i:s", $lastModified) . " GMT");
header("Etag: $etagFile");
header('Cache-Control: public');

//check if page has changed. If not, send 304 and exit
if (@strtotime ($ifModifiedSince) == $lastModified || $etagHeader == $etagFile)
{
       header ("HTTP/1.1 304 Not Modified");
       exit;
}

//your normal code below

Om du limer inn koden ovenfor i starten på de PHP-filene du ønsker å mellomlagre, merker du forskjellen med én gang. Jeg har selv testet koden i flere prosjekter, og den fungerer utmerket. Det som er verdt å merke seg, er at den ikke fanger opp endringer i dynamisk innhold med én gang.
Den merker så klart endringer på seg selv, men dersom du henter innhold fra en database så kan det ta noen minutter før det vises.

Denne prosessen kan, så vidt jeg vet, ikke fremskyndes siden mellomlageret ligger i nettleseren, og det er ikke mulig å fjerne det (dette må sluttbruker gjøre manuelt).