David Steinsland – informatikkstudent og webutvikler

Innlegg fra kategorien «Guider»

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

Skrive og kompilere LaTeX i Notepad++

| Ingen kommentarer »

notepad_plus_plus-banner

Flere ganger har jeg skrevet om Notepad++ og hvordan du enkelt kan utvide funksjonaliteten til programmet. Forrige gang skrev jeg om hvordan du kunne kompilere og kjøre Java-applikasjoner.
Denne gangen handler det om LaTeX og hvordan du kan skrive LaTeX-dokumenter i Notepad++, samt hvordan du får dem over til PDF- eller PostScript-format.

Før vi går i gang …

For at ting skal gå knirkefritt for deg så bør du helst leste artikkelen jeg skrev om kompilering av Java i Notepad++, spesielt om hvordan du installerer NppExec.
Deretter må du sørge for at du har installert MiKTeX, som er nødvendig for å kunne kompilere LaTeX.

Steg 1: Opprette batch-filer

Innstikket NppExec gir oss muligheten til å kjøre kommandoer direkte i Notepad++, og gjerne med en hurtigtast. Vi skal bruke dette innstikket til å kjøre to forskjellige batch-filer, som skal stå for å kompilere LaTeX til PDF eller PostScript.

Opprett en batch-fil med navnet «latex-pdflatex.bat». I denne filen skriver du inn følgende kode:

cd %1
pdflatex.exe %2

Denne batch-filen har som oppgave å kompilere et TeX-dokument direkte til PDF.

Deretter må du opprette en ny batch-fil med navnet «latex-postscript.bat». Denne filen skal brukes dersom LaTeX-dokumentet inneholder vektorgrafikk, for da må dokumentet gjøres om til PostScript først (PostScript-filer kan enkelt konverteres til PDF senere).

Filen må inneholde følgende kode:

cd %1

latex.exe -src-specials %2.tex
dvips.exe %2

Dette programmet sørger for at LaTeX-dokumentet blir gjort om til en .dvi-fil, for så til PostScript.

Steg 2: Opprette makroer i Notepad++

For å finne ut hvordan du oppretter makroer i Notepad++, anbefaler jeg deg å lese forrige artikkel jeg skrev om emnet. Du følger samme prosedyre som beskrevet der, bare du bytter ut navnet på makroen samt kommandoen. Det er også beskrevet hvordan du kan tilegne tastatursnarveier til makroene.

LaTeX: til PDF

latex-pdflatex.bat "$(CURRENT_DIRECTORY)" $(NAME_PART)"

LaTeX: til PDF (grafikk)

latex-postscript.bat "$(CURRENT_DIRECTORY)" $(NAME_PART)"

Dersom du har fått alt på plass, så er det bare til å begynne å skrive!

Kompilere og kjøre Java i Notepad++

| Ingen kommentarer »

notepad_plus_plus-banner

Da jeg startet på Informasjonsteknologi ved HiOA, ble mange anbefalt å skrive Java i en IDE som Eclipse eller Netbeans fremfor teksteditor. Dette ble nok godtatt av de fleste, men for de av oss som har sverget troskap til Notepad++ er det vanskeligere å godta.

Etter litt søking rundt om, fant jeg frem til en løsning som lar deg kompilere og kjøre Java-kode direkte i fra Notepad++ med et par tastetrykk!

1. Installer NppExec

NppExec er en plugin til Notepad++ som lar deg kjøre kommandoer, og du vil finne denne i Plugin Manager. Gå til: Plugins → Plugin Manager → Show Plugin Manager.

I denne listen vil du finne NppExec. Huk av i avkryssningsboksen og trykk Install.

2. Opprett batch

For å kunne kjøre Java-applikasjoner måtte jeg opprette en egen batch-fil for dette. Lag en fil som heter RunJava.bat, og lim inn dette:


cd %1
java -cp . %2
pause

3. Lage makroer

Makroer  er små miniprogrammer egnet for å utføre små oppgaver, som du kan lage med NppExec.

Trykk F6, lim inn følgende kommando:

D:\bin\JavaRun.bat "$(CURRENT_DIRECTORY)" $(NAME_PART)

Trykk så Save, og kall makroen for Java Run.

For å kompilere Java-kode behøver du ingen batch-filer, bare en spesiell kommando.

Trykk F6, velg <temporary script> fra nedtrekksmenyen, og lim inn kommandoen:

cd $(CURRENT_DIRECTORY)
javac "$(FILE_NAME)"

Lagre den som Java Compile.

4. Plassere makroene i meny

Gå til Plugins → NppExec → Advanced Options

I listen under «Associated Script» velger du Java Run. I boksen inder «Item name» skriver du: Java Run Application. Trykk så på Add/modify.

Gjenta samme prosedyre én gang til, bare denne gangen velger du Java Compile og skriver inn Java Compile Application under «Item name».

Trykk OK, og finn frem Macro i hovedmenyen. Du vil nå se dine makroer i listen nederst.

5. Tilegne tastatursnarveier

Det er greit å kunne ha en tastatursnarvei for disse makroene, spesielt fordi de vil bli benyttet ofte.

I hovedmenyen velg Run → Modify Shortcut/Delete Command. Deretter velger du «Plugin Commands», og finn frem til Java Run Application og Java Compile Application. Dobbeltklikk på hver av dem, og du vil få mulighet om å tilegne makroen en tastatursnarvei.

OBS! Pass på at det ikke oppstår konflikt med andre snarveier. Notepad++ håndterer slikt dårlig, og du vil derfor måtte fjerne en eksisterende snarvei før du kan bruke den på ny.

Gå til

Tips: Enkle feilmeldinger med animasjon

| Ingen kommentarer »

errorbox

Her følger en liten kodesnutt som viser en feilmelding i ca. fem sekunder før den fjernes igjen. I dét feilmeldingen vises, blir bakgrunnsfargen forsterket for å få en «se her!»-opplevelse. Dersom musen blir holdt over feilmeldingen, blir den ikke fjernet før etter musepekeren er fjernet fra den.

For å få koden til å fungere trenger du jQuery og jQuery UI.

JavaScript:

$(function ()
{
	$('div.message').hide().effect ('highlight', {
		color:'#ff8a8a'
	}, 2000).delay(3000).animate({
		height: 'toggle',
		opacity: 'toggle'
	}, 'slow').hover (function ()
	{
		$(this).stop(true, true);
	}, function ()
	{
		$(this).delay(2000).animate({ height: 'toggle', opacity: 'toggle' }, 'slow');
	});
});

Om du studerer JavaScript-koden, vil du se at vi bruker effekten «Highlight». Effekten angir feilmeldingsboksen med angitt bakgrunnsfarge, før den fader tilbake til bakgrunnsfargen som er satt i CSS. Alternativt kan en benytte seg av «pulsate», som også gir en ganske behagelig animasjon:

 $(function() {
    $('div.message').hide().effect('pulsate', {
        times: 2
    }, 450).delay(4000).animate({
        height: 'toggle',
        opacity: 'toggle'
    }, 'slow').hover(function() {
        $(this).stop(true, true);
    }, function() {
        $(this).delay(2000).animate({
            height: 'toggle',
            opacity: 'toggle'
        }, 'slow');
    });
}); 

Pulsate-effekten her veksler mellom å skjule/vise feilmeldingsboksen iløpet av 450 millisekunder.

CSS:

div.message
{
    display:none;
    border:1px solid;
    padding:10px;
    color: #D8000C;
    background-color:#FFBABA;
}

div.message h4
{
    font-weight:bold;
    font-size:14px;
}

div.message p
{
    padding:5px 0;
    font-size:12px;
}

HTML:

<div class="message">
    <h4>An Error has Occured!</h4>
    <p>
        There was a problem while editing the article. Your data has been saved, and you can go <a href="#">back</a> to continue editing.
    </p>
    <p>
        Error ID: #1337
    </p>
</div>

Resultatet:

Klikk på «Result» i vinduet under.

Eget vindu

Daglig backup av MySQL med PHP (Windows)

| Ingen kommentarer »

PHP tips og triks

Dersom du drifter din egen server eller har en utviklingsserver på PC-en din, har du nok vært i situasjoner hvor du tenker: «Hvor er sikkerhetskopien når jeg trenger den?».

Jeg var der selv for omtrent en úke siden, hvor jeg ved en feiltakelse kjørte en gammel PHP-fil som overskrev hele Postnummer-databasen min. Heldigvis hadde jeg mange sikkerhetskopier av databasen, så det gikk bra.

Men hva om jeg ikke hadde hatt kopier? Da hadde faktisk hele arbeidet mitt vært ødelagt! Mangfoldige timer bortkastet, bokstavelig talt.

Hver dag (midnatt) blir livredderen utført: en planlagt oppgave i Windows kjører et PHP-skript som tar backup av MySQL.

I mine siste artikler har jeg skrevet litt om planlagte oppgaver i Windows, og vist flere ting du kan gjøre.

Teori

Når du installerte MySQL, fikk du med et program som heter mysqldump. Programmet brukes til å ta backup av MySQL; du kan selv spesifisere én eller flere databaser, eller om du ønsker å ta backup av alle.

Videre kommer jeg til å ta i bruk  PHP fra kommandolinjen, som muliggjør at vi kan bruke PHP-tolkeren til å kjøre en bestemt PHP-fil. Dette programmet ligger i installasjonsmappen til PHP, og heter php.exe

Jeg refererer til disse programmene som henholdsvis mysqldump og php fra kommandolinjen. Dette er fordi jeg har lagt til PHPs installasjonsmappe og bin-mappen til MySQL til miljøvariabelen PATH. Dette forteller Windows at når du skriver inn et program i CMD, skal den også leite etter det programmet i de plasseringene.

Dersom du ikke har gjort dette, må du spesifisere til programmene med full sti, eks: c:\php\php eller skrive inn følgende i CMD:

SET PATH = %PATH%;C:\PHP;C:\MySQL\bin

(jeg antar PHP og MySQL er installert i C:\).

Ved default ligger «.EXE» i miljøvariabelen PATHEXT, som medfører at du slipper å skrive «.exe» bak programnavnet.

PHP-filen

Gjør klar en PHP-fil som du navngir backup.php og plasser denne utenfor ServerRoot (altså en plass den ikke kan nåes via en nettleser).

ServerRoot hos meg er \www\public_html\, slik at jeg har plassert PHP-filen ett steg opp, i \www\ (Med baklengs-skråstrek foran stien, tolker Windows dette som C:\).

Lag så en mappe du kaller for backup i samme mappe hvor du har plassert PHP-filen. Dette er plassen hvor backupene våre kommer til å ligge.

Det første vi gjør i PHP-filen vår er å definere et par runtime-innstillinger, som databasetilkoblingen, plasseringen til backup-filene og hvordan filnavnet skal utformes.

// MySQL host
define ('HOST', 'localhost');
// MySQL username
define ('USER', 'username');
// MySQL password
define ('PW', 'password');

define ('DUMP_FILENAME', date ('Y-m-d-H-i') . '.sql'); // i.e. 2010-10-30-23-00.sql

define ('SELF_DIR', __DIR__ . DIRECTORY_SEPARATOR);
define ('BACKUP_DIR', 'backup' . DIRECTORY_SEPARATOR);

Backupfilene våre får navnet sitt basert på dagens dato og tidspunktet for når filen blir kjørt.

Neste steg er å kalle opp mysqldump:

passthru (sprintf ('mysqldump -h %s -u %s -p%s -A -r "%s"', HOST, USER, PW, SELF_DIR . BACKUP_DIR . DUMP_FILENAME));

Her trenger du ikke endre noe, bortsett fra stien til mysqldump om du ikke har endret PATH i Windows.

Argumentforklaring:

-h
Spesifiserer host-adressen til MySQL
-u
Brukernavnet til MySQL
-p
Passordet ditt MySQL-brukeren (-p og passordet skal ikke ha mellomrom mellom hverandre).
-A
Spesifiserer at vi skal ta backup av alle databasene. Alternativt –all-databases.
-r
Den fulle stien (plassering + filnavn) til hvor backupen ligger

Det neste jeg har valgt å gjøre, er å komprimere SQL-filen i et Zip-arkiv, som medfører at filstørrelsen blir endel mindre (noe som er bra!).

$Zip = new ZipArchive;
$Zip->open (SELF_DIR . BACKUP_DIR . DUMP_FILENAME . '.zip', ZipArchive::CREATE);
$Zip->setArchiveComment ('This database dump was automatically taken at ' . date ('H:i, d.m.Y') . ' by a Windows Scheduled Task');
$Zip->addFile (DUMP_FILENAME, SELF_DIR . BACKUP_DIR . DUMP_FILENAME);
unlink (SELF_DIR . BACKUP_DIR . DUMP_FILENAME);
$Zip->close();

Den planlagte oppgaven

Det siste steget vårt, er å opprette oppaven som skal kjøre PHP-skriptet. Jeg har valgt å kjøre denne oppgaven hver dag ved midnatt.

Opprett den planlagte oppgaven ved å skrive dette inn i CMD:

SCHTASKS /Create /SC DAILY /MO 1 /ST 00:00 /TN "MySQL Backup" /TR "php \www\backup.php"

Tips

Uten PHP

Dersom du ikke ønsker å komprimere filene med Zip, trenger du heller ikke anvende PHP til denne jobben. Da kjører du rett og slett bare mysqldump direkte i oppgaven:

SCHTASKS /Create /SC DAILY /MO 1 /ST 00:00 /TN "MySQL Backup" /TR "mysqldump -h <host> -u <brukernavn> -p<passord> -A -r \www\backups\database_dump.sql"

Husk at det ikke skal være mellomrom mellom -p og passordet!

Én eller flere databaser istedenfor alle

Erstatt -A i mysqldump-kommandoen med:

--database <database>

eller

--databases <database 1> <database 2> ... <database N>

Garbage Collector

Det tar ikke lange tiden før backup-mappen blir full av filer; og strengt talt så trenger du ikke backups som ble tatt for måneder siden. Derfor kan det være lurt å sette opp en søppelsamler, som sletter gamle filer.
Da oppretter du en planlagt oppgave som kjører for eksempel \www\garbage.php hver uke. Innholdet i PHP-filen er å loope gjennom backup-mappen, og sjekke datoen på når filene sist ble modifisert (opprettet). Slik har jeg gjort det:

// How long we are keeping each file, since its creation date
// (in seconds)
define ('MAX_FILE_AGE', (60 * 60 * 24 * 7)); // 7 days

$data = glob (__DIR__ . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . '*.zip');

foreach ($data as $file)
{
	if (time() - filectime ($file) > MAX_FILE_AGE)
	{
		unlink ($file);
	}
}