0001: /*
0002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
0003: * notice. All rights reserved.
0004: */
0005: package com.tc.admin;
0006:
0007: import org.apache.commons.httpclient.HttpClient;
0008: import org.apache.commons.httpclient.HttpStatus;
0009: import org.apache.commons.httpclient.methods.GetMethod;
0010: import org.apache.commons.io.IOUtils;
0011: import org.apache.commons.lang.StringUtils;
0012: import org.apache.log4j.Level;
0013: import org.apache.log4j.Logger;
0014: import org.dijon.Button;
0015: import org.dijon.CheckBox;
0016: import org.dijon.Container;
0017: import org.dijon.Dialog;
0018: import org.dijon.DialogResource;
0019: import org.dijon.FrameResource;
0020: import org.dijon.Label;
0021: import org.dijon.ScrollPane;
0022: import org.dijon.Separator;
0023: import org.dijon.TextArea;
0024: import org.dijon.TextPane;
0025: import org.dijon.UndoMonger;
0026:
0027: import com.tc.admin.common.BrowserLauncher;
0028: import com.tc.admin.common.ComponentNode;
0029: import com.tc.admin.common.ContactTerracottaAction;
0030: import com.tc.admin.common.PrefsHelper;
0031: import com.tc.admin.common.XAbstractAction;
0032: import com.tc.admin.common.XContainer;
0033: import com.tc.admin.common.XMenu;
0034: import com.tc.admin.common.XMenuBar;
0035: import com.tc.admin.common.XMenuItem;
0036: import com.tc.admin.common.XRootNode;
0037: import com.tc.admin.common.XTabbedPane;
0038: import com.tc.admin.common.XTextArea;
0039: import com.tc.admin.common.XTextField;
0040: import com.tc.admin.common.XTreeModel;
0041: import com.tc.admin.common.XTreeNode;
0042:
0043: import java.awt.BorderLayout;
0044: import java.awt.Frame;
0045: import java.awt.HeadlessException;
0046: import java.awt.event.ActionEvent;
0047: import java.awt.event.ActionListener;
0048: import java.awt.event.HierarchyEvent;
0049: import java.awt.event.HierarchyListener;
0050: import java.awt.event.KeyAdapter;
0051: import java.awt.event.KeyEvent;
0052: import java.awt.event.MouseAdapter;
0053: import java.awt.event.MouseEvent;
0054: import java.beans.PropertyChangeEvent;
0055: import java.beans.PropertyChangeListener;
0056: import java.io.IOException;
0057: import java.io.InputStream;
0058: import java.io.PrintWriter;
0059: import java.io.StringWriter;
0060: import java.net.ConnectException;
0061: import java.net.InetAddress;
0062: import java.net.MalformedURLException;
0063: import java.net.URL;
0064: import java.net.URLEncoder;
0065: import java.net.UnknownHostException;
0066: import java.util.ArrayList;
0067: import java.util.Date;
0068: import java.util.Properties;
0069: import java.util.StringTokenizer;
0070: import java.util.prefs.Preferences;
0071:
0072: import javax.swing.Icon;
0073: import javax.swing.JCheckBoxMenuItem;
0074: import javax.swing.JDialog;
0075: import javax.swing.JOptionPane;
0076: import javax.swing.JPopupMenu;
0077: import javax.swing.JScrollPane;
0078: import javax.swing.JSeparator;
0079: import javax.swing.JSplitPane;
0080: import javax.swing.KeyStroke;
0081: import javax.swing.SwingConstants;
0082: import javax.swing.SwingUtilities;
0083: import javax.swing.Timer;
0084: import javax.swing.event.ChangeEvent;
0085: import javax.swing.event.ChangeListener;
0086: import javax.swing.event.DocumentEvent;
0087: import javax.swing.event.DocumentListener;
0088: import javax.swing.event.HyperlinkEvent;
0089: import javax.swing.event.HyperlinkListener;
0090: import javax.swing.event.TreeSelectionEvent;
0091: import javax.swing.event.TreeSelectionListener;
0092: import javax.swing.text.AttributeSet;
0093: import javax.swing.text.Element;
0094: import javax.swing.text.JTextComponent;
0095: import javax.swing.text.html.HTML;
0096: import javax.swing.tree.TreeModel;
0097: import javax.swing.tree.TreePath;
0098: import javax.swing.undo.UndoManager;
0099: import javax.swing.undo.UndoableEdit;
0100:
0101: public class AdminClientPanel extends XContainer implements
0102: AdminClientController, UndoMonger {
0103: private NavTree m_tree;
0104: private XContainer m_nodeView;
0105: private JSplitPane m_mainSplitter;
0106: private Integer m_mainDivLoc;
0107: private JSplitPane m_leftSplitter;
0108: private Integer m_leftDivLoc;
0109: private DividerListener m_dividerListener;
0110: private XTabbedPane m_bottomPane;
0111: private XTextArea m_logArea;
0112: private ArrayList m_logListeners;
0113: private Icon m_infoIcon;
0114: private XTextField m_statusLine;
0115: protected UndoAction m_undoCmd;
0116: protected RedoAction m_redoCmd;
0117: protected UndoManager m_undoManager;
0118: protected NewServerAction m_newServerAction;
0119: protected HelpAction m_helpAction;
0120: protected VersionCheckControlAction m_versionCheckAction;
0121: protected JCheckBoxMenuItem m_versionCheckToggle;
0122: protected AboutAction m_aboutAction;
0123:
0124: public static final String UNDO = "Undo";
0125: public static final String REDO = "Redo";
0126:
0127: protected MouseAdapter m_statusCleaner = new MouseAdapter() {
0128: public void mouseClicked(MouseEvent e) {
0129: setStatus(null);
0130: }
0131: };
0132:
0133: public AdminClientPanel() {
0134: super ();
0135:
0136: AdminClientContext acc = AdminClient.getContext();
0137:
0138: acc.controller = this ;
0139:
0140: FrameResource frameRes = acc.topRes.getFrame("MyFrame");
0141: load(frameRes.getContentPane());
0142:
0143: m_tree = (NavTree) findComponent("Tree");
0144: m_nodeView = (XContainer) findComponent("NodeView");
0145: m_bottomPane = (XTabbedPane) findComponent("BottomPane");
0146: m_logArea = (XTextArea) m_bottomPane.findComponent("LogArea");
0147: m_statusLine = (XTextField) findComponent("StatusLine");
0148:
0149: m_nodeView.setLayout(new BorderLayout());
0150:
0151: m_tree.addMouseListener(new MouseAdapter() {
0152: public void mousePressed(MouseEvent me) {
0153: TreePath path = m_tree.getPathForLocation(me.getX(), me
0154: .getY());
0155:
0156: if (path != null) {
0157: m_tree.requestFocus();
0158:
0159: XTreeNode node = (XTreeNode) path
0160: .getLastPathComponent();
0161: if (node != null) {
0162: select(node);
0163: }
0164: }
0165: }
0166:
0167: public void mouseClicked(MouseEvent me) {
0168: TreePath path = m_tree.getPathForLocation(me.getX(), me
0169: .getY());
0170:
0171: if (path != null) {
0172: m_tree.requestFocus();
0173:
0174: XTreeNode node = (XTreeNode) path
0175: .getLastPathComponent();
0176: if (node != null) {
0177: node.nodeClicked(me);
0178: }
0179: }
0180: }
0181: });
0182:
0183: m_tree.addTreeSelectionListener(new TreeSelectionListener() {
0184: public void valueChanged(TreeSelectionEvent tse) {
0185: TreePath path = tse.getNewLeadSelectionPath();
0186:
0187: m_nodeView.removeAll();
0188:
0189: if (path != null) {
0190: m_tree.requestFocus();
0191:
0192: XTreeNode node = (XTreeNode) path
0193: .getLastPathComponent();
0194: if (node != null) {
0195: node.nodeSelected(tse);
0196:
0197: if (node instanceof ComponentNode) {
0198: ComponentNode cnode = (ComponentNode) node;
0199: java.awt.Component comp = (java.awt.Component) cnode
0200: .getComponent();
0201:
0202: if (comp != null) {
0203: m_nodeView.add(comp);
0204: }
0205: }
0206: }
0207: }
0208:
0209: m_nodeView.revalidate();
0210: m_nodeView.repaint();
0211: }
0212: });
0213:
0214: m_infoIcon = LogHelper.getHelper().getInfoIcon();
0215:
0216: m_logListeners = new ArrayList();
0217: LogDocumentListener ldl = new LogDocumentListener(m_logArea);
0218: m_logListeners.add(ldl);
0219: m_logArea.getDocument().addDocumentListener(ldl);
0220:
0221: m_bottomPane.addChangeListener(new ChangeListener() {
0222: public void stateChanged(ChangeEvent e) {
0223: int index = m_bottomPane.getSelectedIndex();
0224: m_bottomPane.setIconAt(index, null);
0225: }
0226: });
0227:
0228: m_tree.setModel(new NavTreeModel());
0229:
0230: setHelpPath("/com/tc/admin/AdminClient.html");
0231:
0232: addMouseListener(m_statusCleaner);
0233:
0234: addKeyListener(new KeyAdapter() {
0235: public void keyPressed(KeyEvent e) {
0236: setStatus(null);
0237: }
0238: });
0239:
0240: getActionMap().put(UNDO, m_undoCmd = new UndoAction());
0241: getActionMap().put(REDO, m_redoCmd = new RedoAction());
0242:
0243: initNavTreeMenu();
0244: }
0245:
0246: protected NewServerAction getNewServerAction() {
0247: if (m_newServerAction == null) {
0248: m_newServerAction = new NewServerAction();
0249: }
0250: return m_newServerAction;
0251: }
0252:
0253: protected HelpAction getHelpAction() {
0254: if (m_helpAction == null) {
0255: m_helpAction = new HelpAction();
0256: }
0257: return m_helpAction;
0258: }
0259:
0260: protected AboutAction getAboutAction() {
0261: if (m_aboutAction == null) {
0262: m_aboutAction = new AboutAction();
0263: }
0264: return m_aboutAction;
0265: }
0266:
0267: protected void initNavTreeMenu() {
0268: JPopupMenu popup = new JPopupMenu("ProjectTree Actions");
0269:
0270: popup.add(getNewServerAction());
0271: popup.add(new Separator());
0272: popup.add(getHelpAction());
0273: if (shouldAddAboutItem()) {
0274: popup.add(getAboutAction());
0275: }
0276:
0277: m_tree.setPopupMenu(popup);
0278: }
0279:
0280: private String getBundleString(String key) {
0281: return AdminClient.getContext().getMessage(key);
0282: }
0283:
0284: private String formatBundleString(String key, Object[] args) {
0285: return AdminClient.getContext().format(key, args);
0286: }
0287:
0288: private String formatBundleString(String key, Object arg) {
0289: return formatBundleString(key, new Object[] { arg });
0290: }
0291:
0292: public void initMenubar(XMenuBar menuBar) {
0293: XMenu menu = new XMenu(getBundleString("file.menu.label"));
0294:
0295: menu.add(m_newServerAction = new NewServerAction());
0296: menu.add(new JSeparator());
0297: menu.add(new QuitAction());
0298:
0299: menuBar.add(menu);
0300:
0301: menu = new XMenu(getBundleString("help.menu.label"));
0302: XMenuItem mitem = new XMenuItem("AdminConsole Help", HelpHelper
0303: .getHelper().getHelpIcon());
0304: mitem.setAction(m_helpAction = new HelpAction());
0305: menu.add(mitem);
0306: menu.addSeparator();
0307: menu.add(new ContactTerracottaAction("Visit Terracotta Forums",
0308: "http://www.terracottatech.com/forums/"));
0309: menu
0310: .add(new ContactTerracottaAction(
0311: "Contact Terracotta Technical Support",
0312: "http://www.terracottatech.com/support_services.shtml"));
0313: menu.addSeparator();
0314: menu.add(new UpdateCheckerAction());
0315: m_versionCheckAction = new VersionCheckControlAction();
0316: m_versionCheckToggle = new JCheckBoxMenuItem(
0317: m_versionCheckAction);
0318: m_versionCheckToggle.setSelected(m_versionCheckAction
0319: .isVersionCheckEnabled());
0320: menu.add(m_versionCheckToggle);
0321: menu.add(m_aboutAction = new AboutAction());
0322:
0323: menuBar.add(menu);
0324: }
0325:
0326: class HelpAction extends XAbstractAction {
0327: HelpAction() {
0328: super ("AdminConsole Help");
0329: }
0330:
0331: public void actionPerformed(ActionEvent ae) {
0332: block();
0333: BrowserLauncher
0334: .openURL("http://www.terracotta.org/kit/reflector?kitID=2.5&pageID=ConsoleGuide");
0335: unblock();
0336: }
0337: }
0338:
0339: public boolean isExpanded(XTreeNode node) {
0340: return node != null
0341: && m_tree.isExpanded(new TreePath(node.getPath()));
0342: }
0343:
0344: public void expand(XTreeNode node) {
0345: if (node != null) {
0346: m_tree.expandPath(new TreePath(node.getPath()));
0347: }
0348: }
0349:
0350: public boolean isSelected(XTreeNode node) {
0351: return node != null
0352: && m_tree.isPathSelected(new TreePath(node.getPath()));
0353: }
0354:
0355: public void select(XTreeNode node) {
0356: if (node != null) {
0357: m_tree.requestFocus();
0358: m_tree.setSelectionPath(new TreePath(node.getPath()));
0359: }
0360: }
0361:
0362: public void remove(XTreeNode node) {
0363: XTreeModel model = (XTreeModel) m_tree.getModel();
0364: XTreeNode parent = (XTreeNode) node.getParent();
0365: int index = parent.getIndex(node);
0366: TreePath nodePath = new TreePath(node.getPath());
0367: TreePath selPath = m_tree.getSelectionPath();
0368:
0369: node.tearDown();
0370: model.removeNodeFromParent(node);
0371:
0372: if (nodePath.isDescendant(selPath)) {
0373: int count = parent.getChildCount();
0374:
0375: if (count > 0) {
0376: node = (XTreeNode) parent
0377: .getChildAt(index < count ? index : count - 1);
0378: } else {
0379: node = parent;
0380: }
0381:
0382: m_tree.setSelectionPath(new TreePath(node.getPath()));
0383: }
0384: }
0385:
0386: public void nodeStructureChanged(XTreeNode node) {
0387: TreeModel treeModel = m_tree.getModel();
0388:
0389: if (treeModel instanceof XTreeModel) {
0390: ((XTreeModel) treeModel).nodeStructureChanged(node);
0391: }
0392: }
0393:
0394: public void nodeChanged(XTreeNode node) {
0395: TreeModel treeModel = m_tree.getModel();
0396:
0397: if (treeModel instanceof XTreeModel) {
0398: ((XTreeModel) treeModel).nodeChanged(node);
0399: }
0400: }
0401:
0402: protected Preferences getPreferences() {
0403: AdminClientContext acc = AdminClient.getContext();
0404: return acc.prefs.node("AdminClientFrame");
0405: }
0406:
0407: protected void storePreferences() {
0408: AdminClientContext acc = AdminClient.getContext();
0409: acc.client.storePrefs();
0410: }
0411:
0412: private int getSplitPref(JSplitPane splitter) {
0413: Preferences prefs = getPreferences();
0414: Preferences splitPrefs = prefs.node(splitter.getName());
0415:
0416: return splitPrefs.getInt("Split", -1);
0417: }
0418:
0419: private JSplitPane getMainSplitter() {
0420: if (m_mainSplitter == null) {
0421: m_mainSplitter = (JSplitPane) findComponent("MainSplitter");
0422: m_mainDivLoc = new Integer(getSplitPref(m_mainSplitter));
0423:
0424: if (m_dividerListener == null) {
0425: m_dividerListener = new DividerListener();
0426: }
0427: }
0428:
0429: return m_mainSplitter;
0430: }
0431:
0432: private JSplitPane getLeftSplitter() {
0433: if (m_leftSplitter == null) {
0434: m_leftSplitter = (JSplitPane) findComponent("LeftSplitter");
0435: m_leftDivLoc = new Integer(getSplitPref(m_leftSplitter));
0436:
0437: if (m_dividerListener == null) {
0438: m_dividerListener = new DividerListener();
0439: }
0440: }
0441:
0442: return m_leftSplitter;
0443: }
0444:
0445: public void updateServerPrefs() {
0446: XRootNode root = m_tree.getRootNode();
0447: int count = root.getChildCount();
0448: AdminClientContext acc = AdminClient.getContext();
0449: PrefsHelper helper = PrefsHelper.getHelper();
0450: Preferences prefs = acc.prefs.node("AdminClient");
0451: Preferences serverPrefs = prefs.node(ServersHelper.SERVERS);
0452: Preferences serverPref;
0453: ServerNode serverNode;
0454:
0455: helper.clearChildren(serverPrefs);
0456:
0457: for (int i = 0; i < count; i++) {
0458: serverNode = (ServerNode) root.getChildAt(i);
0459: serverPref = serverPrefs.node("server-" + i);
0460: serverNode.setPreferences(serverPref);
0461: }
0462:
0463: storePreferences();
0464: }
0465:
0466: public void disconnectAll() {
0467: XRootNode root = m_tree.getRootNode();
0468: int count = root.getChildCount();
0469: ServerNode serverNode;
0470:
0471: for (int i = 0; i < count; i++) {
0472: serverNode = (ServerNode) root.getChildAt(i);
0473:
0474: if (serverNode.isConnected()) {
0475: serverNode.disconnectOnExit();
0476: }
0477: }
0478:
0479: root.tearDown();
0480: storePreferences();
0481: }
0482:
0483: class DividerListener implements PropertyChangeListener {
0484: public void propertyChange(PropertyChangeEvent pce) {
0485: JSplitPane splitter = (JSplitPane) pce.getSource();
0486: String propName = pce.getPropertyName();
0487:
0488: if (splitter.isShowing() == false
0489: || JSplitPane.DIVIDER_LOCATION_PROPERTY
0490: .equals(propName) == false) {
0491: return;
0492: }
0493:
0494: int divLoc = splitter.getDividerLocation();
0495: Integer divLocObj = new Integer(divLoc);
0496: Preferences prefs = getPreferences();
0497: String name = splitter.getName();
0498: Preferences node = prefs.node(name);
0499:
0500: node.putInt("Split", divLoc);
0501: storePreferences();
0502:
0503: if (m_mainSplitter.getName().equals(name)) {
0504: m_mainDivLoc = divLocObj;
0505: } else {
0506: m_leftDivLoc = divLocObj;
0507: }
0508: }
0509: }
0510:
0511: public void doLayout() {
0512: super .doLayout();
0513:
0514: JSplitPane splitter = getMainSplitter();
0515: if (m_mainDivLoc != null) {
0516: splitter.setDividerLocation(m_mainDivLoc.intValue());
0517: } else {
0518: splitter.setDividerLocation(0.7);
0519: }
0520:
0521: splitter = getLeftSplitter();
0522: if (m_leftDivLoc != null) {
0523: splitter.setDividerLocation(m_leftDivLoc.intValue());
0524: } else {
0525: splitter.setDividerLocation(0.25);
0526: }
0527: }
0528:
0529: public void log(String s) {
0530: m_logArea.append(s + System.getProperty("line.separator"));
0531: m_logArea
0532: .setCaretPosition(m_logArea.getDocument().getLength() - 1);
0533: }
0534:
0535: public void log(Exception e) {
0536: StringWriter sw = new StringWriter();
0537: PrintWriter pw = new PrintWriter(sw);
0538:
0539: e.printStackTrace(pw);
0540: pw.close();
0541:
0542: log(sw.toString());
0543: }
0544:
0545: public void setStatus(String msg) {
0546: m_statusLine.setText(msg);
0547: }
0548:
0549: public void clearStatus() {
0550: setStatus("");
0551: }
0552:
0553: public void addNotify() {
0554: super .addNotify();
0555:
0556: getMainSplitter().addPropertyChangeListener(m_dividerListener);
0557: getLeftSplitter().addPropertyChangeListener(m_dividerListener);
0558:
0559: // TODO: what's up with this in the plugin?
0560: // m_tree.requestFocusInWindow();
0561: }
0562:
0563: public void removeNotify() {
0564: getMainSplitter().removePropertyChangeListener(
0565: m_dividerListener);
0566: getLeftSplitter().removePropertyChangeListener(
0567: m_dividerListener);
0568:
0569: super .removeNotify();
0570: }
0571:
0572: class NewServerAction extends XAbstractAction {
0573: NewServerAction() {
0574: super (getBundleString("new.server.action.label"));
0575: }
0576:
0577: public void actionPerformed(ActionEvent ae) {
0578: XTreeModel model = (XTreeModel) m_tree.getModel();
0579: XTreeNode root = (XTreeNode) model.getRoot();
0580: int index = root.getChildCount();
0581: ServerNode serverNode = new ServerNode();
0582:
0583: model.insertNodeInto(serverNode, root, index);
0584: TreePath path = new TreePath(serverNode.getPath());
0585: m_tree.makeVisible(path);
0586: m_tree.setSelectionPath(path);
0587:
0588: AdminClientContext acc = AdminClient.getContext();
0589: PrefsHelper helper = PrefsHelper.getHelper();
0590: Preferences prefs = acc.prefs.node("AdminClient");
0591: Preferences servers = prefs.node(ServersHelper.SERVERS);
0592: int count = helper.childrenNames(servers).length;
0593:
0594: serverNode.setPreferences(servers.node("server-" + count));
0595: storePreferences();
0596: }
0597: }
0598:
0599: class QuitAction extends XAbstractAction {
0600: QuitAction() {
0601: super (getBundleString("quit.action.label"));
0602:
0603: setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
0604: MENU_SHORTCUT_KEY_MASK, true));
0605: }
0606:
0607: public void actionPerformed(ActionEvent ae) {
0608: System.exit(0);
0609: }
0610: }
0611:
0612: private java.awt.Frame getFrame() {
0613: return (java.awt.Frame) SwingUtilities.getAncestorOfClass(
0614: java.awt.Frame.class, this );
0615: }
0616:
0617: protected boolean shouldAddAboutItem() {
0618: return true;
0619: }
0620:
0621: /**
0622: * Returns if the major and minor elements of the passed version strings match.
0623: */
0624: private static boolean versionsMatch(String v1, String v2) {
0625: if (v1.equals(v2)) {
0626: return true;
0627: }
0628:
0629: String[] v1Elems = StringUtils.split(v1, '.');
0630: String[] v2Elems = StringUtils.split(v2, '.');
0631:
0632: return v1Elems != null && v2Elems != null
0633: && v1Elems.length == v2Elems.length
0634: && v1Elems.length > 1 && v1Elems[0].equals(v2Elems[0])
0635: && v1Elems[1].equals(v2Elems[1]);
0636: }
0637:
0638: public boolean testServerMatch(ServerNode serverNode) {
0639: if (com.tc.util.ProductInfo.getInstance().isDevMode()
0640: || m_versionCheckAction == null
0641: || !m_versionCheckAction.isVersionCheckEnabled()) {
0642: return true;
0643: }
0644:
0645: ProductInfo consoleInfo = new ProductInfo();
0646: String consoleVersion = consoleInfo.getVersion();
0647: ProductInfo serverInfo = serverNode.getProductInfo();
0648: String serverVersion = serverInfo.getVersion();
0649: int spaceIndex = serverVersion.lastIndexOf(" ");
0650:
0651: // The version string that comes from the server is of the form "Terracotta 2.4", while
0652: // the default ProductInfo.getVersion is just the raw version number string: "2.4"
0653:
0654: if (spaceIndex != -1) {
0655: serverVersion = serverVersion.substring(spaceIndex + 1);
0656: }
0657:
0658: if (!versionsMatch(consoleVersion, serverVersion)) {
0659: int answer = showVersionMismatchDialog(serverNode,
0660: consoleVersion, serverVersion);
0661: return (answer == JOptionPane.YES_OPTION);
0662: }
0663:
0664: return true;
0665: }
0666:
0667: public int showVersionMismatchDialog(ServerNode serverNode,
0668: String consoleVersion, String serverVersion)
0669: throws HeadlessException {
0670: Frame frame = getFrame();
0671: String msg = formatBundleString("version.check.message",
0672: new Object[] { serverNode, serverVersion,
0673: consoleVersion });
0674: Label label = new Label(msg);
0675: Container panel = new Container();
0676: panel.setLayout(new BorderLayout());
0677: panel.add(label);
0678: CheckBox versionCheckToggle = new CheckBox(
0679: getBundleString("version.check.disable.label"));
0680: versionCheckToggle.setHorizontalAlignment(SwingConstants.RIGHT);
0681: panel.add(versionCheckToggle, BorderLayout.SOUTH);
0682: String title = frame.getTitle();
0683: JOptionPane pane = new JOptionPane(panel,
0684: JOptionPane.QUESTION_MESSAGE,
0685: JOptionPane.YES_NO_OPTION, null, null, null);
0686:
0687: pane.setInitialValue(null);
0688: pane.setComponentOrientation(frame.getComponentOrientation());
0689:
0690: JDialog dialog = pane.createDialog(frame, title);
0691: serverNode.setVersionMismatchDialog(dialog);
0692:
0693: pane.selectInitialValue();
0694: dialog.show();
0695: dialog.dispose();
0696: serverNode.setVersionMismatchDialog(null);
0697:
0698: Object selectedValue = pane.getValue();
0699:
0700: if (selectedValue == null)
0701: return JOptionPane.CLOSED_OPTION;
0702: m_versionCheckAction.setVersionCheckEnabled(!versionCheckToggle
0703: .isSelected());
0704: if (selectedValue instanceof Integer) {
0705: return ((Integer) selectedValue).intValue();
0706: }
0707:
0708: return JOptionPane.CLOSED_OPTION;
0709: }
0710:
0711: class UpdateCheckerAction extends XAbstractAction {
0712: Dialog m_updateCheckerDialog;
0713: ProductInfo m_productInfo;
0714:
0715: UpdateCheckerAction() {
0716: super (getBundleString("update-checker.action.label"));
0717: Preferences updateCheckerPrefs = getUpdateCheckerPrefs();
0718: boolean shouldCheck = updateCheckerPrefs.getBoolean(
0719: "checking-enabled", true);
0720:
0721: Logger
0722: .getLogger(
0723: "org.apache.commons.httpclient.HttpClient")
0724: .setLevel(Level.OFF);
0725: Logger
0726: .getLogger(
0727: "org.apache.commons.httpclient.params.DefaultHttpParams")
0728: .setLevel(Level.OFF);
0729: Logger.getLogger(
0730: "org.apache.commons.httpclient.methods.GetMethod")
0731: .setLevel(Level.OFF);
0732: Logger.getLogger(
0733: "org.apache.commons.httpclient.HttpMethodDirector")
0734: .setLevel(Level.OFF);
0735: Logger.getLogger(
0736: "org.apache.commons.httpclient.HttpConnection")
0737: .setLevel(Level.OFF);
0738: Logger.getLogger(
0739: "org.apache.commons.httpclient.HttpMethodBase")
0740: .setLevel(Level.OFF);
0741: Logger.getLogger("org.apache.commons.httpclient.HttpState")
0742: .setLevel(Level.OFF);
0743: Logger
0744: .getLogger(
0745: "org.apache.commons.httpclient.HttpParser")
0746: .setLevel(Level.OFF);
0747: Logger.getLogger(
0748: "org.apache.commons.httpclient.cookie.CookieSpec")
0749: .setLevel(Level.OFF);
0750: Logger.getLogger("httpclient.wire.header").setLevel(
0751: Level.OFF);
0752: Logger.getLogger("httpclient.wire.content").setLevel(
0753: Level.OFF);
0754:
0755: if (shouldCheck) {
0756: long nextCheckTime = updateCheckerPrefs.getLong(
0757: "next-check-time", 0L);
0758:
0759: if (nextCheckTime == 0L) {
0760: updateCheckerPrefs.putLong("next-check-time",
0761: nextCheckTime());
0762: storePreferences();
0763: } else if (nextCheckTime < System.currentTimeMillis()) {
0764: AdminClientPanel.this
0765: .addHierarchyListener(new HierarchyListener() {
0766: public void hierarchyChanged(
0767: HierarchyEvent e) {
0768: if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0
0769: && AdminClientPanel.this
0770: .isDisplayable()) {
0771: AdminClientPanel.this
0772: .removeHierarchyListener(this );
0773: SwingUtilities
0774: .invokeLater(new Runnable() {
0775: public void run() {
0776: Timer t = new Timer(
0777: 100,
0778: UpdateCheckerAction.this );
0779: t
0780: .setRepeats(false);
0781: t.start();
0782: }
0783: });
0784: }
0785: }
0786: });
0787: }
0788: }
0789: }
0790:
0791: Preferences getUpdateCheckerPrefs() {
0792: return getPreferences().node("update-checker");
0793: }
0794:
0795: public void actionPerformed(ActionEvent e) {
0796: final Preferences updateCheckerPrefs = getUpdateCheckerPrefs();
0797: final boolean promptForCheck = updateCheckerPrefs
0798: .getBoolean("prompt-for-check-enabled", true);
0799: final Object src = e.getSource();
0800:
0801: if (promptForCheck || !(src instanceof Timer)) {
0802: if (m_updateCheckerDialog == null) {
0803: AdminClientContext acc = AdminClient.getContext();
0804: java.awt.Frame frame = getFrame();
0805:
0806: m_updateCheckerDialog = frame != null ? new Dialog(
0807: frame) : new Dialog();
0808: m_updateCheckerDialog
0809: .load((DialogResource) acc.topRes
0810: .child("UpdateCheckerDialog"));
0811:
0812: Button button = (Button) m_updateCheckerDialog
0813: .findComponent("EnableCheckingButton");
0814: button.addActionListener(new ActionListener() {
0815: public void actionPerformed(ActionEvent ae) {
0816: updateCheckerPrefs.putBoolean(
0817: "checking-enabled", true);
0818: storePreferences();
0819: UpdateCheckerAction.this
0820: .startUpdateCheck(true);
0821: m_updateCheckerDialog.setVisible(false);
0822: }
0823: });
0824: m_updateCheckerDialog.getRootPane()
0825: .setDefaultButton(button);
0826:
0827: CheckBox promptForCheckToggle = (CheckBox) m_updateCheckerDialog
0828: .findComponent("PromptForCheckToggle");
0829: promptForCheckToggle
0830: .addActionListener(new ActionListener() {
0831: public void actionPerformed(
0832: ActionEvent ae) {
0833: CheckBox cb = (CheckBox) ae
0834: .getSource();
0835: updateCheckerPrefs.putBoolean(
0836: "prompt-for-check-enabled",
0837: cb.isSelected());
0838: storePreferences();
0839: }
0840: });
0841: promptForCheckToggle.setSelected(updateCheckerPrefs
0842: .getBoolean("prompt-for-check-enabled",
0843: true));
0844:
0845: button = (Button) m_updateCheckerDialog
0846: .findComponent("DisableCheckingButton");
0847: button.addActionListener(new ActionListener() {
0848: public void actionPerformed(ActionEvent ae) {
0849: updateCheckerPrefs.putBoolean(
0850: "checking-enabled", false);
0851: storePreferences();
0852: m_updateCheckerDialog.setVisible(false);
0853: }
0854: });
0855:
0856: button = (Button) m_updateCheckerDialog
0857: .findComponent("CloseButton");
0858: button.addActionListener(new ActionListener() {
0859: public void actionPerformed(ActionEvent ae) {
0860: m_updateCheckerDialog.setVisible(false);
0861: }
0862: });
0863: Label lastCheckLabel = (Label) m_updateCheckerDialog
0864: .findComponent("LastCheckTimeLabel");
0865: long lastCheckTime = updateCheckerPrefs.getLong(
0866: "last-check-time", 0L);
0867: if (lastCheckTime != 0L) {
0868: lastCheckLabel.setText(formatBundleString(
0869: "update-checker.last.checked.msg",
0870: new Date(lastCheckTime)));
0871: }
0872: }
0873:
0874: m_updateCheckerDialog.pack();
0875: m_updateCheckerDialog.center(AdminClientPanel.this );
0876: m_updateCheckerDialog.setVisible(true);
0877: } else {
0878: UpdateCheckerAction.this .startUpdateCheck(false);
0879: }
0880: }
0881:
0882: ProductInfo getProductInfo() {
0883: if (m_productInfo == null) {
0884: m_productInfo = new ProductInfo();
0885: }
0886: return m_productInfo;
0887: }
0888:
0889: URL constructCheckURL() throws MalformedURLException {
0890: String defaultPropsUrl = "http://www.terracotta.org/kit/reflector?kitID=default&pageID=update.properties";
0891: String propsUrl = System.getProperty(
0892: "terracotta.update-checker.url", defaultPropsUrl);
0893: StringBuffer sb = new StringBuffer(propsUrl);
0894: ProductInfo productInfo = getProductInfo();
0895:
0896: sb.append(defaultPropsUrl.equals(propsUrl) ? '&' : '?');
0897:
0898: sb.append("id=");
0899: sb.append(URLEncoder.encode(Integer
0900: .toString(getIpAddress())));
0901: sb.append("&os-name=");
0902: sb.append(URLEncoder.encode(System.getProperty("os.name")));
0903: sb.append("&jvm-name=");
0904: sb.append(URLEncoder.encode(System
0905: .getProperty("java.vm.name")));
0906: sb.append("&jvm-version=");
0907: sb.append(URLEncoder.encode(System
0908: .getProperty("java.vm.version")));
0909: sb.append("&platform=");
0910: sb.append(URLEncoder.encode(System.getProperty("os.arch")));
0911: sb.append("&tc-version=");
0912: sb.append(URLEncoder.encode(productInfo.getVersion()));
0913: sb.append("&tc-product=");
0914: sb.append(productInfo.getLicense().equals(
0915: ProductInfo.DEFAULT_LICENSE) ? "oss" : "ee");
0916: sb.append("&source=console");
0917:
0918: return new URL(sb.toString());
0919: }
0920:
0921: private int getIpAddress() {
0922: try {
0923: return InetAddress.getLocalHost().hashCode();
0924: } catch (UnknownHostException uhe) {
0925: return 0;
0926: }
0927: }
0928:
0929: void showMessage(String msg) {
0930: TextArea textArea = new TextArea();
0931: textArea.setText(msg);
0932: textArea.setRows(8);
0933: textArea.setColumns(80);
0934: textArea.setEditable(false);
0935: ScrollPane scrollPane = new ScrollPane(textArea);
0936: JOptionPane.showMessageDialog(AdminClientPanel.this ,
0937: scrollPane,
0938: getBundleString("update-checker.action.title"),
0939: JOptionPane.INFORMATION_MESSAGE);
0940: }
0941:
0942: public Properties getResponseBody(URL url, HttpClient client)
0943: throws ConnectException, IOException {
0944: GetMethod get = new GetMethod(url.toString());
0945:
0946: get.setFollowRedirects(true);
0947: try {
0948: int status = client.executeMethod(get);
0949: if (status != HttpStatus.SC_OK) {
0950: throw new ConnectException(
0951: "The http client has encountered a status code other than ok for the url: "
0952: + url + " status: "
0953: + HttpStatus.getStatusText(status));
0954: }
0955: Properties props = new Properties();
0956: props.load(get.getResponseBodyAsStream());
0957: return props;
0958: } finally {
0959: get.releaseConnection();
0960: }
0961: }
0962:
0963: void startUpdateCheck(final boolean wasPrompted) {
0964: Thread t = new Thread() {
0965: public void run() {
0966: InputStream is = null;
0967: Object result = null;
0968: String versionNotice = null;
0969:
0970: try {
0971: StringBuffer sb = new StringBuffer();
0972: String version = getProductInfo().getVersion();
0973: if (version.indexOf('.') != -1) {
0974: URL url = constructCheckURL();
0975: HttpClient httpClient = new HttpClient();
0976: Properties props = getResponseBody(url,
0977: httpClient);
0978:
0979: String propVal = props
0980: .getProperty("general.notice");
0981: if (propVal != null
0982: && (propVal = propVal.trim()) != null
0983: && propVal.length() > 0) {
0984: showMessage(propVal);
0985: }
0986:
0987: propVal = props.getProperty(version
0988: + ".notice");
0989: if (propVal != null
0990: && (propVal = propVal.trim()) != null
0991: && propVal.length() > 0) {
0992: showMessage(propVal);
0993: }
0994: versionNotice = propVal;
0995:
0996: propVal = props.getProperty(version
0997: + ".updates");
0998: if (propVal != null
0999: && (propVal = propVal.trim()) != null
1000: && propVal.length() > 0) {
1001: sb.append("<ul>");
1002: StringTokenizer st = new StringTokenizer(
1003: propVal, ",");
1004: while (st.hasMoreElements()) {
1005: sb.append("<li>");
1006: String newVersion = st.nextToken();
1007: sb.append(newVersion);
1008:
1009: propVal = props
1010: .getProperty(newVersion
1011: + ".release-notes");
1012: if (propVal != null
1013: && (propVal = propVal
1014: .trim()) != null
1015: && propVal.length() > 0) {
1016: sb.append(" -- <a href=\"");
1017: sb.append(propVal);
1018: sb.append("\">");
1019: sb
1020: .append(getBundleString("update-checker.release-notes.label"));
1021: sb.append("</a>");
1022: }
1023: sb.append("</li>\n");
1024: }
1025: sb.append("</ol>");
1026: }
1027: }
1028: if (sb.length() > 0) {
1029: sb
1030: .insert(
1031: 0,
1032: "<html><body><p>"
1033: + getBundleString("update-checker.updates.available.msg")
1034: + "</p>");
1035: sb.append("</body></html>");
1036: TextPane textPane = new TextPane();
1037: textPane.setEditable(false);
1038: textPane.setContentType("text/html");
1039: textPane.setBackground(null);
1040: textPane.setText(sb.toString());
1041: textPane
1042: .addHyperlinkListener(new HyperlinkListener() {
1043: public void hyperlinkUpdate(
1044: HyperlinkEvent e) {
1045: if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
1046: Element elem = e
1047: .getSourceElement();
1048: AttributeSet a = elem
1049: .getAttributes();
1050: AttributeSet anchor = (AttributeSet) a
1051: .getAttribute(HTML.Tag.A);
1052: String action = (String) anchor
1053: .getAttribute(HTML.Attribute.HREF);
1054: BrowserLauncher
1055: .openURL(action);
1056: }
1057: }
1058: });
1059: result = textPane;
1060: } else if (wasPrompted && versionNotice == null) {
1061: result = getBundleString("update-checker.current.msg");
1062: }
1063: } catch (Exception e) {
1064: if (wasPrompted) {
1065: result = getBundleString("update-checker.connect.failed.msg");
1066: }
1067: } finally {
1068: IOUtils.closeQuietly(is);
1069: }
1070: UpdateCheckerAction.this .finishUpdateCheck(
1071: wasPrompted, result);
1072: }
1073: };
1074: t.start();
1075: }
1076:
1077: void finishUpdateCheck(final boolean wasPrompted,
1078: final Object msg) {
1079: if (msg != null) {
1080: SwingUtilities.invokeLater(new Runnable() {
1081: public void run() {
1082: JOptionPane
1083: .showMessageDialog(
1084: AdminClientPanel.this ,
1085: msg,
1086: getBundleString("update-checker.action.title"),
1087: JOptionPane.INFORMATION_MESSAGE);
1088: }
1089: });
1090: }
1091:
1092: final Preferences updateCheckerPrefs = getUpdateCheckerPrefs();
1093: updateCheckerPrefs.putLong("next-check-time",
1094: nextCheckTime());
1095: updateCheckerPrefs.putLong("last-check-time", System
1096: .currentTimeMillis());
1097: storePreferences();
1098: }
1099:
1100: long nextCheckTime() {
1101: long currentTime = System.currentTimeMillis();
1102: Long minutes = Long
1103: .getLong("terracotta.update-checker.next-check-minutes");
1104: long nextCheckTime;
1105:
1106: if (minutes != null) {
1107: nextCheckTime = currentTime
1108: + (minutes.longValue() * 60 * 1000);
1109: } else {
1110: nextCheckTime = currentTime
1111: + (14 * 24 * 60 * 60 * 1000);
1112: }
1113:
1114: return nextCheckTime;
1115: }
1116: }
1117:
1118: class VersionCheckControlAction extends XAbstractAction {
1119: VersionCheckControlAction() {
1120: super (getBundleString("version.check.enable.label"));
1121: }
1122:
1123: boolean isVersionCheckEnabled() {
1124: Preferences versionCheckPrefs = getPreferences().node(
1125: "version-check");
1126: return versionCheckPrefs.getBoolean("enabled", true);
1127: }
1128:
1129: void setVersionCheckEnabled(boolean checkEnabled) {
1130: Preferences versionCheckPrefs = getPreferences().node(
1131: "version-check");
1132: versionCheckPrefs.putBoolean("enabled", checkEnabled);
1133: m_versionCheckToggle.setSelected(checkEnabled);
1134: storePreferences();
1135: }
1136:
1137: public void actionPerformed(ActionEvent ae) {
1138: JCheckBoxMenuItem cbItem = (JCheckBoxMenuItem) ae
1139: .getSource();
1140: Preferences versionCheckPrefs = getPreferences().node(
1141: "version-check");
1142: versionCheckPrefs
1143: .putBoolean("enabled", cbItem.isSelected());
1144: storePreferences();
1145: }
1146: }
1147:
1148: class AboutAction extends XAbstractAction {
1149: Dialog m_aboutDialog;
1150:
1151: AboutAction() {
1152: super (getBundleString("about.action.label"));
1153: }
1154:
1155: public void actionPerformed(ActionEvent ae) {
1156: if (m_aboutDialog == null) {
1157: AdminClientContext acc = AdminClient.getContext();
1158:
1159: m_aboutDialog = new Dialog(getFrame(), true);
1160: m_aboutDialog.load((DialogResource) acc.topRes
1161: .child("AboutDialog"));
1162:
1163: AdminClientInfoPanel info;
1164: String title = getBundleString("title");
1165: info = (AdminClientInfoPanel) m_aboutDialog
1166: .findComponent("AdminClientInfoPanel");
1167: info.init(title, new ProductInfo());
1168: Label monikerLabel = (Label) m_aboutDialog
1169: .findComponent("MonikerLabel");
1170: monikerLabel.setText(title);
1171: Button okButton = (Button) m_aboutDialog
1172: .findComponent("OKButton");
1173: m_aboutDialog.getRootPane().setDefaultButton(okButton);
1174: okButton.addActionListener(new ActionListener() {
1175: public void actionPerformed(ActionEvent ae2) {
1176: m_aboutDialog.setVisible(false);
1177: }
1178: });
1179: }
1180:
1181: m_aboutDialog.pack();
1182: m_aboutDialog.center(AdminClientPanel.this );
1183: m_aboutDialog.setVisible(true);
1184: }
1185: }
1186:
1187: public void addServerLog(ConnectionContext cc) {
1188: ServerLog log = new ServerLog(cc);
1189: JScrollPane scroller = new JScrollPane(log);
1190: int index = m_bottomPane.getTabCount();
1191:
1192: m_bottomPane.addTab(cc.toString(), (Icon) null, scroller, null);
1193: m_bottomPane.setSelectedIndex(index);
1194:
1195: LogDocumentListener ldl = new LogDocumentListener(log);
1196: log.getDocument().addDocumentListener(ldl);
1197: m_logListeners.add(ldl);
1198: }
1199:
1200: public void removeServerLog(ConnectionContext cc) {
1201: JScrollPane scroller;
1202: ServerLog log;
1203: LogDocumentListener ldl;
1204:
1205: for (int i = 1; i < m_bottomPane.getTabCount(); i++) {
1206: scroller = (JScrollPane) m_bottomPane.getComponentAt(i);
1207: log = (ServerLog) scroller.getViewport().getView();
1208:
1209: if (cc.equals(log.getConnectionContext())) {
1210: ldl = (LogDocumentListener) m_logListeners.remove(i);
1211: log.getDocument().removeDocumentListener(ldl);
1212: m_bottomPane.removeTabAt(i);
1213:
1214: int index = m_bottomPane.getSelectedIndex();
1215: m_bottomPane.setIconAt(index, null);
1216:
1217: return;
1218: }
1219: }
1220: }
1221:
1222: class LogDocumentListener implements DocumentListener {
1223: JTextComponent textComponent;
1224:
1225: LogDocumentListener(JTextComponent textComponent) {
1226: this .textComponent = textComponent;
1227: }
1228:
1229: public void insertUpdate(DocumentEvent e) {
1230: int index = m_logListeners.indexOf(this );
1231:
1232: if (!textComponent.isShowing()
1233: && m_bottomPane.getIconAt(index) == null) {
1234: m_bottomPane.setIconAt(index, m_infoIcon);
1235: }
1236: }
1237:
1238: public void removeUpdate(DocumentEvent e) {/**/
1239: }
1240:
1241: public void changedUpdate(DocumentEvent e) {/**/
1242: }
1243: }
1244:
1245: public void block() {
1246: /**/
1247: }
1248:
1249: public void unblock() {
1250: /**/
1251: }
1252:
1253: public UndoManager getUndoManager() {
1254: if (m_undoManager == null) {
1255: m_undoManager = new MyUndoManager();
1256: }
1257:
1258: return m_undoManager;
1259: }
1260:
1261: class UndoAction extends XAbstractAction {
1262: public void actionPerformed(ActionEvent ae) {
1263: UndoManager undoMan = getUndoManager();
1264: UndoableEdit next = ((MyUndoManager) undoMan)
1265: .nextUndoable();
1266:
1267: if (next != null) {
1268: undoMan.undo();
1269: setStatus("Undid '" + next.getPresentationName() + "'");
1270: }
1271: }
1272:
1273: public boolean isEnabled() {
1274: return getUndoManager().canUndo();
1275: }
1276: }
1277:
1278: class RedoAction extends XAbstractAction {
1279: public void actionPerformed(ActionEvent ae) {
1280: UndoManager undoMan = getUndoManager();
1281: UndoableEdit next = ((MyUndoManager) undoMan)
1282: .nextRedoable();
1283:
1284: if (next != null) {
1285: undoMan.redo();
1286: setStatus("Redid '" + next.getPresentationName() + "'");
1287: }
1288: }
1289:
1290: public boolean isEnabled() {
1291: return getUndoManager().canRedo();
1292: }
1293: }
1294:
1295: class MyUndoManager extends UndoManager {
1296: public UndoableEdit nextUndoable() {
1297: return editToBeUndone();
1298: }
1299:
1300: public UndoableEdit nextRedoable() {
1301: return editToBeRedone();
1302: }
1303: }
1304:
1305: public String toString() {
1306: return getName();
1307: }
1308:
1309: protected void addEdit(UndoableEdit edit) {
1310: getUndoManager().addEdit(edit);
1311: }
1312: }
|