StandortPruefer.java
001 /*
002  * Created on 02.03.2004
003  */
004 package de.fub.tip.datenbank.logik;
005 
006 import java.net.MalformedURLException;
007 import java.net.URL;
008 import java.sql.Connection;
009 import java.sql.DriverManager;
010 import java.sql.PreparedStatement;
011 import java.sql.ResultSet;
012 import java.sql.SQLException;
013 import java.sql.Statement;
014 
015 import javax.sql.DataSource;
016 
017 import org.apache.log4j.Logger;
018 import org.postgis.Geometry;
019 import org.postgis.PGgeometry;
020 import org.postgis.Point;
021 import org.postgis.Polygon;
022 import org.postgresql.PGConnection;
023 
024 import de.fub.tip.datenanzeige.Koordinate;
025 import de.fub.tip.datenanzeige.ormapper.LocationVO;
026 import de.fub.tip.datenanzeige.ormapper.PictureVO;
027 import de.fub.tip.datenanzeige.ormapper.UserdataVO;
028 import de.fub.tip.datenbank.DBFunktionen;
029 import de.fub.tip.datenbank.DBKonfig;
030 import de.fub.tip.datenbank.factory.LogicObject;
031 import de.fub.tip.exceptions.AmbiguousLocationException;
032 import de.fub.tip.exceptions.InvalidLocationException;
033 
034 /**
035  * Sinn: während des Anmeldevorgangs am System wird in dieser
036  * Klasse die Benutzereingabe im Hinblick auf die Korrektheit der 
037  * angegebenen Position geprüft. <br>
038  * Dabei existieren einige Routinen, die aus der Datenbank passende 
039  * Informationen auslesen und somit in der Ausgabe für den 
040  * Benutzer sinnvoll ergänzen.
041  <hr>
042  * Damit später auf GPS o.ä. umgestellt werden kann, <br>
043  * hat die Klasse als Parameter Strings, die mit den  
044  <code>checkValidLocation()</code>-
045  * Methode überprüft werden. 
046  <br>
047  * Derzeit wird bei Eingabe von Koordinaten nur geprüft, 
048  * ob der Wert in eine Zahl umwandelbar ist. 
049  * Dadurch sind negative Koordinaten <b>nicht fehlerhaft</b>
050  
051  
052  @author hirsch, 02.03.2004
053  @version 2004-05-03
054  
055  * $Id: StandortPruefer.java,v 1.42 2004/05/13 12:13:39 hirsch Exp $
056  
057  @see de.fub.tip.datenbank.logik.LoginPruefer
058  */
059 public class StandortPruefer implements LogicObject {
060   /** Logger zur Fehlersuche */
061   private static Logger logger = 
062       Logger.getLogger(StandortPruefer.class);
063 
064   /** Datenquelle mit der der <code>StandortPruefer</code> arbeitet */
065   private DataSource datasource = null;
066   /** Ergebnismenge zur Datenabfrage */
067 
068   /**
069    * Standardkonstruktor, der einen <code>StandortPruefer</code> 
070    * erzeugt  
071    @param datasource Datenquelle
072    */
073   public StandortPruefer(DataSource datasource) {
074     this.datasource = datasource;
075     logger.info("StandortPruefer initialized.");
076   // end of Konstruktor
077 
078   /**
079    * Übernimmt als Parameter beispielsweise Eingaben aus einem 
080    * Webformular und prüft diese auf Gültigkeit.<br>
081    * Derzeit erfolgt kein Abgleich mit irgendwelchen Koordinatenlisten -
082    * statt dessen wird nur versucht, den Parameter als numerischen Wert
083    * zu parsen. Schlägt dies fehl, wird eine Ausnahme gemeldet.
084    *  
085    @param koordinateX x-Koordinate
086    @param koordinateY y-Koordinate
087    
088    @return Eingaben als SightVO geparst und 
089    * - wenn vorhanden - mit einem Namen versehen
090    
091    @throws InvalidLocationException wenn Ort ungültig 
092    */
093   public LocationVO checkValidLocation(
094           String koordinateX, String koordinateY
095     throws InvalidLocationException {
096 
097       String sql = "SELECT DISTINCT s.si_id, s.name , p.* " +
098         "FROM sight s , picture p " 
099         "WHERE s.location && GeometryFromText(?,-1) " 
100         "AND Distance(GeometryFromText(?,-1), s.location) < ? " +
101         "AND s.picture_url = p.id ";
102     
103       LocationVO erg = new LocationVO();
104       PictureVO pic = new PictureVO();
105       Connection con = null;
106       
107       try {
108         Koordinate koordinate = new Koordinate(
109           Double.parseDouble(koordinateX),  
110           Double.parseDouble(koordinateY) );
111         erg.setLocation(koordinate);
112         
113         // Wenn in der Datenbank nichts gefunden wird oder ein Fehler
114         // auftritt hat der aktuelle Standort immer diesen Dummywert!
115         erg.setName("unknown")
116         
117         // in Datenbank nach Bezeichnung für den Standort suchen
118           try {
119             con = this.datasource.getConnection();
120             PreparedStatement prepStat = con.prepareStatement(sql);
121 
122             // Parameter basteln
123             String box = DBFunktionen.getBox(koordinate.getXLinks(),
124               koordinate.getYLinks(), koordinate.getXRechts(),
125               koordinate.getYRechts())
126             String punkt = DBFunktionen.getPoint(koordinate.getX()
127               koordinate.getY());
128 
129             // Parameter setzen
130             prepStat = con.prepareStatement(sql);
131             prepStat.setString(1, box);
132             prepStat.setString(2, punkt);
133             prepStat.setDouble(3, Koordinate.getDistance());
134   
135             boolean abfrageErfolgreich = prepStat.execute();  
136             ResultSet rs = prepStat.getResultSet();
137             
138             if (abfrageErfolgreich && rs != null && rs.next()) {
139               erg.setName(rs.getString("name"));
140               logger.debug("Name gefunden: " + erg.getName());
141 
142               // Bildinformationen zusammensuchen
143               pic.setHeight(rs.getString("height"));
144               pic.setHeightThumb(rs.getString("heightthumb"));
145               pic.setId(new Integer(rs.getInt("id")));
146               pic.setUrl(new URL(rs.getString("url")));
147               pic.setUrlThumb(new URL(rs.getString("urlthumb")));
148               pic.setWidth(rs.getString("width"));
149               pic.setWidthThumb(rs.getString("widththumb"));
150               
151               erg.setPicture(pic);
152             // end if
153           catch (SQLException e1) {
154             logger.debug("StandortPruefer.checkLocation(koord): " +
155               "Ausnahme bei Suche nach Standort-Bezeichnung,  "
156               e1 + ", "+ e1.getLocalizedMessage());
157           // end of try        
158         
159       catch(Exception e) {
160         logger.error("StandortPruefer.checkValidUser" +
161           "( "+ koordinateX + " / "+ koordinateY +")- Ausnahme: "+e);
162         throw  new InvalidLocationException(
163           "StandortPruefer.checkValidLocation+"+e);
164       // end of catch
165   
166       // Datenbankverbindung schliessen
167       finally {
168         try {
169           if(con != nullcon.close();
170           logger.debug("StandortPruefer.checkLocation(koord): " +
171             "DB-Con geschlossen.");
172               
173         catch (SQLException e1) {
174           logger.debug("StandortPruefer.checkLocation(koord): " + e1);
175           logger.debug("StandortPruefer.checkLocation(koord): " +
176             "Fehler beim Schliessen der DB-Con: " 
177             e1.getLocalizedMessage());
178         // end of catch
179       // end of finally
180             
181     erg.logObject(logger);
182     return erg;
183   // end of checkValidLocation
184 
185 
186   /**
187    * Diese Methode prüft, ob sich unter dem angegebenen Namen 
188    * eine Sehenswürdigkeit finden lässt und bestimmt deren Koordinaten.
189    <br>
190    * Des weiteren wird bei positivem Fund in der Datenbank der Name
191    * des aktuellen Standorts auf den Namen gesetzt, der sich in der
192    * Datenbank zu diesen Koordinaten befindet - 
193    * somit wird beispielsweise aus der Eingabe
194    * &quot;tumr&" im folgenden &quot;Fernsehturm&quot;.<br>
195    <hr>
196    * Derzeit lassen sich nur POSTGIS-Objekte der Typen:
197    <ol>
198    <li><code>POINT</code></li>
199    <li><code>POLYGON</code></li>
200    </ol>
201    * behandeln - bei allen anderen wird eine Ausnahme ausgelöst.
202    
203    @param name des aktuellen Standorts
204    
205    @return Eingaben als LocationVO geparst
206    
207    @throws InvalidLocationException 
208    *   wenn Ort unbekannt oder ungültig
209    
210    @throws AmbiguousLocationException 
211    *   wenn Ort nicht eindeutig ist und mehrere Treffer in der DB da sind
212    */
213   public LocationVO checkValidLocation(String name
214     throws InvalidLocationException, AmbiguousLocationException {
215     
216       LocationVO erg = new LocationVO();
217 
218       String sql = "SELECT s.si_id, s.name, s.location, " +
219         "GeometryType(s.location), p.*  " +
220         "FROM sight s,  picture p " +
221         "WHERE lower(name) like lower(?) AND " +
222         "s.picture_url = p.id ";
223       
224       Connection con = null;
225       try {
226         // per Hand die DB-Verbindung am Pool vorbei öffnen 
227         try {
228            Class.forName("org.postgresql.Driver");
229         catch (ClassNotFoundException cnfe) {
230             logger.error("StandortPruefer: schwere SCHEISSE - " +
231              "wenn Postgresql nicht gefunden, geht Anwendung nicht! " +
232                 cnfe);
233             throw new SQLException("PostgresqlTreiber nicht " +
234              "im CLASSPATH gefunden! NSP")
235         // end of try
236     
237         // so waere es, wenn die PoolVerbindung ginge:
238         //con = this.dataSource.getConnection();
239         con = DriverManager.getConnection(
240                 DBKonfig.url , DBKonfig.user, DBKonfig.pw)
241         logger.debug("StandortPruefer hat " +
242            "sich eine DB-Conn am Pool vorbei geholt!!!!");
243   
244         // Hinzufügen der PostGIS-Datentypen 
245         ((PGConnectioncon).addDataType("geometry",
246           "org.postgis.PGgeometry");
247 
248         PreparedStatement prepStat = con.prepareStatement(sql);
249         prepStat.setString(1, DBFunktionen.getSQLLike(name));
250 
251         ResultSet rs = prepStat.executeQuery();
252         
253         // Auslesen der Koordinaten zum übergebenen Namen
254         if(rs != null && rs.next()) {
255           // Der Name des Platzes ist aus der Tabellen sight,
256           // dadurch wird die Eingabe "turm" beispielsweise zu 
257             // "Fernsehturm"
258           erg.setName(rs.getString("name"));
259 
260           // Bildinformationen zusammensuchen
261           PictureVO pic = new PictureVO();
262           pic.setHeight(rs.getString("height"));
263           pic.setHeightThumb(rs.getString("heightthumb"));
264           pic.setId(new Integer(rs.getInt("id")));
265           pic.setUrl(new URL(rs.getString("url")));
266           pic.setUrlThumb(new URL(rs.getString("urlthumb")));
267           pic.setWidth(rs.getString("width"));
268           pic.setWidthThumb(rs.getString("widththumb"));
269           
270           erg.setPicture(pic);        
271           
272           // geographische Daten auslesen und einzeln behandeln
273           PGgeometry geometry = (PGgeometry
274             rs.getObject("location");
275           
276           if geometry.getGeoType() == Geometry.POINT) {
277             Koordinate koordinate = 
278                 new Koordinate(new Point(geometry.getValue()));
279             erg.setLocation(koordinate);
280             
281           else if geometry.getGeoType() == Geometry.POLYGON) {
282             /* 
283              * Laut POSTGIS-Referenz gibt es keine Funktion, die einen 
284              * Punkt aus einem mehrdimensionalen Objekt zurückgibt.
285              * - X(location) ergibt NULL für nichtPUNKT-Objekte
286              * - X(force_2d(location)) das gleiche
287              * <br>
288              * Da bisher *nur* POINT und POLYGON in der DB sind, 
289              * muss man ebend von Hand ran! 
290              
291              */
292             Polygon polygon = new Polygon(geometry.getValue());
293             erg.setLocation(Koordinate.convertType(polygon));
294             
295           else {
296             throw new InvalidLocationException("StandortPruefer." +
297               "falschen Typ aus DB gelesen");
298           // end if Polygon
299   
300           
301           // wenn jetzt noch Tupel da sind, ist die Ortsangabe mehrdeutig
302           if rs.next()) {
303             throw new AmbiguousLocationException("StandortPruefer." +
304               "checkLocation(name) - mehrdeutige Angabe");
305           // end if
306 
307         else {
308           // aus der  DB konnte nicht gelesen werden
309           throw new InvalidLocationException("StandortPruefer." +
310             "checkLoc(name): " +
311             "kein Standort in DB mit diesem Namen gefunden");
312         // end if
313 
314       catch (SQLException e) {
315         logger.error("StandortPruefer.checkLocation(name): " +
316           "DBFehler - " 
317           e + ", " + e.getLocalizedMessage());
318   
319       catch (MalformedURLException e) {
320         logger.error("StandortPruefer.checkLocation(name): " +
321           "Fehler beim Auslesen des Bildes - " 
322           e + ", " + e.getLocalizedMessage());
323       // end of try
324 
325         // Datenbankverbindung schliessen
326         finally {
327           try {
328             if(con != nullcon.close();
329             logger.debug("StandortPruefer.checkLocation(name): " +
330               "DB-Con geschlossen.");
331               
332           catch (SQLException e1) {
333             logger.debug("StandortPruefer.checkLocation(name): " + e1);
334             logger.debug("StandortPruefer.checkLocation(name): " +
335               "Fehler beim Schliessen der DB-Con: " 
336               e1.getLocalizedMessage());
337           // end of catch
338         // end of finally
339 
340     erg.logObject(logger);
341     return erg;
342   // end of checkLocation
343   
344   /**
345    * protokolliert in der Datenbank, wer wo war
346    
347    @param loc aktueller Aufenthaltsort
348    @param user angemeldeter Benutzer
349    */
350   public void protokolliereBesuch(LocationVO loc, UserdataVO user) {
351     
352     logger.info("StandortPruefer.protokolliereBesuch() startet ...");
353 
354     // Auf Grund der Tabellenstruktur wird etwas kompliziert eingefügt ....    
355     String sql1 = "INSERT INTO events (time) VALUES(now())";
356     String sql1a = "SELECT currval('events_eid_seq')";
357     // siehe Postgresql-Buch S.274 zu Sequenzen und AutoWerten
358     String sql2 = "INSERT INTO locationevent (leid, udid, location) ";
359     Connection con = null;
360     Statement stat = null;
361 
362     try {
363       con = this.datasource.getConnection();
364       con.setAutoCommit(true);
365       stat = con.createStatement();
366       
367       logger.debug("Transaktion anfangen ...");
368       stat.execute("BEGIN WORK");
369 
370       // fügt erst in events ein, 
371       // damit man den schlüssel daraus verwenden kann
372       stat.executeUpdate(sql1);
373 
374       // im zweiten Anlauf wird der benutzte Schlüssel ausgelesen
375       stat = con.createStatement();
376       ResultSet rs1 = stat.executeQuery(sql1a);
377       
378       if(rs1!=null && rs1.next()) {
379         logger.debug("Select currval = "+rs1.getString(1));
380         sql2+= "VALUES ( " + rs1.getString(1",";
381         sql2+=" ? , GeometryFromText( ?, -1 ))";
382       // end if
383       
384       // jetzt wird der Wert in locationevent eingefügt
385       PreparedStatement prepStat = con.prepareStatement(sql2);
386       prepStat.setInt(1, user.getId().intValue());
387       prepStat.setString(2, DBFunktionen.getPoint(loc.getLocation()));
388       logger.debug("Standort: "+
389           DBFunktionen.getPoint(loc.getLocation()));
390       int erg = prepStat.executeUpdate();
391       logger.debug("prepStatement.execute = "+erg + 
392         " Datensätze betroffen");
393 
394       // Transaktion beenden
395       logger.debug("COMMIT machen ...");
396       stat.execute("COMMIT");
397 
398     catch (SQLException e) {
399       logger.error("StandortPruefer.protokolliereBesuch(): Fehler - "+e);
400       logger.error(e.getLocalizedMessage());
401       
402       try {
403         logger.error("StandortPruefer.protokolliereBesuch(): " +
404           "ROLLBACK gemacht!");
405         stat.execute("ROLLBACK");
406       catch (SQLException e1) {
407         logger.error("StandortPruefer.protokolliereBesuch()-" +
408           "SQLException: " +
409           "verschachtelte Ausnahme beim ROLLBACK aufgetreten!");
410       // end of try
411 
412     catch(Exception e) {
413       // Damit auf keinen Fall auf den Webseiten Fehler angezeigt werden,
414       // die durch das Logging verursacht wurden ....
415       logger.error("StandortPruefer.protokolliereBesuch(): Problem - "+e);
416       logger.error(e.getLocalizedMessage());
417 
418       try {
419         logger.error("StandortPruefer.protokolliereBesuch(): " +
420             "ROLLBACK gemacht!");
421         stat.execute("ROLLBACK");
422       catch (SQLException e1) {
423         logger.error("StandortPruefer.protokolliereBesuch()-Exception: " +
424           "verschachtelte Ausnahme beim ROLLBACK aufgetreten!");
425       // end of try
426 
427     // end of try
428 
429     // Datenbankverbindung immer korrekt schliessen
430     finally {
431       try {
432         if(con != nullcon.close();
433         logger.debug("StandortPruefer.protokolliereBesuch(): " +
434           "DB-Con geschlossen.");
435       catch (SQLException e1) {
436         logger.error("StandortPruefer.protokolliereBesuch(): " + e1+
437           "Fehler beim Schliessen der DB-Con: " 
438           e1.getLocalizedMessage());
439       // end of catch
440     // end of finally
441 
442     logger.info("StandortPruefer.protokolliereBesuch() " +
443         "erfolgreich beendet.");
444   // end of protokolliereBesuch
445 // end of class