Java tutorial
/* * Copyright 2014 Corpuslinguistic working group Humboldt University Berlin. * * 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 annis.gui; import static annis.gui.SidebarState.AUTO_HIDDEN; import static annis.gui.SidebarState.AUTO_VISIBLE; import static annis.gui.SidebarState.HIDDEN; import static annis.gui.SidebarState.VISIBLE; import annis.gui.components.ScreenshotMaker; import annis.gui.components.SettingsStorage; import annis.libgui.AnnisBaseUI; import static annis.libgui.AnnisBaseUI.USER_LOGIN_ERROR; import annis.libgui.AnnisUser; import annis.libgui.Background; import annis.libgui.Helper; import annis.libgui.IDGenerator; import annis.libgui.LoginDataLostException; import annis.security.User; import com.google.common.eventbus.Subscribe; import com.sun.jersey.api.client.UniformInterfaceException; import com.vaadin.data.validator.EmailValidator; import com.vaadin.server.FontAwesome; import com.vaadin.server.Resource; import com.vaadin.server.ThemeResource; import com.vaadin.server.VaadinSession; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.JavaScript; import com.vaadin.ui.JavaScriptFunction; import com.vaadin.ui.Label; import com.vaadin.ui.Notification; import com.vaadin.ui.UI; import com.vaadin.ui.Window; import com.vaadin.ui.themes.BaseTheme; import com.vaadin.ui.themes.ValoTheme; import elemental.json.JsonArray; import java.util.LinkedHashSet; import org.json.JSONException; import org.slf4j.LoggerFactory; /** * The ANNIS main toolbar. Handles login, showing the sidebar (if it exists), * the screenshot making and some information windows. * * @author Thomas Krause <krauseto@hu-berlin.de> */ public class MainToolbar extends HorizontalLayout implements LoginListener, ScreenshotMaker.ScreenshotCallback, SettingsStorage.LoadedListener { private static final org.slf4j.Logger log = LoggerFactory.getLogger(MainToolbar.class); public enum NavigationTarget { SEARCH(SearchView.NAME, "Search interface", FontAwesome.SEARCH), ADMIN(AdminView.NAME, "Administration", FontAwesome.WRENCH); private final String caption; private final String state; private final Resource icon; private NavigationTarget(String state, String caption, Resource icon) { this.caption = caption; this.state = state; this.icon = icon; } } private Button btSidebar; private final Button btNavigate; private NavigationTarget navigationTarget; private final Button btLogin; private final Button btLogout; private final Button btBugReport; private final Button btAboutAnnis; private final Button btOpenSource; private final Label lblUserName; private final String bugEMailAddress; private final LoginWindow windowLogin = new LoginWindow(); private SidebarState sidebarState = SidebarState.VISIBLE; private final LinkedHashSet<LoginListener> loginListeners = new LinkedHashSet<>(); private Throwable lastBugReportCause; private Sidebar sidebar; private ScreenshotMaker screenshotExtension; public static final String BUG_MAIL_KEY = "bug-e-mail"; public static final String LOGIN_URL_KEY = "login-url"; public static final String LOGIN_MAXIMIZED_KEY = "login-window-maximized"; private QueryController queryController; public MainToolbar() { String bugmail = (String) VaadinSession.getCurrent().getAttribute(BUG_MAIL_KEY); if (bugmail != null && !bugmail.isEmpty() && !bugmail.startsWith("${") && new EmailValidator("").isValid(bugmail)) { this.bugEMailAddress = bugmail; } else { this.bugEMailAddress = null; } UI ui = UI.getCurrent(); if (ui instanceof CommonUI) { ((CommonUI) ui).getSettings().addedLoadedListener(MainToolbar.this); } setWidth("100%"); setHeight("-1px"); addStyleName("toolbar"); addStyleName("border-layout"); btAboutAnnis = new Button("About ANNIS"); btAboutAnnis.addStyleName(ValoTheme.BUTTON_SMALL); btAboutAnnis.setIcon(new ThemeResource("images/annis_16.png")); btAboutAnnis.addClickListener(new AboutClickListener()); btSidebar = new Button(); btSidebar.setDisableOnClick(true); btSidebar.addStyleName(ValoTheme.BUTTON_SMALL); btSidebar.setDescription("Show and hide search sidebar"); btSidebar.setIconAlternateText(btSidebar.getDescription()); btBugReport = new Button("Report Problem"); btBugReport.addStyleName(ValoTheme.BUTTON_SMALL); btBugReport.setDisableOnClick(true); btBugReport.setIcon(FontAwesome.ENVELOPE_O); btBugReport.addClickListener(new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { reportBug(); } }); btBugReport.setVisible(this.bugEMailAddress != null); btNavigate = new Button(); btNavigate.setVisible(false); btNavigate.setDisableOnClick(true); btNavigate.addClickListener(new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { btNavigate.setEnabled(true); if (navigationTarget != null) { UI.getCurrent().getNavigator().navigateTo(navigationTarget.state); } } }); lblUserName = new Label("not logged in"); lblUserName.setWidth("-1px"); lblUserName.setHeight("-1px"); lblUserName.addStyleName("right-aligned-text"); btLogin = new Button("Login", new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { showLoginWindow(false); } }); btLogout = new Button("Logout", new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { // logout Helper.setUser(null); for (LoginListener l : loginListeners) { l.onLogout(); } Notification.show("Logged out", Notification.Type.TRAY_NOTIFICATION); updateUserInformation(); } }); btLogin.setSizeUndefined(); btLogin.setStyleName(ValoTheme.BUTTON_SMALL); btLogin.setIcon(FontAwesome.USER); btLogout.setSizeUndefined(); btLogout.setStyleName(ValoTheme.BUTTON_SMALL); btLogout.setIcon(FontAwesome.USER); btOpenSource = new Button("Help us to make ANNIS better!"); btOpenSource.setStyleName(BaseTheme.BUTTON_LINK); btOpenSource.addClickListener(new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { Window w = new HelpUsWindow(); w.setCaption("Help us to make ANNIS better!"); w.setModal(true); w.setResizable(true); w.setWidth("600px"); w.setHeight("500px"); UI.getCurrent().addWindow(w); w.center(); } }); addComponent(btSidebar); setComponentAlignment(btSidebar, Alignment.MIDDLE_LEFT); addComponent(btAboutAnnis); addComponent(btBugReport); addComponent(btNavigate); addComponent(btOpenSource); setSpacing(true); setComponentAlignment(btAboutAnnis, Alignment.MIDDLE_LEFT); setComponentAlignment(btBugReport, Alignment.MIDDLE_LEFT); setComponentAlignment(btNavigate, Alignment.MIDDLE_LEFT); setComponentAlignment(btOpenSource, Alignment.MIDDLE_CENTER); setExpandRatio(btOpenSource, 1.0f); addLoginButton(); btSidebar.addClickListener(new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { btSidebar.setEnabled(true); // decide new state switch (sidebarState) { case VISIBLE: if (event.isCtrlKey()) { sidebarState = SidebarState.AUTO_VISIBLE; } else { sidebarState = SidebarState.HIDDEN; } break; case HIDDEN: if (event.isCtrlKey()) { sidebarState = SidebarState.AUTO_HIDDEN; } else { sidebarState = SidebarState.VISIBLE; } break; case AUTO_VISIBLE: if (event.isCtrlKey()) { sidebarState = SidebarState.VISIBLE; } else { sidebarState = SidebarState.AUTO_HIDDEN; } break; case AUTO_HIDDEN: if (event.isCtrlKey()) { sidebarState = SidebarState.HIDDEN; } else { sidebarState = SidebarState.AUTO_VISIBLE; } break; } updateSidebarState(); } }); screenshotExtension = new ScreenshotMaker(this); JavaScript.getCurrent().addFunction("annis.gui.logincallback", new LoginCloseCallback()); updateSidebarState(); MainToolbar.this.updateUserInformation(); } @Override public void attach() { super.attach(); UI ui = UI.getCurrent(); if (ui instanceof AnnisBaseUI) { ((AnnisBaseUI) ui).getLoginDataLostBus().register(this); } IDGenerator.assignIDForFields(MainToolbar.this, btAboutAnnis, btOpenSource); } @Override public void detach() { UI ui = UI.getCurrent(); if (ui instanceof AnnisBaseUI) { ((AnnisBaseUI) ui).getLoginDataLostBus().unregister(this); } super.detach(); } public void setNavigationTarget(NavigationTarget target) { if (target == this.navigationTarget) { // nothing changed, return } this.navigationTarget = target; btNavigate.setVisible(false); if (target == NavigationTarget.ADMIN) { // check in background if display is necessary AnnisUser user = Helper.getUser(); if (user != null && user.getUserName() != null) { Background.run(new CheckIfUserIsAdministratorJob(user.getUserName(), UI.getCurrent())); } } else if (target != null) { btNavigate.setVisible(true); btNavigate.setCaption(target.caption); btNavigate.setIcon(target.icon); } } private void updateSidebarState() { if (sidebar != null && btSidebar != null) { btSidebar.setIcon(sidebarState.getIcon()); sidebar.updateSidebarState(sidebarState); } } public void notifiyQueryStarted() { if (sidebarState == SidebarState.AUTO_VISIBLE) { sidebarState = SidebarState.AUTO_HIDDEN; } updateSidebarState(); } public void addLoginListener(LoginListener listener) { this.loginListeners.add(listener); } public void removeLoginListener(LoginListener listener) { this.loginListeners.remove(listener); } /** * Adds the login button + login text to the toolbar. This is only happened, * when the gui is not started via the kickstarter. * * <p> * The Kickstarter overrides the "kickstarterEnvironment" context parameter * and set it to "true", so the gui can detect, that is not necessary to offer * a login button.</p> * * component. */ private void addLoginButton() { VaadinSession session = VaadinSession.getCurrent(); if (session != null) { boolean kickstarter = Helper.isKickstarter(session); if (!kickstarter) { addComponent(lblUserName); setComponentAlignment(lblUserName, Alignment.MIDDLE_RIGHT); addComponent(btLogin); setComponentAlignment(btLogin, Alignment.MIDDLE_RIGHT); } } } @Override public void onSettingsLoaded(SettingsStorage settings) { String sidebarStateSetting = settings.get("annis-sidebar-state"); if (sidebarStateSetting != null) { try { sidebarState = SidebarState.valueOf(sidebarStateSetting); // don't be invisible if (sidebarState == SidebarState.AUTO_HIDDEN) { sidebarState = SidebarState.AUTO_VISIBLE; } else if (sidebarState == SidebarState.HIDDEN) { sidebarState = SidebarState.VISIBLE; } } catch (IllegalArgumentException ex) { log.debug("Invalid cookie for sidebar state", ex); } } updateSidebarState(); } private void updateUserInformation() { if (lblUserName == null) { return; } if (navigationTarget == NavigationTarget.ADMIN) { // don't show administration link per default btNavigate.setVisible(false); } AnnisUser user = Helper.getUser(); // always close the window if (windowLogin != null) { windowLogin.close(user != null); } if (user == null) { Object loginErrorOject = VaadinSession.getCurrent().getSession().getAttribute(USER_LOGIN_ERROR); if (loginErrorOject != null && loginErrorOject instanceof String) { Notification.show((String) loginErrorOject, Notification.Type.WARNING_MESSAGE); } VaadinSession.getCurrent().getSession().removeAttribute(AnnisBaseUI.USER_LOGIN_ERROR); lblUserName.setValue("not logged in"); if (getComponentIndex(btLogout) > -1) { replaceComponent(btLogout, btLogin); setComponentAlignment(btLogin, Alignment.MIDDLE_RIGHT); } } else { // logged in if (user.getUserName() != null) { Notification.show("Logged in as \"" + user.getUserName() + "\"", Notification.Type.TRAY_NOTIFICATION); lblUserName.setValue("logged in as \"" + user.getUserName() + "\""); } if (getComponentIndex(btLogin) > -1) { replaceComponent(btLogin, btLogout); setComponentAlignment(btLogout, Alignment.MIDDLE_RIGHT); } // do not show the logout button if the user cannot logout using ANNIS btLogout.setVisible(!user.isRemote()); if (navigationTarget == NavigationTarget.ADMIN) { // check in background if display is necessary if (user.getUserName() != null) { Background.run(new CheckIfUserIsAdministratorJob(user.getUserName(), UI.getCurrent())); } } } } @Override public void onLogin() { updateUserInformation(); } @Override public void onLogout() { if (windowLogin != null) { // make sure to close the login window without triggering a search execution windowLogin.close(false); } updateUserInformation(); } public boolean canReportBugs() { return this.bugEMailAddress != null; } public void reportBug() { reportBug(null); } public void reportBug(Throwable cause) { lastBugReportCause = cause; if (screenshotExtension.isAttached()) { screenshotExtension.makeScreenshot(); btBugReport.setCaption("problem report is initialized..."); } else { Notification.show("This user interface does not allow screenshots. Can't report bug.", Notification.Type.ERROR_MESSAGE); } } @Override public void screenshotReceived(byte[] imageData, String mimeType) { btBugReport.setEnabled(true); btBugReport.setCaption("Report Problem"); if (bugEMailAddress != null) { ReportBugWindow reportBugWindow = new ReportBugWindow(bugEMailAddress, imageData, mimeType, lastBugReportCause); reportBugWindow.setModal(true); reportBugWindow.setResizable(true); UI.getCurrent().addWindow(reportBugWindow); reportBugWindow.center(); lastBugReportCause = null; } } public ScreenshotMaker getScreenshotExtension() { return screenshotExtension; } private static class AboutClickListener implements Button.ClickListener { public AboutClickListener() { } @Override public void buttonClick(Button.ClickEvent event) { Window w = new AboutWindow(); w.setCaption("About ANNIS"); w.setModal(true); w.setResizable(true); w.setWidth("500px"); w.setHeight("500px"); UI.getCurrent().addWindow(w); } } public boolean isLoggedIn() { return Helper.getUser() != null; } private class LoginCloseCallback implements JavaScriptFunction { @Override public void call(JsonArray arguments) throws JSONException { if (isLoggedIn()) { for (LoginListener l : loginListeners) { try { l.onLogin(); } catch (Exception ex) { log.error("exception thrown while notifying login listeners", ex); } } } updateUserInformation(); } } @Subscribe public void handleLoginDataLostException(LoginDataLostException ex) { Notification.show("Login data was lost, please login again.", "Due to a server misconfiguration the login-data was lost. Please contact the adminstrator of this ANNIS instance.", Notification.Type.WARNING_MESSAGE); for (LoginListener l : loginListeners) { try { l.onLogout(); } catch (Exception loginEx) { log.error("exception thrown while notifying login listeners", loginEx); } } updateUserInformation(); } public Sidebar getSidebar() { return sidebar; } public void setSidebar(Sidebar sidebar) { this.sidebar = sidebar; btSidebar.setVisible(sidebar != null); updateSidebarState(); } private class CheckIfUserIsAdministratorJob implements Runnable { private final String userName; private final UI ui; public CheckIfUserIsAdministratorJob(String userName, UI ui) { this.userName = userName; this.ui = ui; } @Override public void run() { User user = null; try { user = Helper.getAnnisWebResource().path("admin/users").path(userName).get(User.class); } catch (UniformInterfaceException ex) { // ignore } finally { boolean hasAdmistrationRights = false; if (user != null) { for (String perm : user.getPermissions()) { if (perm.startsWith("*:") || perm.startsWith("admin:")) { // the user has at least some administration rights hasAdmistrationRights = true; } } } if (hasAdmistrationRights) { ui.access(new Runnable() { @Override public void run() { // make the administration button visible btNavigate.setCaption(NavigationTarget.ADMIN.caption); btNavigate.setIcon(NavigationTarget.ADMIN.icon); btNavigate.setVisible(true); } }); } } } } public void showLoginWindow(boolean executeQueryAfterLogin) { windowLogin.setExecuteSearchAfterClose(executeQueryAfterLogin); if (windowLogin.isAttached()) { windowLogin.close(); } UI.getCurrent().addWindow(windowLogin); } public QueryController getQueryController() { return queryController; } public void setQueryController(QueryController queryController) { this.queryController = queryController; windowLogin.setQueryController(queryController); } }