blob: 453bd4dbe99e268e286f40da60abbd5af3517217 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.lucene.luke.app.desktop.components.dialog.menubar;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import java.awt.BorderLayout;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.index.CheckIndex;
import org.apache.lucene.luke.app.DirectoryHandler;
import org.apache.lucene.luke.app.DirectoryObserver;
import org.apache.lucene.luke.app.IndexHandler;
import org.apache.lucene.luke.app.IndexObserver;
import org.apache.lucene.luke.app.LukeState;
import org.apache.lucene.luke.app.desktop.Preferences;
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
import org.apache.lucene.luke.app.desktop.util.DialogOpener;
import org.apache.lucene.luke.app.desktop.util.FontUtils;
import org.apache.lucene.luke.app.desktop.util.ImageUtils;
import org.apache.lucene.luke.app.desktop.util.MessageUtils;
import org.apache.lucene.luke.app.desktop.util.StyleConstants;
import org.apache.lucene.luke.app.desktop.util.TextAreaPrintStream;
import org.apache.lucene.luke.models.tools.IndexTools;
import org.apache.lucene.luke.models.tools.IndexToolsFactory;
import org.apache.lucene.luke.util.LoggerFactory;
import org.apache.lucene.util.NamedThreadFactory;
/** Factory of check index dialog */
public final class CheckIndexDialogFactory implements DialogOpener.DialogFactory {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static CheckIndexDialogFactory instance;
private final Preferences prefs;
private final IndexToolsFactory indexToolsFactory;
private final DirectoryHandler directoryHandler;
private final IndexHandler indexHandler;
private final JLabel resultLbl = new JLabel();
private final JLabel statusLbl = new JLabel();
private final JLabel indicatorLbl = new JLabel();
private final JButton repairBtn = new JButton();
private final JTextArea logArea = new JTextArea();
private JDialog dialog;
private LukeState lukeState;
private CheckIndex.Status status;
private IndexTools toolsModel;
private final ListenerFunctions listeners = new ListenerFunctions();
public synchronized static CheckIndexDialogFactory getInstance() throws IOException {
if (instance == null) {
instance = new CheckIndexDialogFactory();
}
return instance;
}
private CheckIndexDialogFactory() throws IOException {
this.prefs = PreferencesFactory.getInstance();
this.indexToolsFactory = new IndexToolsFactory();
this.indexHandler = IndexHandler.getInstance();
this.directoryHandler = DirectoryHandler.getInstance();
indexHandler.addObserver(new Observer());
directoryHandler.addObserver(new Observer());
initialize();
}
private void initialize() {
repairBtn.setText(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("checkidx.button.fix")));
repairBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
repairBtn.setMargin(new Insets(3, 3, 3, 3));
repairBtn.setEnabled(false);
repairBtn.addActionListener(listeners::repairIndex);
indicatorLbl.setIcon(ImageUtils.createImageIcon("indicator.gif", 20, 20));
logArea.setEditable(false);
}
@Override
public JDialog create(Window owner, String title, int width, int height) {
dialog = new JDialog(owner, title, Dialog.ModalityType.APPLICATION_MODAL);
dialog.add(content());
dialog.setSize(new Dimension(width, height));
dialog.setLocationRelativeTo(owner);
dialog.getContentPane().setBackground(prefs.getColorTheme().getBackgroundColor());
return dialog;
}
private JPanel content() {
JPanel panel = new JPanel();
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
panel.add(controller());
panel.add(new JSeparator(JSeparator.HORIZONTAL));
panel.add(logs());
return panel;
}
private JPanel controller() {
JPanel panel = new JPanel(new GridLayout(3, 1));
panel.setOpaque(false);
JPanel idxPath = new JPanel(new FlowLayout(FlowLayout.LEADING));
idxPath.setOpaque(false);
idxPath.add(new JLabel(MessageUtils.getLocalizedMessage("checkidx.label.index_path")));
JLabel idxPathLbl = new JLabel(lukeState.getIndexPath());
idxPathLbl.setToolTipText(lukeState.getIndexPath());
idxPath.add(idxPathLbl);
panel.add(idxPath);
JPanel results = new JPanel(new GridLayout(2, 1));
results.setOpaque(false);
results.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
results.add(new JLabel(MessageUtils.getLocalizedMessage("checkidx.label.results")));
results.add(resultLbl);
panel.add(results);
JPanel execButtons = new JPanel(new FlowLayout(FlowLayout.TRAILING));
execButtons.setOpaque(false);
JButton checkBtn = new JButton(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("checkidx.button.check")));
checkBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
checkBtn.setMargin(new Insets(3, 0, 3, 0));
checkBtn.addActionListener(listeners::checkIndex);
execButtons.add(checkBtn);
JButton closeBtn = new JButton(MessageUtils.getLocalizedMessage("button.close"));
closeBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
closeBtn.setMargin(new Insets(3, 0, 3, 0));
closeBtn.addActionListener(e -> dialog.dispose());
execButtons.add(closeBtn);
panel.add(execButtons);
return panel;
}
private JPanel logs() {
JPanel panel = new JPanel(new BorderLayout());
panel.setOpaque(false);
JPanel header = new JPanel();
header.setOpaque(false);
header.setLayout(new BoxLayout(header, BoxLayout.PAGE_AXIS));
JPanel repair = new JPanel(new FlowLayout(FlowLayout.LEADING));
repair.setOpaque(false);
repair.add(repairBtn);
JTextArea warnArea = new JTextArea(MessageUtils.getLocalizedMessage("checkidx.label.warn"), 3, 30);
warnArea.setLineWrap(true);
warnArea.setEditable(false);
warnArea.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
repair.add(warnArea);
header.add(repair);
JPanel note = new JPanel(new FlowLayout(FlowLayout.LEADING));
note.setOpaque(false);
note.add(new JLabel(MessageUtils.getLocalizedMessage("checkidx.label.note")));
header.add(note);
JPanel status = new JPanel(new FlowLayout(FlowLayout.LEADING));
status.setOpaque(false);
status.add(new JLabel(MessageUtils.getLocalizedMessage("label.status")));
statusLbl.setText("Idle");
status.add(statusLbl);
indicatorLbl.setVisible(false);
status.add(indicatorLbl);
header.add(status);
panel.add(header, BorderLayout.PAGE_START);
logArea.setText("");
panel.add(new JScrollPane(logArea), BorderLayout.CENTER);
return panel;
}
private class Observer implements IndexObserver, DirectoryObserver {
@Override
public void openIndex(LukeState state) {
lukeState = state;
toolsModel = indexToolsFactory.newInstance(state.getIndexReader(), state.useCompound(), state.keepAllCommits());
}
@Override
public void closeIndex() {
close();
}
@Override
public void openDirectory(LukeState state) {
lukeState = state;
toolsModel = indexToolsFactory.newInstance(state.getDirectory());
}
@Override
public void closeDirectory() {
close();
}
private void close() {
toolsModel = null;
}
}
private class ListenerFunctions {
void checkIndex(ActionEvent e) {
ExecutorService executor = Executors.newFixedThreadPool(1, new NamedThreadFactory("check-index-dialog-check"));
SwingWorker<CheckIndex.Status, Void> task = new SwingWorker<CheckIndex.Status, Void>() {
@Override
protected CheckIndex.Status doInBackground() {
setProgress(0);
statusLbl.setText("Running...");
indicatorLbl.setVisible(true);
TextAreaPrintStream ps;
try {
ps = new TextAreaPrintStream(logArea);
CheckIndex.Status status = toolsModel.checkIndex(ps);
ps.flush();
return status;
} catch (UnsupportedEncodingException e) {
// will not reach
} catch (Exception e) {
statusLbl.setText(MessageUtils.getLocalizedMessage("message.error.unknown"));
throw e;
} finally {
setProgress(100);
}
return null;
}
@Override
protected void done() {
try {
CheckIndex.Status st = get();
resultLbl.setText(createResultsMessage(st));
indicatorLbl.setVisible(false);
statusLbl.setText("Done");
if (!st.clean) {
repairBtn.setEnabled(true);
}
status = st;
} catch (Exception e) {
log.error("Error checking index", e);
statusLbl.setText(MessageUtils.getLocalizedMessage("message.error.unknown"));
}
}
};
executor.submit(task);
executor.shutdown();
}
private String createResultsMessage(CheckIndex.Status status) {
String msg;
if (status == null) {
msg = "?";
} else if (status.clean) {
msg = "OK";
} else if (status.toolOutOfDate) {
msg = "ERROR: Can't check - tool out-of-date";
} else {
StringBuilder sb = new StringBuilder("BAD:");
if (status.missingSegments) {
sb.append(" Missing segments.");
}
if (status.numBadSegments > 0) {
sb.append(" numBadSegments=");
sb.append(status.numBadSegments);
}
if (status.totLoseDocCount > 0) {
sb.append(" totLoseDocCount=");
sb.append(status.totLoseDocCount);
}
msg = sb.toString();
}
return msg;
}
void repairIndex(ActionEvent e) {
if (status == null) {
return;
}
ExecutorService executor = Executors.newFixedThreadPool(1, new NamedThreadFactory("check-index-dialog-repair"));
SwingWorker<CheckIndex.Status, Void> task = new SwingWorker<CheckIndex.Status, Void>() {
@Override
protected CheckIndex.Status doInBackground() {
setProgress(0);
statusLbl.setText("Running...");
indicatorLbl.setVisible(true);
logArea.setText("");
TextAreaPrintStream ps;
try {
ps = new TextAreaPrintStream(logArea);
toolsModel.repairIndex(status, ps);
statusLbl.setText("Done");
ps.flush();
return status;
} catch (UnsupportedEncodingException e) {
// will not occur
} catch (Exception e) {
statusLbl.setText(MessageUtils.getLocalizedMessage("message.error.unknown"));
throw e;
} finally {
setProgress(100);
}
return null;
}
@Override
protected void done() {
indexHandler.open(lukeState.getIndexPath(), lukeState.getDirImpl());
logArea.append("Repairing index done.");
resultLbl.setText("");
indicatorLbl.setVisible(false);
repairBtn.setEnabled(false);
}
};
executor.submit(task);
executor.shutdown();
}
}
}