/* Copyright 2013 Pascal Christoph, hbz.
* Licensed under the Eclipse Public License 1.0 */
package org.lobid.lodmill;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.NoSuchElementException;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.riot.RDFLanguages;
import org.culturegraph.mf.framework.DefaultStreamReceiver;
import org.culturegraph.mf.framework.ObjectReceiver;
import org.culturegraph.mf.framework.annotations.Description;
import org.culturegraph.mf.framework.annotations.In;
import org.culturegraph.mf.framework.annotations.Out;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hp.hpl.jena.rdf.model.LiteralRequiredException;
import com.hp.hpl.jena.rdf.model.Model;
/**
* A sink, writing triples into MySQL DBMS. The primary keys are constructed
* from the literals of an property residing in the RDF model.
*
* @author Pascal Christoph
*/
@Description("Writes the object value of an RDF model into MySQL. Default serialization is 'NTRIPLES'. The name of the entry is "
+ "constructed from the literal of an given property (recommended properties are identifier).\n"
+ " Mandatory variable are:\n" + "- username\n" + "- password\n"
+ "- dbname\n" + "- tablename\n" + "- columnId\n" + "- columnData\n" + "\n"
+ " Optional variables are:\n"
+ "- 'property' (a property in the RDF model. The object value of this property"
+ " will be the DB's entry name.) \n"
+ "- 'serialization (e.g. one of 'NTRIPLES', 'TURTLE', 'RDFXML','RDFJSON'\n"
+ "- dbProtocolAndAdress\n")
@In(Model.class)
@Out(Void.class)
public final class RdfModelMysqlWriter extends DefaultStreamReceiver
implements RecordIdentifier, RDFSink, ObjectReceiver<Model> {
private static final Logger LOG =
LoggerFactory.getLogger(RdfModelMysqlWriter.class);
private Lang serialization;
private String nameProperty;
Connection conn = null;
private Statement stmt = null;
private PreparedStatement ps;
private String tablename;
private final String columnId = "identifier";
private final String columnData = "data";
private String dbname;
private String username;
private String password;
private String dbProtocolAndAdress;
/**
* Default constructor
*
*/
public RdfModelMysqlWriter() {
setProperty("http://purl.org/dc/terms/identifier");
setSerialization("NTRIPLES");
}
private void init() {
if (this.dbProtocolAndAdress != null && this.username != null
&& this.password != null && this.tablename != null
&& this.dbname != null) {
connectMysqlDB();
try {
// the "REPLACE" is no standard ANSI SQL, only works with MySQL
this.ps = conn.prepareStatement("REPLACE INTO " + this.tablename + "("
+ this.columnId + "," + this.columnData + ") VALUES (?,?)");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* Sets the protocoll and adress of the DBMS, e. g. "jdbc:mysql://localhost/"
*
* @param dbProtocolAndAdress the protocol and adress of the DBMS
*/
public void setDbProtocolAndAdress(final String dbProtocolAndAdress) {
this.dbProtocolAndAdress = dbProtocolAndAdress;
init();
}
/**
* Sets the username of the DBMS.
*
* @param username the name of the user
*/
public void setUsername(final String username) {
this.username = username;
init();
}
/**
* Sets the password of the username of the DBMS.
*
* @param password the password of the user of the DBMS
*/
public void setPassword(final String password) {
this.password = password;
init();
}
/**
* Sets the serialization format. Default is NTriples.
*
* @param serialization the serialization of the triples
*/
@Override
public void setSerialization(final String serialization) {
this.serialization = RDFLanguages.nameToLang(serialization);
}
@Override
public void setProperty(String nameProperty) {
this.nameProperty = nameProperty;
}
/**
* Sets the name of the database of the DBMS.
*
* @param dbname the name of the database
*/
public void setDbname(final String dbname) {
this.dbname = dbname;
init();
}
/**
* Sets the name of the table of the database of the DBMS
*
* @param tablename the name of the table
*/
public void setTablename(final String tablename) {
this.tablename = tablename;
init();
}
@Override
public void process(final Model model) {
String identifier = null;
try {
identifier =
model.listObjectsOfProperty(model.createProperty(nameProperty)).next()
.asLiteral().toString();
LOG.debug("Going to store identifier=" + identifier);
} catch (NoSuchElementException e) {
LOG.warn(
"No identifier => cannot derive a filename for " + model.toString());
return;
} catch (LiteralRequiredException e) {
LOG.info("Identifier is a URI. Derive filename from that URI ... "
+ model.toString(), e);
identifier =
model.listObjectsOfProperty(model.createProperty(nameProperty)).next()
.toString();
}
if (identifier != null) {
final StringWriter tripleWriter = new StringWriter();
RDFDataMgr.write(tripleWriter, model, this.serialization);
String triples = tripleWriter.getBuffer().toString();
if (triples != null && triples.length() > 1) {
try {
this.ps.setString(1, identifier);
this.ps.setString(2, triples);
this.ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
this.ps.toString();
}
}
}
}
private void connectMysqlDB() {
try {
conn = DriverManager.getConnection(this.dbProtocolAndAdress,
this.username, this.password);
stmt = conn.createStatement();
stmt.executeUpdate("CREATE DATABASE IF NOT EXISTS " + this.dbname);
conn = DriverManager.getConnection(this.dbProtocolAndAdress + this.dbname
+ "?" + "user=" + this.username + "&password=" + this.password);
stmt = conn.createStatement();
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " + this.tablename + " ( "
+ this.columnId + " VARCHAR(20), PRIMARY KEY (" + this.columnId + "),"
+ this.columnData + " MEDIUMTEXT) ENGINE = MyISAM");
if (stmt != null) {
try {
stmt.close();
} catch (SQLException sqlEx) {
LOG.error("Closing SQL statement Exception:" + sqlEx.getMessage());
}
stmt = null;
}
} catch (SQLException ex) {
LOG.error("SQLException: " + ex.getMessage());
LOG.error("SQLState: " + ex.getSQLState());
LOG.error("VendorError: " + ex.getErrorCode());
}
}
}