/* * Licensed 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 openbook.client; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Desktop; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.io.PrintStream; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JToolBar; import javax.swing.JTree; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import jpa.tools.swing.AttributeLegendView; import jpa.tools.swing.ConfigurationViewer; import jpa.tools.swing.ErrorDialog; import jpa.tools.swing.GraphicOutputStream; import jpa.tools.swing.MetamodelView; import jpa.tools.swing.PowerPointViewer; import jpa.tools.swing.PreparedQueryViewer; import jpa.tools.swing.ScrollingTextPane; import jpa.tools.swing.StatusBar; import jpa.tools.swing.SwingHelper; import openbook.domain.Customer; import openbook.server.OpenBookService; import openbook.server.ServiceFactory; import openbook.util.PropertyHelper; import org.apache.openjpa.conf.OpenJPAVersion; import org.apache.openjpa.lib.jdbc.JDBCListener; import org.apache.openjpa.persistence.OpenJPAPersistence; /** * A graphical user interface based client of OpenBooks for demonstration. * * @author Pinaki Poddar * */ @SuppressWarnings("serial") public class Demo extends JFrame { private static Dimension TAB_VIEW = new Dimension(800,600); private static Dimension OUT_VIEW = new Dimension(800,200); private static Dimension NAV_VIEW = new Dimension(400,800); /** * The actions invoked by this sample demonstration. */ private Action _root; private Action _about; private Action _buyBook; private Action _deliver; private Action _supply; private Action _viewConfig; private Action _viewDomain; private Action _viewSource; private Action _viewQuery; /** * The primary graphic widgets used to invoke and display the results of the actions. */ private JToolBar _toolBar; private JTree _navigator; private JTabbedPane _tabbedPane; private JTabbedPane _outputPane; private StatusBar _statusBar; private ScrollingTextPane _sqlLog; private SQLLogger _sqlListener; private SourceCodeBrowser _sourceBrowser; private static Demo _instance; private static final String SRC_ROOT = "source/"; /** * The handle to the service. */ private OpenBookService _service; private Customer _customer; private Map<String, Object> _config; /** * Runs the demo. */ public static void main(String[] args) throws Exception { SwingHelper.setLookAndFeel(14); adjustWidgetSize(); SwingUtilities.invokeLater(new Runnable() { public void run() { Demo demo = Demo.getInstance(); demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); demo.pack(); SwingHelper.position(demo, null); demo.setVisible(true); } }); } public synchronized static Demo getInstance() { if (_instance == null) { _instance = new Demo(); } return _instance; } static void adjustWidgetSize() { Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); int sw = (int)(95*screen.getWidth()/100); int sh = (int)(80*screen.getHeight()/100); NAV_VIEW = new Dimension(25*sw/100, sh); TAB_VIEW = new Dimension(75*sw/100, 65*sh/100); OUT_VIEW = new Dimension(75*sw/100, 35*sh/100); } private Demo() { Thread.currentThread().setUncaughtExceptionHandler(new ErrorHandler()); _config = PropertyHelper.load(System.getProperty("openbook.client.config", "demo.properties")); setIconImage(((ImageIcon)Images.LOGO_OPENBOOKS).getImage()); setTitle("OpenBooks: A Sample JPA 2.0 Application"); _root = new WelcomeAction("OpenBooks", Images.LOGO_OPENBOOKS, "OpenBooks"); _about = new AboutAction("About OpenBooks", Images.LOGO_OPENBOOKS, "About OpenBooks"); _buyBook = new BuyBookAction("Buy", Images.BUY, "Browse and Buy Books"); _deliver = new DeliveryAction("Deliver", Images.DELIVERY, "Deliver Pending Orders"); _supply = new SupplyAction("Supply", Images.SUPPLY, "Supply Books"); _viewConfig = new ViewConfigAction("Unit", Images.VIEW_UNIT, "View Configuration"); _viewDomain = new ViewDomainAction("Domain", Images.VIEW_DOMAIN, "View Domain Model"); _viewSource = new ViewSourceAction("Source", Images.VIEW_CODE, "View Source Code"); _viewQuery = new ViewQueryCacheAction("Query", Images.VIEW_QUERY, "View Queries"); _toolBar = createToolBar(); _navigator = createNavigator(); _tabbedPane = createTabbedView(); _outputPane = createOutputView(); _statusBar = createStatusBar(); JSplitPane horizontalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); horizontalSplitPane.setContinuousLayout(true); horizontalSplitPane.setDividerSize(5); JScrollPane scrollPane = new JScrollPane(_navigator); scrollPane.setMinimumSize(new Dimension(NAV_VIEW.width/4, NAV_VIEW.height)); scrollPane.setPreferredSize(NAV_VIEW); horizontalSplitPane.add(scrollPane); JSplitPane verticalSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); verticalSplitPane.setContinuousLayout(true); verticalSplitPane.setDividerSize(5); verticalSplitPane.add(_tabbedPane); verticalSplitPane.add(_outputPane); horizontalSplitPane.add(verticalSplitPane); Container content = getContentPane(); content.add(_toolBar, BorderLayout.PAGE_START); content.add(horizontalSplitPane, BorderLayout.CENTER); content.add(_statusBar, BorderLayout.SOUTH); _root.actionPerformed(null); } /** * Gets the handle to OpenBooks service. */ public OpenBookService getService() { if (_service == null) { final String unitName = getConfiguration("openbook.unit", OpenBookService.DEFAULT_UNIT_NAME); SwingWorker<OpenBookService, Void> getService = new SwingWorker<OpenBookService, Void> () { @Override protected OpenBookService doInBackground() throws Exception { Map<String, Object> runtimeConfig = new HashMap<String, Object>(); runtimeConfig.put("openjpa.jdbc.JDBCListeners", new JDBCListener[]{_sqlListener}); OpenBookService service = ServiceFactory.getService(unitName, runtimeConfig); service.initialize(null); return service; } }; getService.execute(); try { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); _service = getService.get(10, TimeUnit.SECONDS); } catch (Exception t) { new ErrorDialog(t).setVisible(true); } finally { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } return _service; } public Customer getCustomer() { if (_customer == null) { SwingWorker<Customer, Void> task = new SwingWorker<Customer, Void> () { @Override protected Customer doInBackground() throws Exception { return getService().login("guest"); } }; task.execute(); try { _customer = task.get(1, TimeUnit.SECONDS); } catch (Exception t) { new ErrorDialog(t).setVisible(true); } } return _customer; } private JToolBar createToolBar() { JToolBar toolBar = new JToolBar(); toolBar.add(_buyBook); toolBar.add(_deliver); toolBar.add(_supply); Dimension d = new Dimension(40, 32); toolBar.addSeparator(d); toolBar.add(_viewConfig); toolBar.add(_viewDomain); toolBar.add(_viewSource); toolBar.add(_viewQuery); toolBar.addSeparator(d); toolBar.add(Box.createHorizontalGlue()); toolBar.add(_about); toolBar.add(Box.createHorizontalStrut(2)); return toolBar; } private StatusBar createStatusBar() { return new StatusBar(); } public String getConfiguration(String key, String def) { return PropertyHelper.getString(_config, key, def); } private SourceCodeBrowser getSourceCodeBrowser() { if (_sourceBrowser == null) { String root = getConfiguration("openbook.source.root", SRC_ROOT); boolean external = "true".equalsIgnoreCase( getConfiguration("openbook.source.browser.external", "false")) && Desktop.isDesktopSupported(); _sourceBrowser = new SourceCodeBrowser(root, false); if (!external) { Map<String,String> initialPages = PropertyHelper.getMap(_config, "openbook.source.list"); for (Map.Entry<String, String> entry : initialPages.entrySet()) { _sourceBrowser.addPage(entry.getKey(), entry.getValue()); } showTab(_tabbedPane, "Source Code", _sourceBrowser.getViewer()); } } return _sourceBrowser; } /** * Abstract root of all Action objects helps to locate/configure visual action parameters such as * tooltip text or image. * * @author Pinaki Poddar * */ public abstract class OpenBookAction extends AbstractAction { public OpenBookAction(String name, Icon icon, String tooltip) { putValue(Action.NAME, name); putValue(Action.SHORT_DESCRIPTION, tooltip); putValue(Action.SMALL_ICON, icon); } } public class BuyBookAction extends OpenBookAction { BuyBookPage _buyBookPage; public BuyBookAction(String name, Icon icon, String tooltip) { super(name, icon, tooltip); } public void actionPerformed(ActionEvent e) { if (_buyBookPage == null) { _buyBookPage = new BuyBookPage(getService(), getCustomer()); } showTab(_tabbedPane, "Buy Books", _buyBookPage); switchTab(_outputPane, _sqlLog); } } public class DeliveryAction extends OpenBookAction { DeliveryPage _deliveryPage; public DeliveryAction(String name, Icon icon, String tooltip) { super(name, icon, tooltip); } public void actionPerformed(ActionEvent e) { if (_deliveryPage == null) { _deliveryPage = new DeliveryPage(getService()); } showTab(_tabbedPane, "Deliver Books", _deliveryPage); switchTab(_outputPane, _sqlLog); } } public class SupplyAction extends OpenBookAction { SupplyPage _supplyPage; public SupplyAction(String name, Icon icon, String tooltip) { super(name, icon, tooltip); } public void actionPerformed(ActionEvent e) { if (_supplyPage == null) { _supplyPage = new SupplyPage(getService()); } showTab(_tabbedPane, "Supply Books", _supplyPage); switchTab(_outputPane, _sqlLog); } } public class ViewConfigAction extends OpenBookAction { ConfigurationViewer _configView; public ViewConfigAction(String name, Icon icon, String tooltip) { super(name, icon, tooltip); } public void actionPerformed(ActionEvent e) { if (_configView == null) { _configView = new ConfigurationViewer("Unit Configuration", getService().getUnit().getProperties()); showTab(_tabbedPane, "Configuration", new JScrollPane(_configView)); } else { showTab(_tabbedPane, "Configuration", _configView); } } } public class ViewDomainAction extends OpenBookAction { MetamodelView _domainView; AttributeLegendView _legends; public ViewDomainAction(String name, Icon icon, String tooltip) { super(name, icon, tooltip); } public void actionPerformed(ActionEvent e) { if (_domainView == null) { _domainView = new MetamodelView(getService().getUnit().getMetamodel()); _legends = new AttributeLegendView(); showTab(_outputPane, "Legends", new JScrollPane(_legends)); } showTab(_tabbedPane, "Domain Model", _domainView); } } public class ViewDataAction extends OpenBookAction { public ViewDataAction(String name, Icon icon, String tooltip) { super(name, icon, tooltip); } public void actionPerformed(ActionEvent e) { showTab(_tabbedPane, "Buy Books", null); } } public class ViewQueryCacheAction extends OpenBookAction { PreparedQueryViewer _queryView; public ViewQueryCacheAction(String name, Icon icon, String tooltip) { super(name, icon, tooltip); } public void actionPerformed(ActionEvent e) { if (_queryView == null) { _queryView = new PreparedQueryViewer(OpenJPAPersistence.cast(getService().getUnit())); showTab(_tabbedPane, "JPQL Query", new JScrollPane(_queryView)); } showTab(_tabbedPane, "JPQL Queries", _queryView); } } public class ViewSourceAction extends OpenBookAction { public ViewSourceAction(String name, Icon icon, String tooltip) { super(name, icon, tooltip); } public void actionPerformed(ActionEvent e) { getSourceCodeBrowser(); } } /** * An action to show a piece of code in an internal or external browser. * */ public class ShowCodeAction extends OpenBookAction { private String _key; private String _page; public ShowCodeAction() { super("View Code", Images.VIEW_CODE, "View Java Source Code"); } public void setPage(String key, String page) { _key = key; _page = page; } public void actionPerformed(ActionEvent e) { getSourceCodeBrowser().showPage(_key, _page); } } /** * Displays the "welcome" page. * * @author Pinaki Poddar * */ public class WelcomeAction extends OpenBookAction { PowerPointViewer _powerpoint; JLabel _logoLabel = new JLabel(Images.LOGO_OPENBOOKS); boolean _showPresentation = true; public WelcomeAction(String name, Icon icon, String tooltip) { super(name, icon, tooltip); } public void actionPerformed(ActionEvent e) { if (_powerpoint == null && _showPresentation) { String dir = getConfiguration("openbook.slides.dir", "slides/"); String[] defaultSlides = { "Slide1.JPG", "Slide2.JPG", "Slide3.JPG", "Slide4.JPG", "Slide5.JPG", "Slide6.JPG", "Slide7.JPG", "Slide8.JPG", "Slide9.JPG", "Slide10.JPG", "Slide11.JPG", "Slide12.JPG", "Slide13.JPG", "Slide14.JPG", "Slide15.JPG"}; List<String> slides = PropertyHelper.getStringList(_config, "openbook.slides.list", Arrays.asList(defaultSlides)); try { _powerpoint = new PowerPointViewer(dir, slides); } catch (Exception e1) { _showPresentation = false; System.err.println("Error while opening slide deck at " + dir + ". \r\n"+ e1); } } showTab(_tabbedPane, "Home", _powerpoint != null ? _powerpoint : _logoLabel); } } public class AboutAction extends OpenBookAction { AboutDialog _dialog; public AboutAction(String name, Icon icon, String tooltip) { super(name, icon, tooltip); } public void actionPerformed(ActionEvent e) { if (_dialog == null) { _dialog = new AboutDialog(Images.LOGO_OPENBOOKS); SwingHelper.position(_dialog, Demo.this); } _dialog.setVisible(true); } } /** * Show the given tab in the given pane. * @param pane the tabbed pane * @param title title of the tab component * @param tab the component to show */ void showTab(JTabbedPane pane, String title, Component tab) { if (tab == null) return; Component c = locateTab(pane, tab); if (c == null) { pane.addTab(title, tab); pane.setSelectedComponent(tab); } else { pane.setSelectedComponent(c); } } void switchTab(JTabbedPane pane, Component tab) { if (tab == null) return; Component c = locateTab(pane, tab); if (c == null) { pane.setSelectedComponent(c); } } Component locateTab(JTabbedPane pane, Component tab) { int index = pane.indexOfComponent(tab); if (index == -1) { Component[] components = pane.getComponents(); for (int i = 0; i < components.length; i++) { if (components[i] instanceof JScrollPane && (((JScrollPane)components[i]).getViewport().getView() == tab)) { return components[i]; } } } else { return pane.getComponentAt(index); } return null; } private JTabbedPane createTabbedView() { JTabbedPane pane = new JTabbedPane(); pane.setPreferredSize(TAB_VIEW); pane.setMinimumSize(new Dimension(TAB_VIEW.width, TAB_VIEW.height)); return pane; } private JTabbedPane createOutputView() { JTabbedPane pane = new JTabbedPane(); pane.setPreferredSize(OUT_VIEW); _sqlListener = new SQLLogger(); _sqlLog = new ScrollingTextPane(); GraphicOutputStream stream = new GraphicOutputStream(_sqlLog); _sqlLog.setPreferredSize(TAB_VIEW); _sqlListener.setOutput(stream); pane.addTab("SQL Log", new JScrollPane(_sqlLog)); ScrollingTextPane consoleLog = new ScrollingTextPane(); GraphicOutputStream console = new GraphicOutputStream(consoleLog); System.setErr(new PrintStream(console, true)); pane.addTab("Console", new JScrollPane(consoleLog)); return pane; } /** * Creates the navigation tree and adds the tree nodes. Each tree node is attached with an action * that fires when the node is selected. */ private JTree createNavigator() { ActionTreeNode root = new ActionTreeNode(_root); DefaultMutableTreeNode app = new DefaultMutableTreeNode("WorkFlows"); DefaultMutableTreeNode views = new DefaultMutableTreeNode("Views"); root.add(app); root.add(views); app.add(new ActionTreeNode(_buyBook)); app.add(new ActionTreeNode(_deliver)); app.add(new ActionTreeNode(_supply)); views.add(new ActionTreeNode(_viewConfig)); views.add(new ActionTreeNode(_viewDomain)); views.add(new ActionTreeNode(_viewQuery)); views.add(new ActionTreeNode(_viewSource)); JTree tree = new JTree(root); tree.setShowsRootHandles(true); tree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { Object treeNode = _navigator.getLastSelectedPathComponent(); if (treeNode instanceof ActionTreeNode) { ((ActionTreeNode)treeNode)._action.actionPerformed(null); } } }); tree.setCellRenderer(new TypedTreeCellRenderer()); for (int i = 0; i < tree.getRowCount(); i++) { tree.expandRow(i); } return tree; } /** * A tree node which may have an associated action. * * @author Pinaki Poddar * */ public static class ActionTreeNode extends DefaultMutableTreeNode { private final Action _action; public ActionTreeNode(Action action) { _action = action; } public String toString() { return _action.getValue(Action.SHORT_DESCRIPTION).toString(); } } public class TypedTreeCellRenderer extends DefaultTreeCellRenderer { @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); } } public static class AboutDialog extends JDialog { public AboutDialog(Icon logo) { setModal(true); setLayout(new BorderLayout()); JButton button = new JButton("<html>" + "<b>OpenBooks</b> " + "<p>" + "<br> by JPA Team, SWG" + "<br> IBM Corporation" + "<p>" + "</html>"); button.setIcon(logo); button.setHorizontalTextPosition(SwingConstants.RIGHT); button.setIconTextGap(4); button.setEnabled(true); button.setBorderPainted(false); JLabel openJPALogo = new JLabel(Images.LOGO_OPENJPA); String version = "Version " + OpenJPAVersion.MAJOR_RELEASE + "." + OpenJPAVersion.MINOR_RELEASE; openJPALogo.setBorder(BorderFactory.createTitledBorder(version)); openJPALogo.setBackground(Color.BLACK); add(button, BorderLayout.CENTER); add(openJPALogo, BorderLayout.SOUTH); setTitle("About OpenBooks"); setAlwaysOnTop(true); setResizable(false); pack(); } } }