/* * Copyright 2013 Robert von Burg <eitch@eitchnet.ch> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package li.strolch.persistence.postgresql; import java.io.IOException; import java.io.InputStream; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLXML; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.sax.SAXResult; import li.strolch.model.Resource; import li.strolch.model.Tags; import li.strolch.model.query.ResourceQuery; import li.strolch.model.xml.ResourceToSaxVisitor; import li.strolch.model.xml.SimpleStrolchElementListener; import li.strolch.model.xml.XmlModelSaxReader; import li.strolch.persistence.api.ResourceDao; import li.strolch.persistence.api.StrolchPersistenceException; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; @SuppressWarnings("nls") public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements ResourceDao { public static final String RESOURCES = "resources"; protected PostgreSqlResourceDao(PostgreSqlStrolchTransaction tx) { super(tx); } @Override protected String getClassName() { return Tags.RESOURCE; } @Override protected String getTableName() { return RESOURCES; } @Override protected Resource parseFromXml(String id, String type, SQLXML sqlxml) { SimpleStrolchElementListener listener = new SimpleStrolchElementListener(); try (InputStream binaryStream = sqlxml.getBinaryStream()) { SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); parser.parse(binaryStream, new XmlModelSaxReader(listener)); } catch (SQLException | IOException | SAXException | ParserConfigurationException e) { throw new StrolchPersistenceException(MessageFormat.format( "Failed to extract Resource from sqlxml value for {0} / {1}", id, type), e); } if (listener.getResources().size() == 0) throw new StrolchPersistenceException(MessageFormat.format( "No Resource parsed from sqlxml value for {0} / {1}", id, type)); if (listener.getResources().size() > 1) throw new StrolchPersistenceException(MessageFormat.format( "Multiple Resources parsed from sqlxml value for {0} / {1}", id, type)); return listener.getResources().get(0); } protected SQLXML createSqlXml(Resource res, PreparedStatement preparedStatement) throws SQLException, SAXException { SQLXML sqlxml = tx().getConnection().createSQLXML(); SAXResult saxResult = sqlxml.setResult(SAXResult.class); ContentHandler contentHandler = saxResult.getHandler(); contentHandler.startDocument(); new ResourceToSaxVisitor(contentHandler).visit(res); contentHandler.endDocument(); return sqlxml; } @Override protected void internalSave(final Resource res) { String sql = "insert into " + getTableName() + " (id, name, type, asxml) values (?, ?, ?, ?)"; try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) { preparedStatement.setString(1, res.getId()); preparedStatement.setString(2, res.getName()); preparedStatement.setString(3, res.getType()); SQLXML sqlxml = createSqlXml(res, preparedStatement); preparedStatement.setSQLXML(4, sqlxml); try { int modCount = preparedStatement.executeUpdate(); if (modCount != 1) { String msg = "Expected to save 1 element with id {0} but SQL statement modified {1} elements!"; msg = MessageFormat.format(msg, res.getId(), modCount); throw new StrolchPersistenceException(msg); } } finally { sqlxml.free(); } } catch (SQLException | SAXException e) { throw new StrolchPersistenceException(MessageFormat.format("Failed to insert Resource {0} due to {1}", res.getLocator(), e.getLocalizedMessage()), e); } } @Override protected void internalUpdate(final Resource resource) { String sql = "update " + getTableName() + " set name = ?, type = ?, asxml = ? where id = ? "; try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) { preparedStatement.setString(1, resource.getName()); preparedStatement.setString(2, resource.getType()); preparedStatement.setString(4, resource.getId()); SQLXML sqlxml = createSqlXml(resource, preparedStatement); preparedStatement.setSQLXML(3, sqlxml); try { int modCount = preparedStatement.executeUpdate(); if (modCount != 1) { String msg = "Expected to update 1 element with id {0} but SQL statement modified {1} elements!"; msg = MessageFormat.format(msg, resource.getId(), modCount); throw new StrolchPersistenceException(msg); } } finally { sqlxml.free(); } } catch (SQLException | SAXException e) { throw new StrolchPersistenceException(MessageFormat.format("Failed to update Resource {0} due to {1}", resource.getLocator(), e.getLocalizedMessage()), e); } } @Override public <U> List<U> doQuery(ResourceQuery<U> query) { PostgreSqlResourceQueryVisitor queryVisitor = new PostgreSqlResourceQueryVisitor("id, asxml"); query.accept(queryVisitor); queryVisitor.validate(); List<U> list = new ArrayList<>(); String sql = queryVisitor.getSql(); try (PreparedStatement ps = tx().getConnection().prepareStatement(sql)) { queryVisitor.setValues(ps); try (ResultSet result = ps.executeQuery()) { while (result.next()) { String id = result.getString("id"); SQLXML sqlxml = result.getSQLXML("asxml"); Resource t = parseFromXml(id, queryVisitor.getType(), sqlxml); list.add(query.getResourceVisitor().visit(t)); } } } catch (SQLException e) { throw new StrolchPersistenceException("Failed to perform query due to: " + e.getMessage(), e); } return list; } }