001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2006 Continuent.
004: * Contact: sequoia@continuent.org
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * Initial developer(s): Jeff Mesnil.
019: * Contributor(s): ______________________.
020: */package org.continuent.sequoia.console.text.commands.dbadmin;
021:
022: import java.io.IOException;
023: import java.util.ArrayList;
024: import java.util.Arrays;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.StringTokenizer;
028:
029: import javax.management.openmbean.CompositeData;
030: import javax.management.openmbean.TabularData;
031:
032: import org.continuent.sequoia.common.i18n.ConsoleTranslate;
033: import org.continuent.sequoia.common.jmx.mbeans.RecoveryLogControlMBean;
034: import org.continuent.sequoia.console.text.formatter.TableFormatter;
035: import org.continuent.sequoia.console.text.module.VirtualDatabaseAdmin;
036:
037: /**
038: * This class defines the command used to dump the recovery log.
039: */
040: public class DumpRecoveryLog extends AbstractAdminCommand {
041: /**
042: * Creates a new <code>DumpRecoveryLog</code> object
043: *
044: * @param module the commands is attached to
045: */
046: public DumpRecoveryLog(VirtualDatabaseAdmin module) {
047: super (module);
048: }
049:
050: /**
051: * @see org.continuent.sequoia.console.text.commands.ConsoleCommand#parse(java.lang.String)
052: */
053: public void parse(String commandText) throws Exception {
054: if ("indexes".equals(commandText.trim())) //$NON-NLS-1$
055: {
056: printIndexes();
057: return;
058: }
059: StringTokenizer tokenizer = new StringTokenizer(commandText
060: .trim());
061:
062: long min = 0;
063: if (tokenizer.hasMoreTokens()) {
064: String minStr = tokenizer.nextToken();
065: try {
066: min = Long.parseLong(minStr);
067: } catch (NumberFormatException e) {
068: console.printError(getUsage());
069: return;
070: }
071: }
072:
073: RecoveryLogControlMBean recoveryLog = jmxClient.getRecoveryLog(
074: dbName, user, password);
075:
076: long max;
077:
078: // if a negative min is supplied, dump from tail
079: if (min < 0) {
080: max = recoveryLog.getIndexes()[1];
081: min = max + min + 1;
082: } else {
083: String maxStr;
084: if (tokenizer.hasMoreTokens()) {
085: maxStr = tokenizer.nextToken();
086: try {
087: max = Long.parseLong(maxStr);
088: } catch (NumberFormatException e) {
089: console.printError(getUsage());
090: return;
091: }
092: } else {
093: max = recoveryLog.getIndexes()[1];
094: }
095: }
096:
097: if (min < 0 || max < 0) {
098: console.printError("Negative indexes are not allowed");
099: return;
100: }
101:
102: boolean verbose = false;
103: if (tokenizer.hasMoreTokens()) {
104: String verboseStr = tokenizer.nextToken();
105: if (verboseStr.equals("-v")) {
106: verbose = true;
107: } else {
108: console.printError(getUsage());
109: return;
110: }
111: }
112: String[] headers = recoveryLog.getHeaders();
113:
114: // zap sql params column - which can be huge - if not printing
115: // verbose (default)
116: if (!verbose) {
117: List l = new ArrayList(Arrays.asList(headers));
118: l.remove(3); // this is the sql params column
119: headers = (String[]) l.toArray(new String[l.size()]);
120: }
121:
122: // first headers is used to sort the entries
123: final String idKey = headers[0];
124: TabularData logEntries = recoveryLog.dump(min);
125:
126: if (logEntries.isEmpty()) {
127: console.printInfo(ConsoleTranslate
128: .get("DumpRecoveryLog.empty")); //$NON-NLS-1$
129: return;
130: }
131: long lastIndex = 0;
132: while (!logEntries.isEmpty()) {
133: List entries = JMXUtils.sortEntriesByKey(logEntries, idKey);
134: lastIndex = getIndexOfLastEntry(entries, idKey);
135: if (lastIndex > max) {
136: removeAfterIndex(entries, max, idKey);
137: }
138: String[][] entriesStr = JMXUtils.from(entries, headers);
139: console.println(TableFormatter.format(headers, entriesStr,
140: true));
141:
142: if (lastIndex > max) {
143: break;
144: }
145: logEntries = recoveryLog.dump(lastIndex + 1);
146: }
147: }
148:
149: /*
150: * Remove all entries whose indexes are greater than the index parameter
151: */
152: private void removeAfterIndex(List entries, long index, String idKey) {
153: Iterator iter = entries.iterator();
154: while (iter.hasNext()) {
155: CompositeData data = (CompositeData) iter.next();
156: String idStr = (String) data.get(idKey);
157: long id = Long.parseLong(idStr);
158: if (id > index) {
159: iter.remove();
160: }
161: }
162: }
163:
164: private void printIndexes() {
165: try {
166: RecoveryLogControlMBean recoveryLog = jmxClient
167: .getRecoveryLog(dbName, user, password);
168: long[] indexes = recoveryLog.getIndexes();
169: String minIndex = (indexes != null) ? Long
170: .toString(indexes[0]) : ConsoleTranslate
171: .get("DumpRecoveryLog.notAvailable"); //$NON-NLS-1$
172: String maxIndex = (indexes != null) ? Long
173: .toString(indexes[1]) : ConsoleTranslate
174: .get("DumpRecoveryLog.notAvailable"); //$NON-NLS-1$
175: console.println(ConsoleTranslate.get(
176: "DumpRecoveryLog.minIndex", minIndex)); //$NON-NLS-1$
177: console.println(ConsoleTranslate.get(
178: "DumpRecoveryLog.maxIndex", maxIndex)); //$NON-NLS-1$
179: console
180: .println(ConsoleTranslate
181: .get(
182: "DumpRecoveryLog.numberOfEntries", recoveryLog.getEntries())); //$NON-NLS-1$
183: } catch (IOException e) {
184: console.printError(e.getMessage());
185: }
186: }
187:
188: /**
189: * Returns the index of the last entry in the <code>entries</code> List
190: *
191: * @param entries a List<CompositeData>
192: * @param idKey the key corresponding to the id of the index
193: * @return the index of the last entry in the <code>entries</code> List
194: */
195: private static int getIndexOfLastEntry(List entries, String idKey) {
196: CompositeData data = (CompositeData) entries
197: .get(entries.size() - 1);
198: String idStr = (String) data.get(idKey);
199: return Integer.parseInt(idStr);
200: }
201:
202: /**
203: * @see org.continuent.sequoia.console.text.commands.ConsoleCommand#getCommandName()
204: */
205: public String getCommandName() {
206:
207: return "dump recoverylog"; //$NON-NLS-1$
208: }
209:
210: /**
211: * @see org.continuent.sequoia.console.text.commands.ConsoleCommand#getCommandParameters()
212: */
213: public String getCommandParameters() {
214: return "[indexes | {<min> <max>} | -<nb entries> [-v]]"; //$NON-NLS-1$
215: }
216:
217: /**
218: * @see org.continuent.sequoia.console.text.commands.ConsoleCommand#getCommandDescription()
219: */
220: public String getCommandDescription() {
221: return ConsoleTranslate.get("DumpRecoveryLog.description"); //$NON-NLS-1$
222: }
223: }
|