001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.storage.search.implementation.database.informix.excalibur;
011:
012: import java.io.*;
013: import java.sql.*;
014: import java.util.*;
015:
016: import org.mmbase.util.xml.ModuleReader;
017: import org.w3c.dom.*;
018: import org.xml.sax.*;
019:
020: /**
021: * The Etx index creator creates Excalibur Text Search indices,
022: * when used with an Informix database and a Excalibur Text Search datablade.
023: * This class is provided as a utility to supplement the
024: * {@link EtxSqlHandler EtxSqlHandler}.
025: * <p>
026: * When run as an application, the index creator reads a list of etx-indices
027: * from a configuration file, and creates the indices that are not present
028: * already.
029: * The configurationfile must be named <em>etxindices.xml</em> and located
030: * inside the <em>databases</em> configuration directory.
031: * It's DTD is located in the directory
032: * <code>org.mmbase.storage.search.implementation.database.informix.excalibur.resources</code>
033: * in the MMBase source tree and
034: * <a href="http://www.mmbase.org/dtd/etxindices.dtd">here</a> online.
035: *
036: * @author Rob van Maris
037: * @version $Id: EtxIndexCreator.java,v 1.6 2007/03/02 21:03:05 nklasens Exp $
038: * @since MMBase-1.7
039: */
040: public class EtxIndexCreator {
041:
042: /** Path to the MMBase configuration directory. */
043: private String configDir = null;
044:
045: /** Database connection. */
046: private Connection con = null;
047:
048: /**
049: * Creates a new instance of EtxIndexCreator, opens database connection.
050: *
051: * @param configDir Path to MMBase configuration directory.
052: */
053: public EtxIndexCreator(String configDir) throws Exception {
054: this .configDir = configDir;
055:
056: // Get database connection:
057: // 1 - read database configuration
058: ModuleReader moduleReader = new ModuleReader(new InputSource(
059: new FileInputStream(configDir + "/modules/jdbc.xml")));
060: Map<String, String> properties = moduleReader.getProperties();
061: String url = properties.get("url");
062: String host = properties.get("host");
063: String port = properties.get("port");
064: String database = properties.get("database");
065: String user = properties.get("user");
066: String password = properties.get("password");
067: String driver = properties.get("driver");
068: // 2 - construct url, substituting database, host and port when needed
069: int pos = url.indexOf("$DBM");
070: if (pos != -1) {
071: url = url.substring(0, pos) + database
072: + url.substring(pos + 4);
073: }
074: pos = url.indexOf("$HOST");
075: if (pos != -1) {
076: url = url.substring(0, pos) + host + url.substring(pos + 5);
077: }
078: pos = url.indexOf("$PORT");
079: if (pos != -1) {
080: url = url.substring(0, pos) + port + url.substring(pos + 5);
081: }
082: // 3 - Load driver
083: Class.forName(driver);
084: // 4 - Create connection
085: if (user.equals("url") && password.equals("url")) {
086: con = DriverManager.getConnection(url);
087: } else {
088: con = DriverManager.getConnection(url, user, password);
089: }
090: }
091:
092: /**
093: * Application main method.
094: * <p>
095: * Reads etxindices configuration file, and creates the etx indices
096: * that are not already created.
097: *
098: * @param args The command line arguments, should be path to
099: * MMBase configuration directory.
100: */
101: public static void main(String[] args) {
102: if (args.length != 1) {
103: System.out
104: .println("Command line arguments not as expected,"
105: + "should be path to MMBase configuration directory.");
106: System.exit(1);
107: }
108: try {
109: // Execute tasks.
110: new EtxIndexCreator(args[0]).execute();
111: } catch (Exception e) {
112: e.printStackTrace();
113: }
114: }
115:
116: /**
117: * Executes the tasks: reads configuration file and creates indices
118: * as needed, and closes database connection.
119: */
120: public void execute() throws Exception {
121: try {
122: // Read etxindices config.
123: File etxConfigFile = new File(configDir
124: + "/databases/etxindices.xml");
125: XmlEtxIndicesReader configReader = new XmlEtxIndicesReader(
126: new InputSource(new BufferedReader(new FileReader(
127: etxConfigFile))));
128:
129: for (Iterator<Element> iSbspaces = configReader
130: .getSbspaceElements(); iSbspaces.hasNext();) {
131: Element sbspace = iSbspaces.next();
132: String sbspaceName = configReader
133: .getSbspaceName(sbspace);
134:
135: for (Iterator<Element> iEtxindices = configReader
136: .getEtxindexElements(sbspace); iEtxindices
137: .hasNext();) {
138: Element etxindex = iEtxindices.next();
139: String name = configReader
140: .getEtxindexValue(etxindex);
141: String table = configReader
142: .getEtxindexTable(etxindex);
143: String field = configReader
144: .getEtxindexField(etxindex);
145: if (!etxIndexExists(name)) {
146: createEtxIndex(sbspaceName, name, table, field);
147: }
148: }
149: }
150:
151: } finally {
152: if (con != null) {
153: con.close();
154: }
155: }
156: }
157:
158: /**
159: * Tests if a Etx index already exists with a specified name.
160: * NOTE: Tests if the index exists, but does not verify that is is
161: * indeed an Etx index.
162: *
163: * @param etxindexName The index name.
164: * @return True if a Etx index already exists with this name,
165: * false otherwise.
166: */
167: private boolean etxIndexExists(String etxindexName)
168: throws SQLException {
169: PreparedStatement ps = null;
170: ResultSet rs = null;
171: try {
172: ps = con
173: .prepareStatement("SELECT * FROM sysindexes WHERE idxname = ?");
174: ps.setString(1, etxindexName);
175: try {
176: rs = ps.executeQuery();
177:
178: if (rs.next()) {
179: System.out.println("Index " + etxindexName
180: + " exists already.");
181: return true;
182: } else {
183: System.out.println("Index " + etxindexName
184: + " does not exist already.");
185: return false;
186: }
187: } finally {
188: if (rs != null) {
189: rs.close();
190: }
191: }
192: } finally {
193: if (ps != null) {
194: ps.close();
195: }
196: }
197: }
198:
199: /**
200: * Creates new Etx index.
201: *
202: * @param sbspace The sbspace to use.
203: * @param name The index name.
204: * @param table The table.
205: * @param field The field.
206: */
207: private void createEtxIndex(String sbspace, String name,
208: String table, String field) throws SQLException {
209: String operatorclass = getOperatorClass(table, field);
210: String sqlCreateIndex = "CREATE INDEX " + name + " ON " + table
211: + " (" + field + " " + operatorclass
212: + ") USING etx (CHAR_SET='OVERLAP_ISO', "
213: + "PHRASE_SUPPORT='MAXIMUM', "
214: + "WORD_SUPPORT='PATTERN') IN " + sbspace;
215:
216: PreparedStatement st = null;
217: try {
218: st = con.prepareStatement(sqlCreateIndex);
219: st.executeUpdate();
220: System.out.println("Index " + name + " created.");
221: } finally {
222: if (st != null) {
223: st.close();
224: }
225: }
226: }
227:
228: /**
229: * Determines the appropriate operator class for a field to be indexed,
230: * based on metadata retrieved from the database.
231: *
232: * @param table The table.
233: * @param field The field.
234: * @return The operator class.
235: */
236: private String getOperatorClass(String table, String field)
237: throws SQLException {
238: DatabaseMetaData metadata = con.getMetaData();
239: ResultSet columninfo = metadata.getColumns(null, null, table,
240: field);
241: try {
242: boolean hasRows = columninfo.next();
243: if (!hasRows) {
244: throw new IllegalArgumentException("The field " + field
245: + " of table " + table + " does not exist.");
246: }
247: String typeName = columninfo.getString("TYPE_NAME")
248: .toLowerCase();
249: if (typeName.equals("blob")) {
250: return "etx_blob_ops";
251: } else if (typeName.equals("clob")) {
252: return "etx_clob_ops";
253: } else if (typeName.equals("char")) {
254: return "etx_char_ops";
255: } else if (typeName.equals("lvarchar")) {
256: return "etx_lvarc_ops";
257: } else if (typeName.equals("varchar")) {
258: return ("etx_varc_ops");
259: } else {
260: throw new IllegalArgumentException(
261: "The field "
262: + field
263: + " of table "
264: + table
265: + " is not of an appropriate type for an Etx index.");
266: }
267: } finally {
268: columninfo.close();
269: }
270: }
271: }
|