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 != null) con.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 * "tumr&" im folgenden "Fernsehturm".<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 ((PGConnection) con).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 != null) con.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 != null) con.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
|