/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/chat/trunk/chat-impl/impl/src/java/org/sakaiproject/chat2/model/impl/ChatDataMigration.java $ * $Id: ChatDataMigration.java 105079 2012-02-24 23:08:11Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2007, 2008 The Sakai Foundation * * Licensed under the Educational Community 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.opensource.org/licenses/ECL-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 org.sakaiproject.chat2.model.impl; import java.sql.Clob; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.chat2.model.ChatManager; import org.sakaiproject.db.api.SqlService; import org.sakaiproject.util.ResourceLoader; import org.sakaiproject.util.Xml; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * @author chrismaurer * */ public class ChatDataMigration { protected final transient Log logger = LogFactory.getLog(getClass()); private boolean debug = false; private SqlService sqlService = null; private ChatManager chatManager = null; private Statement stmt; private ResourceLoader toolBundle; private boolean performChatMigration = false; private boolean chatMigrationExecuteImmediate = true; /** init thread - so we don't wait in the actual init() call */ public class ChatDataMigrationThread extends Thread { /** * construct and start the init activity */ public ChatDataMigrationThread() { start(); } /** * run the init */ public void run() { try { load(); } catch (Exception e) { logger.warn("Error with ChatDataMigrationThread.run()", e); } } } /** * Called on after the startup of the singleton. This sets the global * list of functions which will have permission managed by sakai * @throws Exception */ protected void init() throws Exception { logger.info("init()"); try { if (performChatMigration) { new ChatDataMigrationThread(); //load(); } } catch (Exception e) { logger.warn("Error with ChatDataMigration.init()", e); } } /** * Destroy */ public void destroy() { logger.info("destroy()"); } public void load() throws Exception { logger.info("Running Chat Migration; immediate: " + chatMigrationExecuteImmediate); Connection connection = null; try { connection = sqlService.borrowConnection(); printDebug("*******BORROWED A CONNECTION"); runChannelMigration(connection); runMessageMigration(connection); } catch (Exception e) { printDebug(e.toString()); logger.error(e.getMessage()); throw new Exception(e); } finally { if (connection != null) { try { sqlService.returnConnection(connection); } catch (Exception e) { // can't do anything with this. } } } logger.info("Chat migration complete"); } protected void runChannelMigration(Connection con) { logger.debug("runChannelMigration()"); printDebug("*******GETTING CHANNELS"); String sql = getMessageFromBundle("select.oldchannels"); int oldChannelsFound = 0; int newChannelsWritten = 0; try { stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(sql); try { while (rs.next()) { oldChannelsFound++; if ((oldChannelsFound % 5000) == 0) { logger.info("Processed channels: "+oldChannelsFound); } /* * CHANNEL_ID * XML */ String oldId = rs.getString("CHANNEL_ID"); Object xml = rs.getObject("XML"); printDebug("*******FOUND CHANNEL: " + oldId); printDebug("*******FOUND CHANNEL: " + xml); Document doc = null; try { doc = Xml.readDocumentFromString((String)xml); } catch (ClassCastException cce) { Clob xmlClob = (Clob) xml; doc = Xml.readDocumentFromStream(xmlClob.getAsciiStream()); } if (doc == null) { logger.error("error converting chat channel. skipping CHANNEL_ID: ["+oldId+"] XML: ["+xml+"]"); continue; } // verify the root element Element root = doc.getDocumentElement(); String context = root.getAttribute("context"); String title = root.getAttribute("id"); String newChannelId = escapeSpecialCharsForId(oldId); //TODO Chat lookup the config params? newChannelsWritten++; String runSql = getMessageFromBundle("insert.channel"); Object[] fields = new Object[] {newChannelId, context, null, title, "", "SelectMessagesByTime", 3, 0, 1, oldId, oldId}; /* * CHANNEL_ID, * CONTEXT, * CREATION_DATE, * title, * description, * filterType, * filterParam, * placementDefaultChannel, * migratedChannelId, * ENABLE_USER_OVERRIDE */ if (chatMigrationExecuteImmediate) { sqlService.dbWrite(null, runSql, fields); } } } finally { rs.close(); } } catch (Exception e) { logger.error("error selecting data with this sql: " + sql); logger.error("", e); } finally { try { stmt.close(); } catch (Exception e) { logger.error("Unexpected error in chat channel conversion:"+e); } } logger.debug("Migration task fininshed: runChannelMigration()"); logger.info("chat channel conversion done. Old channels found: " +oldChannelsFound+" New channels written: "+newChannelsWritten); } protected void runMessageMigration(Connection con) { logger.debug("runMessageMigration()"); printDebug("*******GETTING MESSAGES"); String sql = getMessageFromBundle("select.oldmessages"); int oldMessagesFound = 0; int newMessagesWritten = 0; try { stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(sql); try { while (rs.next()) { oldMessagesFound++; if ((oldMessagesFound % 5000) == 0) { logger.info("Processed messages: "+oldMessagesFound); } /* * MESSAGE_ID * CHANNEL_ID * XML * OWNER * MESSAGE_DATE */ String oldMessageId = rs.getString("MESSAGE_ID"); String oldChannelId = rs.getString("CHANNEL_ID"); Object xml = rs.getObject("XML"); String owner = rs.getString("OWNER"); Date messageDate = rs.getTimestamp("MESSAGE_DATE"); printDebug("*******FOUND MESSAGE: " + oldMessageId); printDebug("*******FOUND MESSAGE: " + xml); Document doc = null; try { doc = Xml.readDocumentFromString((String)xml); } catch (ClassCastException cce) { Clob xmlClob = (Clob) xml; doc = Xml.readDocumentFromStream(xmlClob.getAsciiStream()); } if (doc == null) { logger.error("error converting chat message. " +"skipping CHANNEL_ID: ["+oldChannelId+"] MESSAGE_ID: ["+oldMessageId+"]" + " xml: "+xml); continue; } // verify the root element Element root = doc.getDocumentElement(); String body = Xml.decodeAttribute(root, "body"); String newMessageId = oldMessageId; String runSql = getMessageFromBundle("insert.message"); newMessagesWritten++; Object[] fields = new Object[] { escapeSpecialCharsForId(newMessageId), escapeSpecialCharsForId(oldChannelId), owner, messageDate, body, oldMessageId}; /* * insert into CHAT2_MESSAGE (MESSAGE_ID, CHANNEL_ID, OWNER, MESSAGE_DATE, BODY) \ values ('{0}', '{1}', '{2}', '{3}', '{4}'); */ if (chatMigrationExecuteImmediate) { getChatManager().migrateMessage(runSql, fields); } } } finally { rs.close(); } } catch (Exception e) { logger.error("error selecting data with this sql: " + sql); logger.error("", e); } finally { try { stmt.close(); } catch (Exception e) { logger.error("Unexpected error in chat message conversion:"+e); } } logger.debug("Migration task fininshed: runMessageMigration()"); logger.info("chat message conversion done. Old messages found: " +oldMessagesFound+" New messages written: "+newMessagesWritten); } /** * Escapes special characters that may be bad in sql statements * -- "/" is replaced with "_" * @param input Original string to parse * @return A string with any special characters escaped */ protected String escapeSpecialCharsForId(String input) { String output = input.replaceAll("/", "_"); return output; } protected void printDebug(String message) { if (debug) { //System.out.println(message); //logger.debug(message); logger.info("DEBUG: " + message); } } /** * Looks up the sql statements defined in chat-sql.properties. * @param key * @return */ private String getMessageFromBundle(String key) { if (toolBundle == null) toolBundle = new ResourceLoader("chat-sql"); return toolBundle.getString(key); } public boolean isChatMigrationExecuteImmediate() { return chatMigrationExecuteImmediate; } public void setChatMigrationExecuteImmediate( boolean chatMigrationExecuteImmediate) { this.chatMigrationExecuteImmediate = chatMigrationExecuteImmediate; } public boolean isPerformChatMigration() { return performChatMigration; } public void setPerformChatMigration(boolean performChatMigration) { this.performChatMigration = performChatMigration; } public boolean isDebug() { return debug; } public void setDebug(boolean debug) { this.debug = debug; } public SqlService getSqlService() { return sqlService; } public void setSqlService(SqlService sqlService) { this.sqlService = sqlService; } public ChatManager getChatManager() { return chatManager; } public void setChatManager(ChatManager chatManager) { this.chatManager = chatManager; } }