/**
* Copyright (c) 2009 Juwi MacMillan Group GmbH
*
* 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 de.juwimm.cms.search.xmldb;
import java.io.BufferedReader;
import java.io.IOException;
import java.security.MessageDigest;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import de.juwimm.cms.beans.foreign.TizzitPropertiesBeanSpring;
import de.juwimm.cms.search.vo.XmlSearchValue;
/**
* Abstract class providing common functionality for all concrete XmlDb-Implementations
* @author <a href="mailto:carsten.schalm@juwimm.com">Carsten Schalm</a>
* company Juwi|MacMillan Group Gmbh, Walsrode, Germany
* @version $Id$
*/
public abstract class AbstractXmlDbImpl implements XmlDb {
private static Logger log = Logger.getLogger(AbstractXmlDbImpl.class);
private static String xmldbDatasource = null;
private Connection xmldbConnection = null;
private TizzitPropertiesBeanSpring tizzitPropertiesBeanSpring;
protected static final String DELETE_STATEMENT = "DELETE FROM XML_SEARCH_DB WHERE SITE_ID=? AND VIEW_COMPONENT_ID=?";
protected static final String SELECT_COUNT_STATEMENT = "SELECT COUNT(SITE_ID) FROM XML_SEARCH_DB WHERE SITE_ID=? AND VIEW_COMPONENT_ID=?";
protected static final String SELECT_COUNT_HASHCODE_STATEMENT = "SELECT COUNT(SITE_ID) FROM XML_SEARCH_DB WHERE SITE_ID=? AND VIEW_COMPONENT_ID=? AND HASHCODE=?";
abstract protected String getInsertStatementSql();
abstract protected PreparedStatement getSearchXmlStatement(Integer siteId, String xpathQuery) throws SQLException;
abstract protected PreparedStatement getSearchXmlByUnitStatement(Integer unitId, Integer viewDocumentId, String xpathQuery) throws SQLException;
public TizzitPropertiesBeanSpring getTizzitPropertiesBeanSpring() {
return tizzitPropertiesBeanSpring;
}
@Autowired
public void setTizzitPropertiesBeanSpring(TizzitPropertiesBeanSpring tizzitPropertiesBeanSpring) {
this.tizzitPropertiesBeanSpring = tizzitPropertiesBeanSpring;
}
protected Connection getConnection() {
if (log.isTraceEnabled()) log.trace("getConnection() -> begin");
if (this.xmldbConnection == null) {
try {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(this.getXmldbDatasource());
this.xmldbConnection = ds.getConnection();
} catch (NamingException ex) {
log.error("getConnection() -> lookup for " + this.getXmldbDatasource() + " failed: ", ex);
} catch (SQLException ex) {
log.error("getConnection() -> getConnection() for " + this.getXmldbDatasource() + " failed: ", ex);
}
}
if (log.isTraceEnabled()) log.trace("getConnection() -> end");
return this.xmldbConnection;
}
protected void releaseConnection() {
if (log.isTraceEnabled()) log.trace("releaseConnection() -> begin");
if (this.xmldbConnection != null) {
try {
this.xmldbConnection.close();
} catch (SQLException e) {
log.warn("releaseConnection() -> error releasing db-connection (already closed?)\n" + e.getMessage());
} finally {
this.xmldbConnection = null;
}
}
if (log.isTraceEnabled()) log.trace("releaseConnection() -> end");
}
private String getXmldbDatasource() {
return getTizzitPropertiesBeanSpring().getSearch().getXmlDatasource();
}
protected String getHashCode(String value) {
if (log.isTraceEnabled()) log.trace("getHashCode(...) -> begin");
String retVal = null;
try {
MessageDigest mdAlgorithm = MessageDigest.getInstance("MD5");
mdAlgorithm.update(value.getBytes());
byte[] digest = mdAlgorithm.digest();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < digest.length; i++) {
sb.append(this.toHexString(digest[i]));
}
retVal = sb.toString();
} catch (Exception e) {
log.error("getHashCode(...) -> error occured generating hashcode ", e);
}
if (log.isTraceEnabled()) log.trace("getHashCode(...) -> end");
return retVal;
}
private String toHexString(byte b) {
String retVal;
int value = (b & 0x7F) + (b < 0 ? 128 : 0);
retVal = (value < 16 ? "0" : "");
retVal += Integer.toHexString(value).toUpperCase();
return retVal;
}
/**
* @toDo TODO: Remove this method!
* @deprecated will be removed soon!
*/
@Deprecated
protected String clobToString(Clob clob) {
if (log.isTraceEnabled()) log.trace("clobToString(...) -> begin");
String retVal = "";
if (clob != null) {
StringBuffer sb = new StringBuffer();
String tmp;
try {
BufferedReader br = new BufferedReader(clob.getCharacterStream());
while ((tmp = br.readLine()) != null)
sb.append(tmp);
} catch (IOException ioe) {
log.error("clobToString(...) -> error creating BufferedReader");
} catch (SQLException sqle) {
log.error("clobToString(...) -> error getting Clob.getCharacterStream()");
}
retVal = sb.toString();
}
if (log.isTraceEnabled()) log.trace("clobToString(...) -> end");
return retVal;
}
/**
* Executing the search and building the XmlSearchValue[]
* @param pstmt
* @return
* @throws SQLException
*/
protected synchronized XmlSearchValue[] search(PreparedStatement pstmt) throws SQLException {
LinkedList<XmlSearchValue> tmpList = new LinkedList<XmlSearchValue>();
ResultSet qResult = pstmt.executeQuery();
while (qResult.next()) {
XmlSearchValue newEntry = new XmlSearchValue();
newEntry.setViewComponentId(qResult.getInt(1));
newEntry.setUnitId(qResult.getInt(2));
newEntry.setInfoText(qResult.getString(3));
newEntry.setText(qResult.getString(4));
newEntry.setContent(qResult.getString(5));
tmpList.add(newEntry);
}
return tmpList.toArray(new XmlSearchValue[0]);
}
/**
*
* @param siteId
* @param viewComponentId
* @param contentText
* @param metaAttributes
* @return
*/
protected synchronized boolean doInsert(Integer siteId, Integer viewComponentId, String contentText, Map<String, String> metaAttributes, String hashcode) {
if (log.isTraceEnabled()) log.trace("doInsert(...) -> begin");
boolean retVal = false;
PreparedStatement pstmt = null;
Connection connection = null;
String infoText = null;
String text = null;
int unitId = 0;
try {
if (contentText == null || contentText.length() == 0) {
// do not insert anything if content is empty
return true;
}
if (log.isTraceEnabled()) log.trace("doInsert(...) -> before parsing metaAttributes");
if (!("".equals(metaAttributes.get("unitId")))) {
unitId = Integer.parseInt(metaAttributes.get("unitId"));
}
infoText = metaAttributes.get("infoText");
text = metaAttributes.get("text");
if (log.isTraceEnabled()) log.trace("doInsert(...) -> after parsing metaAttributes");
connection = this.getConnection();
pstmt = connection.prepareStatement(this.getInsertStatementSql());
pstmt.setInt(1, siteId.intValue());
pstmt.setInt(2, viewComponentId.intValue());
pstmt.setString(3, contentText);
pstmt.setInt(4, unitId);
pstmt.setString(5, infoText);
pstmt.setString(6, text);
pstmt.setString(7, hashcode);
retVal = pstmt.execute();
if (log.isTraceEnabled()) log.trace("doInsert(...) -> insert query executed");
} catch (SQLException sqle) {
log.error("doInsert(...) -> failed to excecute insert query for viewComponentId = " + viewComponentId, sqle);
if (log.isTraceEnabled()) log.trace("Content to insert: " + contentText);
} catch (Exception e) {
log.error("doInsert(...) -> unkown exception occured " + e.getMessage(), e);
} finally {
try {
if (pstmt != null) pstmt.close();
} catch (SQLException e) {
log.error("doInsert(...) -> error closing pstmt " + e.getMessage(), e);
}
this.releaseConnection();
}
if (log.isTraceEnabled()) log.trace("doInsert(...) -> end");
return retVal;
}
public synchronized void deleteXml(Integer siteId, Integer viewComponentId) {
if (log.isTraceEnabled()) log.trace("deleteXML(...) -> begin");
PreparedStatement pstmt = null;
try {
pstmt = this.getConnection().prepareStatement(AbstractXmlDbImpl.DELETE_STATEMENT);
pstmt.setInt(1, siteId.intValue());
pstmt.setInt(2, viewComponentId.intValue());
pstmt.executeUpdate();
if (log.isTraceEnabled()) log.trace("deleteXML(...) -> delete query executed");
} catch (SQLException sqle) {
log.error("deleteXML(...) -> failed to excecute delete query " + sqle.getMessage(), sqle);
} catch (Exception e) {
log.error("deleteXML(...) -> unkown exception occured " + e.getMessage(), e);
} finally {
try {
if (pstmt != null) pstmt.close();
} catch (SQLException e) {
log.error("deleteXML(...) -> error closing pstmt " + e.getMessage(), e);
}
this.releaseConnection();
}
if (log.isTraceEnabled()) log.trace("deleteXML(...) -> end");
}
public synchronized boolean saveXml(Integer siteId, Integer viewComponentId, String contentText, Map<String, String> metaAttributes) {
if (log.isDebugEnabled()) log.debug("saveXml(...) -> begin");
boolean retVal = false;
Iterator<String> it = metaAttributes.keySet().iterator();
String hashbase = "";
while (it.hasNext()) {
String key = it.next();
hashbase += key + "=" + metaAttributes.get(key) + "\n";
}
hashbase += contentText;
String hashcode = this.getHashCode(hashbase);
PreparedStatement pstmt = null;
PreparedStatement hashPstmt = null;
try {
pstmt = this.getConnection().prepareStatement(AbstractXmlDbImpl.SELECT_COUNT_STATEMENT);
pstmt.setInt(1, siteId);
pstmt.setInt(2, viewComponentId);
ResultSet qResult = pstmt.executeQuery();
qResult.next();
if (qResult.getInt(1) == 0) {
retVal = this.doInsert(siteId, viewComponentId, contentText, metaAttributes, hashcode);
} else {
hashPstmt = this.getConnection().prepareStatement(AbstractXmlDbImpl.SELECT_COUNT_HASHCODE_STATEMENT);
hashPstmt.setInt(1, siteId);
hashPstmt.setInt(2, viewComponentId);
hashPstmt.setString(3, hashcode);
ResultSet hashResult = hashPstmt.executeQuery();
hashResult.next();
if (hashResult.getInt(1) == 0) {
this.deleteXml(siteId, viewComponentId);
retVal = this.doInsert(siteId, viewComponentId, contentText, metaAttributes, hashcode);
} else {
retVal = true;
if (log.isDebugEnabled()) log.debug("saveXml(...) -> nothing to do, hashcodes are equal (" + hashcode + ")");
}
hashResult.close();
}
qResult.close();
} catch (SQLException e) {
log.error("saveXml(...) -> error getting row count ", e);
} finally {
try {
if (hashPstmt != null) hashPstmt.close();
if (pstmt != null) pstmt.close();
} catch (SQLException e) {
log.error("saveXml(...) -> error closing pstmt ", e);
}
this.releaseConnection();
}
if (log.isDebugEnabled()) log.debug("saveXml(...) -> end");
return retVal;
}
public synchronized XmlSearchValue[] searchXml(Integer siteId, String xpathQuery) {
if (log.isDebugEnabled()) log.debug("searchXML(...) -> begin at " + sdf.format(new java.util.Date()));
XmlSearchValue[] retArray = null;
PreparedStatement pstmt = null;
try {
pstmt = this.getSearchXmlStatement(siteId, xpathQuery);
retArray = this.search(pstmt);
} catch (SQLException sqle) {
log.error("searchXML(...) -> failed to excecute query for xpathQuery = \"" + xpathQuery + "\": ", sqle);
} catch (Exception e) {
log.error("searchXML(...) -> unkown exception occured ", e);
} finally {
try {
if (pstmt != null) pstmt.close();
} catch (SQLException e) {
log.error("searchXML(...) -> error closing pstmt ", e);
}
this.releaseConnection();
}
if (log.isDebugEnabled()) log.debug("searchXML(...) -> end at " + sdf.format(new java.util.Date()));
return retArray;
}
/** @see de.juwimm.cms.search.xmldb.XmlDb#searchXmlByUnit(java.lang.Integer, java.lang.String) */
public synchronized XmlSearchValue[] searchXmlByUnit(Integer unitId, Integer viewDocumentId, String xpathQuery) {
if (log.isDebugEnabled()) log.debug("searchXmlByUnit(...) -> begin at " + sdf.format(new java.util.Date()));
XmlSearchValue[] retArray = null;
PreparedStatement pstmt = null;
try {
pstmt = this.getSearchXmlByUnitStatement(unitId, viewDocumentId, xpathQuery);
retArray = this.search(pstmt);
} catch (SQLException sqle) {
log.error("searchXmlByUnit(...) -> failed to excecute query for xpathQuery = \"" + xpathQuery + "\": " + sqle.getMessage(), sqle);
} catch (Exception e) {
log.error("searchXmlByUnit(...) -> unknown exception occured " + e.getMessage(), e);
} finally {
try {
if (pstmt != null) pstmt.close();
} catch (SQLException e) {
log.error("searchXmlByUnit(...) -> error closing pstmt " + e.getMessage(), e);
}
this.releaseConnection();
}
if (log.isDebugEnabled()) log.debug("searchXmlByUnit(...) -> end at " + sdf.format(new java.util.Date()));
return retArray;
}
}