/******************************************************************************* * Copyright (c) 2010, 2012 AGETO Service GmbH and others. * All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html. * * Contributors: * Mike Tschierschke - initial API and implementation * Gunnar Wagenknecht - rework to new console look (based on RAP Examples) *******************************************************************************/ package org.eclipse.gyrex.admin.ui.internal.application; import java.util.HashMap; import java.util.Map; import org.eclipse.gyrex.admin.ui.internal.AdminUiActivator; import org.eclipse.gyrex.admin.ui.internal.pages.registry.AdminPageRegistry; import org.eclipse.gyrex.admin.ui.internal.pages.registry.PageContribution; import org.eclipse.gyrex.admin.ui.pages.AdminPage; import org.eclipse.gyrex.admin.ui.pages.FilteredAdminPage; import org.eclipse.gyrex.admin.ui.pages.IAdminUi; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.util.ILogger; import org.eclipse.jface.util.Policy; import org.eclipse.jface.util.StatusHandler; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.application.EntryPoint; import org.eclipse.rap.rwt.client.service.BrowserNavigation; import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent; import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.osgi.framework.Version; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class controls all aspects of the application's execution and is * contributed through the plugin.xml. */ public class AdminApplication implements EntryPoint, IAdminUi { private static final String HISTORY_TOKEN_SEPARATOR = "--"; private static final String GYREX_WEBSITE_URL = "http://eclipse.org/gyrex/"; private static final int CONTENT_MIN_HEIGHT = 800; private static final int CENTER_AREA_WIDTH = 998; private static final Logger LOG = LoggerFactory.getLogger(AdminApplication.class); private static Label createHeadlineLabel(final Composite parent, final String text) { final Label label = new Label(parent, SWT.NONE); label.setText(text.replace("&", "&&")); label.setData(RWT.CUSTOM_VARIANT, "pageHeadline"); return label; } private static FormData createLogoFormData(final Image logo) { final FormData data = new FormData(); data.left = new FormAttachment(0); data.top = new FormAttachment(0); return data; } private static FormData createNavBarFormData() { final FormData data = new FormData(); data.bottom = new FormAttachment(100, 5); data.right = new FormAttachment(100, 0); return data; } public static Image getImage(final Display display, final String path) { final ImageDescriptor imageDescriptor = AdminUiActivator.getImageDescriptor("img/" + path); return imageDescriptor.createImage(display); } private static String getVersion() { final Version version = AdminUiActivator.getInstance().getBundleVersion(); final StringBuilder resultBuffer = new StringBuilder(60); final String buildId = AdminUiActivator.getInstance().getBundle().getBundleContext().getProperty("gyrex.buildId"); if (StringUtils.isNotBlank(buildId)) { resultBuffer.append("(Gyrex ").append(buildId); final String buildTimestamp = AdminUiActivator.getInstance().getBundle().getBundleContext().getProperty("gyrex.buildTimestamp"); if (StringUtils.isNotBlank(buildTimestamp)) { resultBuffer.append(" Build ").append(buildTimestamp); resultBuffer.append(buildTimestamp); } resultBuffer.append(')'); } else if (StringUtils.equals(version.getQualifier(), "qualifier")) { // running out of the IDE resultBuffer.append("(Developer Build)"); } else { // don't append any version info at all } return resultBuffer.toString(); } private static void makeLink(final Label control, final String url) { control.setCursor(control.getDisplay().getSystemCursor(SWT.CURSOR_HAND)); control.addMouseListener(new MouseAdapter() { /** serialVersionUID */ private static final long serialVersionUID = 1L; @Override public void mouseDown(final MouseEvent e) { final JavaScriptExecutor service = RWT.getClient().getService(JavaScriptExecutor.class); if (service != null) { service.execute("window.location.href = '" + url + "'"); } } }); } private Composite centerArea; private NavigationBar navigation; private Composite navBar; private final Map<String, AdminPage> pagesById = new HashMap<String, AdminPage>(); private AdminPage currentPage; private Image logo; private Composite filterContainer; private Composite headerCenterArea; private void activate(final AdminPage page, final PageContribution contribution, String[] args) { // TODO: should switch to using a StackLayout and not disposing children every time // however, disposal might be necessary if input changes for (final Control child : centerArea.getChildren()) { child.dispose(); } for (final Control child : filterContainer.getChildren()) { child.dispose(); } // initialize arguments (allows safe use within this method as well as for API contract) if ((args == null) || (args.length == 0)) { args = new String[] { contribution.getId() }; } // update page with input page.setArguments(args); // create history entry final String historyText = StringUtils.isNotBlank(page.getTitleToolTip()) ? String.format("%s - %s - Gyrex Admin", contribution.getName(), page.getTitleToolTip()) : String.format("%s - Gyrex Admin", contribution.getName()); final BrowserNavigation browserNavigation = RWT.getClient().getService(BrowserNavigation.class); if (browserNavigation != null) { browserNavigation.pushState(StringUtils.join(args, HISTORY_TOKEN_SEPARATOR), historyText); } // create page createPage(page, contribution, centerArea); // re-layout headerCenterArea.layout(true, true); centerArea.layout(true, true); // activate page.activate(); } private void attachHistoryListener() { final BrowserNavigation browserNavigation = RWT.getClient().getService(BrowserNavigation.class); if (browserNavigation != null) { browserNavigation.addBrowserNavigationListener(new BrowserNavigationListener() { private static final long serialVersionUID = 1L; @Override public void navigated(final BrowserNavigationEvent event) { final String[] tokens = StringUtils.splitByWholeSeparator(event.getState(), HISTORY_TOKEN_SEPARATOR); final PageContribution contribution = AdminPageRegistry.getInstance().getPage(tokens[0]); if (contribution != null) { openPage(contribution, tokens); } } }); } } private Composite createCenterArea(final Composite parent, final Control topControl, final Control bottomControl) { final Composite centerArea = new Composite(parent, SWT.NONE); centerArea.setLayout(new FillLayout()); centerArea.setLayoutData(createCenterAreaFormData(topControl, bottomControl)); centerArea.setData(RWT.CUSTOM_VARIANT, "centerArea"); return centerArea; } private FormData createCenterAreaFormData(final Control topAttachment, final Control bottomAttachment) { final FormData data = new FormData(); data.top = new FormAttachment(topAttachment, 0, SWT.BOTTOM); data.bottom = new FormAttachment(bottomAttachment, -10, SWT.TOP); data.left = new FormAttachment(50, (-CENTER_AREA_WIDTH / 2) + 10); data.width = CENTER_AREA_WIDTH - 10; return data; } private Composite createContent(final ScrolledComposite scrolledArea) { logo = getImage(scrolledArea.getDisplay(), "gyrex/gyrex-juno.png"); final Composite comp = new Composite(scrolledArea, SWT.NONE); comp.setLayout(new FormLayout()); final Composite header = createHeader(comp); header.setLayoutData(createHeaderFormData(logo.getBounds().height)); createContentBody(comp, header); return comp; } private void createContentBody(final Composite parent, final Composite header) { // FIXME: the separator is a hack to work around missing "border-top" (or -bottom) in RAP (bug 283872) final Composite separator = new Composite(parent, SWT.NONE); separator.setData(RWT.CUSTOM_VARIANT, "mainContentAreaHeaderSeparator"); final FormData data = new FormData(); data.top = new FormAttachment(header, 0); data.left = new FormAttachment(0, 0); data.right = new FormAttachment(100, 0); data.height = 2; separator.setLayoutData(data); final Composite composite = new Composite(parent, SWT.NONE); composite.setData(RWT.CUSTOM_VARIANT, "mainContentArea"); composite.setLayout(new FormLayout()); composite.setLayoutData(createContentBodyFormData(separator)); final Composite footer = createFooter(composite); centerArea = createCenterArea(composite, separator, footer); } private FormData createContentBodyFormData(final Control topControlToAttachTo) { final FormData data = new FormData(); data.top = new FormAttachment(topControlToAttachTo, 0); data.left = new FormAttachment(0, 0); data.right = new FormAttachment(100, 0); data.bottom = new FormAttachment(100, 0); return data; } private Composite createFilterContainer(final Composite parent, final Control left) { final Composite filterContainer = new Composite(parent, SWT.NONE); filterContainer.setData(RWT.CUSTOM_VARIANT, "filter-container"); final FormData data = new FormData(); data.bottom = new FormAttachment(100, 5); data.left = new FormAttachment(left, 5); filterContainer.setLayoutData(data); final GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.horizontalSpacing = 0; layout.verticalSpacing = 0; filterContainer.setLayout(layout); return filterContainer; } private Composite createFooter(final Composite contentComposite) { final Composite footer = new Composite(contentComposite, SWT.NONE); footer.setLayout(new FormLayout()); footer.setData(RWT.CUSTOM_VARIANT, "footer"); footer.setLayoutData(createFooterFormData()); footer.setBackgroundMode(SWT.INHERIT_FORCE); final Label label = new Label(footer, SWT.NONE); label.setData(RWT.CUSTOM_VARIANT, "footerLabel"); label.setText("Admin Console " + getVersion()); label.setLayoutData(createFooterLabelFormData(footer)); return footer; } private FormData createFooterFormData() { final FormData data = new FormData(); data.left = new FormAttachment(50, -CENTER_AREA_WIDTH / 2); data.top = new FormAttachment(100, -40); data.bottom = new FormAttachment(100); data.width = CENTER_AREA_WIDTH - 10 - 2; return data; } private FormData createFooterLabelFormData(final Composite footer) { final FormData data = new FormData(); data.top = new FormAttachment(50, -10); data.right = new FormAttachment(100, -15); return data; } private Composite createHeader(final Composite parent) { final Composite comp = new Composite(parent, SWT.NONE); comp.setData(RWT.CUSTOM_VARIANT, "header"); comp.setBackgroundMode(SWT.INHERIT_DEFAULT); comp.setLayout(new FormLayout()); headerCenterArea = createHeaderCenterArea(comp); final Label logoLabel = new Label(headerCenterArea, SWT.NONE); logoLabel.setImage(logo); logoLabel.setLayoutData(createLogoFormData(logo)); makeLink(logoLabel, GYREX_WEBSITE_URL); // final Label title = new Label(headerCenterArea, SWT.NONE); // title.setText("Admin Console"); // title.setData(RWT.CUSTOM_VARIANT, "title"); // final FormData titleFormData = new FormData(); // titleFormData.bottom = new FormAttachment(100, -18); // titleFormData.left = new FormAttachment(logo, 0); // title.setLayoutData(titleFormData); filterContainer = createFilterContainer(headerCenterArea, logoLabel); navigation = createNavigation(headerCenterArea); return comp; } private Composite createHeaderCenterArea(final Composite parent) { final Composite headerCenterArea = new Composite(parent, SWT.NONE); headerCenterArea.setLayout(new FormLayout()); headerCenterArea.setLayoutData(createHeaderCenterAreaFormData()); return headerCenterArea; } private FormData createHeaderCenterAreaFormData() { final FormData data = new FormData(); data.left = new FormAttachment(50, -CENTER_AREA_WIDTH / 2); data.top = new FormAttachment(0); data.bottom = new FormAttachment(100); data.width = CENTER_AREA_WIDTH; return data; } private FormData createHeaderFormData(final int height) { final FormData data = new FormData(); data.top = new FormAttachment(0); data.left = new FormAttachment(0); data.right = new FormAttachment(100); data.height = height; return data; } private Shell createMainShell(final Display display) { final Shell shell = new Shell(display, SWT.NO_TRIM); shell.setMaximized(true); shell.setData(RWT.CUSTOM_VARIANT, "mainshell"); return shell; } private NavigationBar createNavigation(final Composite parent) { navBar = new Composite(parent, SWT.NONE); navBar.setLayoutData(createNavBarFormData()); navBar.setData(RWT.CUSTOM_VARIANT, "nav-bar"); final GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.horizontalSpacing = 0; layout.verticalSpacing = 0; navBar.setLayout(layout); final NavigationBar navigation = new NavigationBar(navBar) { /** serialVersionUID */ private static final long serialVersionUID = 1L; @Override protected void openPage(final PageContribution page) { AdminApplication.this.openPage(page, null); } }; return navigation; } private void createPage(final AdminPage page, final PageContribution contribution, final Composite parent) { final Composite pageComp = new Composite(parent, SWT.NONE); pageComp.setLayout(AdminUiUtil.createGridLayoutWithoutMargin(1, false)); if (page instanceof FilteredAdminPage) { ((FilteredAdminPage) page).createFilterControls(filterContainer); } String title = page.getTitle(); if (StringUtils.isBlank(title)) { title = contribution.getName(); } if (StringUtils.isNotBlank(title)) { final Label label = createHeadlineLabel(pageComp, page.getTitle()); final GridData layoutData = new GridData(); layoutData.verticalIndent = 30; // layoutData.horizontalIndent = DEFAULT_SPACE; label.setLayoutData(layoutData); } final Composite contentComp = new Composite(pageComp, SWT.NONE); contentComp.setLayoutData(AdminUiUtil.createFillData()); contentComp.setLayout(new FillLayout()); page.createControl(contentComp); // sanity check for (final Control child : contentComp.getChildren()) { if (null != child.getLayoutData()) { LOG.warn("Programming error in page {}: child composites ({}) should not make any assumptions about the parent layout!", contribution.getId(), child); child.setLayoutData(null); } } } private ScrolledComposite createScrolledArea(final Composite parent) { final ScrolledComposite scrolledComp = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL); scrolledComp.setMinHeight(CONTENT_MIN_HEIGHT); scrolledComp.setMinWidth(CENTER_AREA_WIDTH); scrolledComp.setExpandVertical(true); scrolledComp.setExpandHorizontal(true); return scrolledComp; } @Override public int createUI() { final Display display = new Display(); setupJFacePolicy(); final Shell shell = createMainShell(display); shell.setLayout(new FillLayout()); final ScrolledComposite scrolledArea = createScrolledArea(shell); final Composite content = createContent(scrolledArea); scrolledArea.setContent(content); attachHistoryListener(); shell.open(); openInitialPage(); display.disposeExec(new Runnable() { @Override public void run() { if (null != currentPage) { deactivate(currentPage); } } }); return 0; } private void deactivate(final AdminPage page) { page.deactivate(); } private AdminPage getPage(final PageContribution contribution) throws CoreException { if (!pagesById.containsKey(contribution.getId())) { final AdminPage page = contribution.createPage(); page.setAdminUi(this); pagesById.put(contribution.getId(), page); } return pagesById.get(contribution.getId()); } private void openInitialPage() { final PageContribution contribution = navigation.findInitialPage(); if (contribution != null) { openPage(contribution, null); } } private void openPage(final PageContribution contribution, final String[] args) { try { final AdminPage page = getPage(contribution); if (null == page) { Policy.getStatusHandler().show(new Status(IStatus.ERROR, AdminUiActivator.SYMBOLIC_NAME, String.format("Page '%s' not found!", contribution.getId())), "Error Opening Page"); return; } if (page == currentPage) // don't do anything if it's the same page return; else if (null != currentPage) { // deactivate old page first deactivate(currentPage); } currentPage = page; navigation.selectNavigationEntry(contribution); activate(page, contribution, args); } catch (final Exception | LinkageError | AssertionError e) { Policy.getStatusHandler().show(e instanceof CoreException ? ((CoreException) e).getStatus() : new Status(IStatus.ERROR, AdminUiActivator.SYMBOLIC_NAME, String.format("Unable to open page '%s (id %s)'. Please check the server logs.", contribution.getName(), contribution.getId()), e), "Error Opening Page"); } } @Override public void openPage(final String pageId, final String... args) { if (StringUtils.isBlank(pageId)) throw new IllegalArgumentException("invalid page id"); final PageContribution contribution = AdminPageRegistry.getInstance().getPage(pageId); if (contribution == null) return; final String[] argsWithPageId; if (args != null) { argsWithPageId = new String[args.length + 1]; argsWithPageId[0] = pageId; System.arraycopy(args, 0, argsWithPageId, 1, args.length); } else { argsWithPageId = new String[] { pageId }; } openPage(contribution, argsWithPageId); } private void setupJFacePolicy() { Policy.setLog(new ILogger() { @Override public void log(final IStatus status) { if (status.matches(IStatus.CANCEL) || status.matches(IStatus.ERROR)) { LOG.error(status.getMessage(), status.getException()); } else if (status.matches(IStatus.WARNING)) { LOG.warn(status.getMessage(), status.getException()); } else { LOG.info(status.getMessage(), status.getException()); } } }); final StatusHandler defaultStatusHandler = Policy.getStatusHandler(); Policy.setStatusHandler(new StatusHandler() { @Override public void show(final IStatus status, final String title) { Policy.getLog().log(status); defaultStatusHandler.show(status, title); } }); } }