KANDIDATNUMMER: EKSAMEN EMNENAVN: Programutvikling EMNENUMMER: IMT3281 EKSAMENSDATO:05/12-2007 KLASSE: 06HBINDA/06HBINDP TID: 09.00 13.00 EMNEANSVARLIG: Øivind Kolloen ANTALL SIDER UTLEVERT: 7 TILLATTE HJELPEMIDLER: Alle trykte og skrevne INNFØRING MED PENN, evt. trykkblyant som gir gjennomslag Ved innlevering skilles hvit og gul besvarelse og legges i hvert sitt omslag. Oppgavetekst, kladd og blå kopi beholder kandidaten. Husk kandidatnummer på alle ark.
Les hele oppgavesettet før du begynner. Etter høstens store problemer med billettbestilling via Billettservice har de valgt å utvikle et helt nytt system for salg av billetter via web. Det skal utvikles en klient- serverløsning i Java for å løse problemene. Klienten vil kjøres via Java webstart men dette er noe du ikke trenger å ta hensyn til i forbindelse med denne oppgaven. For kommunikasjon mellom klient og server kan du også gå ut i fra at begge kjøres på samme maskin i testperioden (dvs at du kan bruke localhost som server når du setter opp nettverksforbindelsen.) Databasen som ligger i bunn av systemet finner du i vedlegg 3. Informasjon om brukeren slik som navn, adresse etc vedlikeholdes via et webgrensesnitt, du skal altså ikke gjøre noe i forhold til dette. Oppgave 1 (teller 20%) Det skal først utvikles et GUI for klienten, her får du god hjelp ved at deler av GUI'en allerede finnes. Se vedlegg 1 hvor du finner kode og skjermdump for den delen av GUI'en som skal inneholde betalingsinformasjon. Ved å benytte klassen fra vedlegg 1 og egen kode skal du nå lage et program som ser ut som i illustrasjon 1. Legg merke til at drop down boksen er tom, dette er fordi vi foreløpig ikke har noen data om arrangementer å fylle denne med. Den delen av skjermbildet som omhandler betalingsinformasjon skal kunne vises eller skjules avhengig av om billetter for arrangementet som vises er lagt ut for salg enda eller ikke. Illustrasjon 1: Skjermdump av ikke initialisert grensesnitt Løsningsforslag package no.hig.okolloen.billettservice; import javax.swing.*; import java.awt.*; import java.net.*; import java.io.*; import java.awt.event.*;
public class BillettService extends JFrame { PaymentDetails pd = new PaymentDetails (); JComboBox arrangement = new JComboBox (new DefaultComboBoxModel()); JTextArea beskrivelse = new JTextArea (); public BillettService () { super ("Billettservice"); JPanel p = new JPanel (); p.setlayout (new FlowLayout (FlowLayout.LEFT)); p.add (new JLabel ("Velg arrangement")); p.add (arrangement); add (p, BorderLayout.NORTH); JPanel p1 = new JPanel (); p1.setlayout (new FlowLayout (FlowLayout.LEFT)); p1.add (new JLabel ("Beskrivelse av arrangementet")); JPanel p2 = new JPanel (); p2.setlayout (new BorderLayout ()); p2.add (p1, BorderLayout.NORTH); JScrollPane sp = new JScrollPane (beskrivelse); sp.setpreferredsize (new Dimension (400, 200)); p2.add (sp); add (p2, BorderLayout.CENTER); add (pd, BorderLayout.SOUTH); pack (); // *1 setvisible (true); // *2 public static void main (String args[]) { BillettService bs = new BillettService ();
Oppgave 2 (teller 30%) I denne oppgaven skal du lage første del av serverapplikasjonen. Denne delen skal lytte på port 8082 og når den mottar en forbindelse på denne porten skal den hente alle arrangementer fra databasen som enda ikke har vært gjennomført. For å koble til databasen kan du benytte klassen i vedlegg 1 (DBEngine). Du må altså gjøre en spørring mot tabellen «arrangementer» og hente alle rader hvor arrangementsdatoen ligger frem i tid. Dette skal så sendes til den klienten som er tilkoblet på port 8082, du bestemmer selv hvordan du gjøre dette men klassen i vedlegg 4 (ArrangementsInfo) kan benyttes. Variabelen isalg benyttes for å indikere hvorvidt billetter til dette arrangementet er i salg. Billetter er kun til salgs mellom datoen/klokkeslettet som ligger i salgs_dato og det tidspunktet som arrangementet faktisk finner sted. Løsningsforslag package no.hig.okolloen.billettservice; import java.sql.*; import java.net.*; import java.io.*; public class BillettServiceServer { DBEngine db = new DBEngine (); boolean stopper = false; public BillettServiceServer () { startarrangementoversikt (); public void startarrangementoversikt () { Thread t = new Thread () { public void run() { try { ServerSocket ss = new ServerSocket (8082); while (!stopper) { Socket s = ss.accept (); ObjectOutputStream oos = new ObjectOutputStream (s.getoutputstream()); Statement stmt = db.con.createstatement (); ResultSet rs = stmt.executequery ( "SELECT * FROM arrangementer WHERE arr_dato>now()"); java.util.vector v = new java.util.vector (); while (rs.next()) { String n = rs.getstring ("navn"); String b = rs.getstring ("beskrivelse"); int id = rs.getint ("id"); java.util.date d = new java.util.date(); ArrangementsInfo ai = null; if (rs.getdate("salgs_dato").gettime()<d.gettime()) ai = new ArrangementsInfo (n, b, id, true); else ai = new ArrangementsInfo (n, b, id, false); v.add (ai); oos.writeobject (v); s.close (); catch (Exception e) {
e.printstacktrace (); ; t.start (); public static void main (String args[]) { BillettServiceServer bss = new BillettServiceServer (); Oppgave 3, teller 30% Du skal nå gjøre ferdig første del av klienten, dvs at du må skrive kode som kontakter serveren fra oppgave 2 på port 8082 og henter oversikt over alle arrangementer og fyller inn drop down boksen. Du skal også skrive kode for en lytterutine slik at når et arrangement velges i drop down boksen så vises informasjonen om dette arrangementet i tekstområdet. Videre skal betalingsinformasjon vises dersom det er billetter for salg og denne informasjonen skal skjules dersom billetter ikke er til salgs. Se Illustrasjon 2 og 3 for hvordan dette kan se ut. Illustrasjon 2: Arrangementet som er Illustrasjon 3: Arrangementet som er valgt er enda ikke i salg valgt er i salg Vis tydelig hvor du setter inn denne koden i koden fra oppgave 1 (sett *1, *2 osv i oppgave 1 og referer til disse i denne oppgaven.) Løsningsforslag *1) arrangement.addactionlistener (new VelgArrangement()); hentarrangementer (); *2) class VelgArrangement implements ActionListener { public void actionperformed (ActionEvent ae) { if (arrangement.getselectedindex()==-1)
return; ArrangementsInfo ai = (ArrangementsInfo)arrangement.getSelectedItem(); if (ai.getisalg ()) else pd.setvisible (true); pd.setvisible (false); beskrivelse.settext (ai.getbeskrivelse()); public void hentarrangementer () { try { Socket s = new Socket ("localhost", 8082); ObjectInputStream ois = new ObjectInputStream (s.getinputstream()); java.util.vector v = (java.util.vector)ois.readobject(); DefaultComboBoxModel model = (DefaultComboBoxModel)arrangement.getModel(); model.removeallelements(); for (int i=0; i<v.size(); i++) model.addelement ((ArrangementsInfo)v.elementAt(i)); s.close (); catch (Exception e) { e.printstacktrace (); Oppgave 4, teller 20% Anta at du har en egen klasse som du kan benytte for å sjekke gyldighet og reservere penger på kredittkortet. Beskriv de problemer du mener eksisterer i forhold til det å ta i mot bestillinger, reservere billetter, sjekke kredittkort og effektuere bestillingen. Du bør her ta hensyn til at det er MANGE samtidige brukere, noen har ikke dekning på kortet, andre har skrevet feil i kortnummer eller annen informasjon. Med bare de databasetabellene som er gitt i vedlegg 3, beskriv med ord hvordan du vil legge opp koden for å holde databasen konsistent til enhver tid men samtidig optimalisere ytelsen til systemet (kredittkortsjekken tar noe tid, så en må anta at flere kredittkortsjekker kjører parallelt.) Løsningsforslag Billetter må reserveres før en gjør kredittkortsjekken. Dersom det da er noe feil med kredittkortet, om det er feil informasjon eller ikke dekning på konto så må disse billettene føres tilbake til oversikten over ledige billetter.
Ved billettreservering kan en for eksempel låse databasen og så lese ut antall ledige billetter. Dersom det er nok billetter ledig så fjerner vi så mange som denne personen ønsker å kjøpe før databasen låses opp igjen slik at andre prosesser slipper til. Disse billettene holder vi så «på hånden» inntil kredittsjekken er gjennomført. Dersom kredittsjekken er OK så anses billettene som kjøpt mens dersom kredittsjekken feiler så låses igjen databasen før billettene tilbakeføres inn i mengden med tilgjengelige billetter. Kunden informeres deretter om det som har skjedd. Det er viktig å sørge for at databasen låses før vi sjekker antall billetter siden mangel på slik lås kan medføre at vi selger flere billetter enn hva som er tilgjengelig. Lykke til og takk til de av dere som ga ideen til oppgaven :-)
Vedlegg 1, DBEngine.java package no.hig.okolloen.billettservice; import java.sql.*; public class DBEngine { Connection con = null; public DBEngine () { try { Class.forName("com.mysql.jdbc.Driver").newInstance(); catch (ClassNotFoundException cnfe) { System.out.println ("Kunne ikke finne databasedriveren"); cnfe.printstacktrace (); System.exit (0); catch (InstantiationException ie) { System.out.println ("Kunne ikke opprette databasedriveren"); ie.printstacktrace (); System.exit (0); catch (Exception e) { System.out.println ("Feil ved initialisering av databasedriveren"); e.printstacktrace (); System.exit (0); try { con = DriverManager.getConnection("jdbc:mysql://localhost/billettservice", "billettservice", "passord"); catch (SQLException ex) { System.out.println("SQLException: " + ex.getmessage()); System.out.println("SQLState: " + ex.getsqlstate()); System.out.println("VendorError: " + ex.geterrorcode()); System.exit (0);
Vedlegg 2, PaymentDetails.java package no.hig.okolloen.billettservice; import javax.swing.*; import java.awt.*; import sun.layout.*; public class PaymentDetails extends JPanel { JTextField brukernavn = new JTextField(20); JTextField kortnummer = new JTextField(20); JTextField utlopsdato = new JTextField(10); JTextField sikkerhetskode = new JTextField(10); JTextField antallbilletter = new JTextField(10); JButton ok = new JButton ("OK"); JButton cancel = new JButton ("Cancel"); public PaymentDetails () { JPanel p = new JPanel (); p.setlayout (new SpringLayout ()); JLabel l = new JLabel ("Brukernavn"); p.add (l); l.setlabelfor (brukernavn); brukernavn.settooltiptext ("Ditt brukernavn (E-post adresse)"); p.add (brukernavn); l = new JLabel ("Antall billetter"); p.add (l); l.setlabelfor (antallbilletter); p.add (antallbilletter); l = new JLabel ("Kortnummer"); p.add (l); l.setlabelfor (kortnummer); p.add (kortnummer); l = new JLabel ("Utløpsdato"); p.add (l); l.setlabelfor (utlopsdato); p.add (utlopsdato); l = new JLabel ("Sikkerhetskode"); p.add (l); l.setlabelfor (sikkerhetskode); p.add (sikkerhetskode); SpringUtilities.makeCompactGrid(p, 5, 2, setlayout (new BorderLayout ()); add (p, BorderLayout.CENTER); JPanel p1 = new JPanel (); p1.setlayout (new GridLayout (1,2)); p1.add (ok); p1.add (cancel); JPanel p2 = new JPanel (); p2.add (p1); add (p2, BorderLayout.SOUTH); public static void main (String args[]) { PaymentDetails pd = new PaymentDetails (); JFrame f = new JFrame (); f.add (pd); f.pack (); f.setvisible (true); //rows, cols 6, 6, //initx, inity 6, 6); //xpad, ypad Illustrasjon 4: Skjermdump for "java PaymentDetails"
Vedlegg 3, SQL script for å sette opp databasen Database: `billettservice` Tabellstruktur for tabell `arrangementer` CREATE TABLE `arrangementer` ( `id` int(11) NOT NULL auto_increment, `arr_dato` datetime NOT NULL, `salgs_dato` datetime NOT NULL, `pris` float NOT NULL, `ant_billetter` int(11) NOT NULL, `solgte_billetter` int(11) NOT NULL, `navn` varchar(128) NOT NULL, `beskrivelse` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ; Tabellstruktur for tabell `billetter` CREATE TABLE `billetter` ( `id` int(11) NOT NULL auto_increment, `arr_id` int(11) NOT NULL, `bruker_id` int(11) NOT NULL, `ant_billetter` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; Tabellstruktur for tabell `brukere` CREATE TABLE `brukere` ( `id` int(11) NOT NULL auto_increment, `fornavn` varchar(128) NOT NULL, `etternavn` varchar(128) NOT NULL, `epost` varchar(128) NOT NULL, `adresse` varchar(128) NOT NULL, `adresse1` varchar(128) NOT NULL, `postnr` char(4) NOT NULL, `poststed` varchar(128) NOT NULL, `telefon` varchar(32) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Vedlegg 4, ArrangementsInfo.java package no.hig.okolloen.billettservice; public class ArrangementsInfo implements java.io.serializable { private String navn; private String beskrivelse; private int id; private boolean isalg; public ArrangementsInfo (String navn, String beskrivelse, int id, boolean isalg) { this.navn = navn; this.beskrivelse = beskrivelse; this.id = id; this.isalg = isalg; public String tostring () { return navn; public String getnavn () { return navn; public String getbeskrivelse () { return beskrivelse; public int getid () { return id; public boolean getisalg () { return isalg;