![]() |
|
|
Home> HowTos> Java-Howtos> Tomcat 5.5 DBCP - Postgres |
|
Tomcat 5.5 DBCP - PostgreSQLUm BLOBS (Bilder, Dokumente, etc) aus einer Postgres-Datenbank zu lesen, braucht man den LargeObjectManager. Für den braucht man aber wieder die org.postgresql.PGConnection; bei Zugriff über einen Connection Pool kann das zu Problemen führen. |
|
|
Zugriff auf die Blobs über die LargeObjectAPI
VorraussetzungenWir wollen eine Webseite bauen, auf der Benutzer Bilder hoch laden können. Da wir aber nicht wildfremden Leuten erlauben wollen, Daten in mein Dateisystem zu schreiben, muss eine Möglichkeit her, die Bilder anders ab zu legen.Da die Seite aus einer Postgres-Datenbank gespeist wird, ist es naheliegend, die Daten in BLOBS (Binary Large OBjectS) in der Datenbank ab zu legen. Die Datenbank dazu ist schon da, und ein ConnectionPool ist eingerichtet: server.xml:
(...)
<Context path="/webApp"
docBase="/srv/www/tomcat/base/apps/webapp.war"
debug="0" privileged="false" reload="true">
<Resource name="jdbc/postgresDB" auth="Container" type="javax.sql.DataSource"
maxActive="20" maxIdle="10" maxWait="10000"
username="MyUsername" password="######" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost:5432/myDatabase"
removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"/>
</Context>
(...)
nach oben Zugriff auf die BLOBS der DatenbankFix in die Postgres-Doku geguckt, und eine eigene Klasse geschrieben, die auf die Connection aus dem ConnectionPool zugreift.MyClass.java:
(...)
public void updateImage(byte[] imageData) throws IOException, SQLException, Exception{
DataSource ds = (DataSource)new InitialContext().lookup("java:comp/env/jdbc/postgresDB");
Connection con = ds.getConnection();
boolean oldAutoCommit = con.getAutoCommit();
con.setAutoCommit(false);
ByteArrayInputStream inStream = new ByteArrayInputStream(imageData);
LargeObjectManager lobj = ((org.postgresql.PGConnection)con).getLargeObjectAPI();
//Das Bild selbst erzeugen
int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE);
LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE);
byte buf[] = new byte[2048];
int s, tl = 0;
while ((s = inStream.read(buf, 0, 2048)) > 0)
{
obj.write(buf, 0, s);
tl += s;
}
obj.close();
PreparedStatement ps = con.prepareStatement("UPDATE images SET img_data = ? WHERE img_id = ?");
ps.setInt(1,oid);
ps.setBigDecimal(2,getImageId());
ps.executeUpdate();
ps.close();
pcon.commit();
pcon.setAutoCommit(oldAutoCommit);
con.close();
}
(...)
Leider nein - die Zeile LargeObjectManager lobj = ((org.postgresql.PGConnection)con).getLargeObjectAPI(); Um auf die innere Connection zugreifen zu können, müssen wir das in der server.xml explizit erlauben, per Default ist der Zugriff auf die Basis-Connection verboten. nach oben Zugriff auf die Connection erlauben:Damit auf die inneren Connections zugegriffen werden kann, muss das im Deployment-Descriptor erlaubt werden.Das passiert über den Parameter accessToUnderlyingConnectionAllowed, der muss auf "true" gesetzt werden. server.xml:
(...)
<Context path="/webApp"
docBase="/srv/www/tomcat/base/apps/webapp.war"
debug="0" privileged="false" reload="true">
<Resource name="jdbc/postgresDB" auth="Container" type="javax.sql.DataSource"
maxActive="20" maxIdle="10" maxWait="10000"
username="MyUsername" password="######" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost:5432/myDatabase"
removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"
accessToUnderlyingConnectionAllowed="true"/>
</Context>
(...)
nach oben Cast der ConnectionUm an die Postgres-Connection zu kommen, brauchen wird die innerste Connection.Um an diese heran zu kommen, müssen wir die Connection, die wir aus dem ConnectionPool haben, auf eine org.apache.tomcat.dbcp.dbcp.DelegatingConnection casten. Diese bietet uns die Methode getInnermostDelegate() an, mit der wir die Connection bekommen, deren Typ wir auch bei der Definition des ConnectionPools in der server.xml angegeben haben. Die erforderlichen Klassen zum Kompilieren finden sich in der Bibliothek naming-factory-dbcp.jar im Verzeichnis common/lib/ des Tomcat; sie gehören zum Jakarta Commons - Projekt. Also wird die Bibliothek in den Classpath aufgenommen, und die Klasse folgendermaßen modifiziert: MyClass.java:
(...)
private Connection getPostgresConnection(Connection con) throws Exception {
pgCon = ((org.apache.tomcat.dbcp.dbcp.DelegatingConnection)con).getInnermostDelegate();
return pgCon;
}
public void updateImage(byte[] imageData) throws IOException, SQLException, Exception{
DataSource ds = (DataSource)new InitialContext().lookup("java:comp/env/jdbc/postgresDB");
Connection con = ds.getConnection();
Connection pgCon = getPostgresConnection(con);
boolean oldAutoCommit = pgCon.getAutoCommit();
pgCon.setAutoCommit(false);
ByteArrayInputStream inStream = new ByteArrayInputStream(imageData);
LargeObjectManager lobj = ((org.postgresql.PGConnection)pgCon).getLargeObjectAPI();
//Das Bild selbst erzeugen
int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE);
LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE);
byte buf[] = new byte[2048];
int s, tl = 0;
while ((s = inStream.read(buf, 0, 2048)) > 0)
{
obj.write(buf, 0, s);
tl += s;
}
obj.close();
PreparedStatement ps = con.prepareStatement("UPDATE images SET img_data = ? WHERE img_id = ?");
ps.setInt(1,oid);
ps.setBigDecimal(2,getImageId());
ps.executeUpdate();
ps.close();
pgCon.commit();
pgCon.setAutoCommit(oldAutoCommit);
con.close();
//Ganz wichtig: NICHT die pgCon schliessen, sonst wird sie nicht in den Pool
//zurück gegeben, sondern echt geschlossen!
}
(...)
nach oben Anpassungen für den Tomcat 5.0Wir wollen die Klasse auch auf dem Tomcat 5.0 benutzen.Bei der Ausführung wirft aber die Zeile pgCon = ((org.apache.tomcat.dbcp.dbcp.DelegatingConnection)con).getInnermostDelegate(); Der Grund: Der Tomcat 5.0 arbeitet mit einer anderen Version der Commons-Klassen; Die Packages heißen anders. Und bleibt nichts anderes übrig, als die Klassen vorher mit instanceof ab zu prüfen. Die Klassen für die DelegatingConnection des Tomcat 5.0 finden sich in der Datei commons-dbcp-1.2.1 vom Jakarta Commons Projekt. MyClass.java:
private Connection getPostgresConnection(Connection con) throws Exception {
Connection pgCon = null;
if(con instanceof org.apache.commons.dbcp.DelegatingConnection)
pgCon = ((org.apache.commons.dbcp.DelegatingConnection)con).getInnermostDelegate();
else
pgCon = ((org.apache.tomcat.dbcp.dbcp.DelegatingConnection)con).getInnermostDelegate();
return pgCon;
}
nach oben |
|