/*
* Copyright 2015 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.Tags;
import li.strolch.model.activity.Activity;
import li.strolch.model.query.ActivityQuery;
import li.strolch.model.xml.ActivityToSaxVisitor;
import li.strolch.model.xml.SimpleStrolchElementListener;
import li.strolch.model.xml.XmlModelSaxReader;
import li.strolch.persistence.api.ActivityDao;
import li.strolch.persistence.api.StrolchPersistenceException;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
@SuppressWarnings("nls")
public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements ActivityDao {
public static final String ACTIVITIES = "activities";
protected PostgreSqlActivityDao(PostgreSqlStrolchTransaction tx) {
super(tx);
}
@Override
protected String getClassName() {
return Tags.ACTIVITY;
}
@Override
protected String getTableName() {
return ACTIVITIES;
}
@Override
protected Activity 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 Activity from sqlxml value for {0} / {1}", id, type), e);
}
if (listener.getActivities().size() == 0)
throw new StrolchPersistenceException(MessageFormat.format(
"No Activity parsed from sqlxml value for {0} / {1}", id, type));
if (listener.getActivities().size() > 1)
throw new StrolchPersistenceException(MessageFormat.format(
"Multiple Activities parsed from sqlxml value for {0} / {1}", id, type));
return listener.getActivities().get(0);
}
protected SQLXML createSqlXml(Activity activity, PreparedStatement preparedStatement) throws SQLException,
SAXException {
SQLXML sqlxml = tx().getConnection().createSQLXML();
SAXResult saxResult = sqlxml.setResult(SAXResult.class);
ContentHandler contentHandler = saxResult.getHandler();
contentHandler.startDocument();
new ActivityToSaxVisitor(contentHandler).visit(activity);
contentHandler.endDocument();
return sqlxml;
}
@Override
protected void internalSave(final Activity activity) {
String sql = "insert into " + getTableName() + " (id, name, type, asxml) values (?, ?, ?, ?)";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
preparedStatement.setString(1, activity.getId());
preparedStatement.setString(2, activity.getName());
preparedStatement.setString(3, activity.getType());
SQLXML sqlxml = createSqlXml(activity, 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, activity.getId(), modCount);
throw new StrolchPersistenceException(msg);
}
} finally {
sqlxml.free();
}
} catch (SQLException | SAXException e) {
throw new StrolchPersistenceException(MessageFormat.format("Failed to insert Activity {0} due to {1}",
activity.getLocator(), e.getLocalizedMessage()), e);
}
}
@Override
protected void internalUpdate(final Activity activity) {
String sql = "update " + getTableName() + " set name = ?, type = ?, asxml = ? where id = ? ";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
preparedStatement.setString(1, activity.getName());
preparedStatement.setString(2, activity.getType());
preparedStatement.setString(4, activity.getId());
SQLXML sqlxml = createSqlXml(activity, 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, activity.getId(), modCount);
throw new StrolchPersistenceException(msg);
}
} finally {
sqlxml.free();
}
} catch (SQLException | SAXException e) {
throw new StrolchPersistenceException(MessageFormat.format("Failed to update Activity {0} due to {1}",
activity.getLocator(), e.getLocalizedMessage()), e);
}
}
@Override
public <U> List<U> doQuery(ActivityQuery<U> query) {
PostgreSqlActivityQueryVisitor queryVisitor = new PostgreSqlActivityQueryVisitor("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");
Activity t = parseFromXml(id, queryVisitor.getType(), sqlxml);
list.add(query.getActivityVisitor().visit(t));
}
}
} catch (SQLException e) {
throw new StrolchPersistenceException("Failed to perform query due to: " + e.getMessage(), e);
}
return list;
}
}