Java tutorial
/*- * Copyright 2009 Diamond Light Source Ltd. * * This file is part of GDA. * * GDA is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License version 3 as published by the Free * Software Foundation. * * GDA is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with GDA. If not, see <http://www.gnu.org/licenses/>. */ package gda.rcp; import gda.configuration.properties.LocalProperties; import gda.data.PathConstructor; import gda.data.metadata.VisitEntry; import gda.data.metadata.icat.IcatProvider; import gda.factory.FactoryException; import gda.factory.Finder; import gda.factory.ObjectFactory; import gda.factory.corba.util.AdapterFactory; import gda.factory.corba.util.NetService; import gda.jython.InterfaceProvider; import gda.jython.MockJythonServerFacade; import gda.jython.authenticator.Authenticator; import gda.jython.authenticator.UserAuthentication; import gda.jython.authoriser.AuthoriserProvider; import gda.rcp.util.UIScanDataPointEventService; import gda.util.ElogEntry; import gda.util.ObjectServer; import gda.util.SpringObjectServer; import gda.util.logging.LogbackUtils; import java.io.File; import java.net.URL; import java.util.HashMap; import org.eclipse.core.runtime.Platform; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.osgi.service.datalocation.Location; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.PlatformUI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.gda.ClientManager; import uk.ac.gda.preferences.PreferenceConstants; import uk.ac.gda.richbeans.BeansFactoryInit; import uk.ac.gda.ui.dialog.AuthenticationDialog; import uk.ac.gda.ui.dialog.GenericDialog; import uk.ac.gda.ui.dialog.VisitIDDialog; /** * This class controls all aspects of the application's execution. We are very similar to an IDEApplication, so some of * this code comes from there. */ public class Application implements IApplication { private static final Logger logger = LoggerFactory.getLogger(Application.class); private static final String PROP_EXIT_CODE = "eclipse.exitcode"; @Override public Object start(IApplicationContext context) { Display display = PlatformUI.createDisplay(); try { // NOTE: Please keep the methods called during startup in tidy order. New tests or configurations should be // encapsulated in their own method. final boolean localObjectsOnly = createLocalObjectsIfRequired(); LogbackUtils.configureLoggingForClientProcess("rcp"); LogbackUtils.configureLoggingForClientBeagle(); authenticateUser(display); if (!localObjectsOnly) { //get access to distributed metadata object needed for identifying Visit ObjectFactory objectFactory = new ObjectFactory(); objectFactory.setName(LocalProperties.get("gda.factory.factoryName")); Finder finder = Finder.getInstance(); finder.addFactory(objectFactory); NetService netService = NetService.getInstance(); // Add an adapter factory to the finder to allow access to // objects created elsewhere. eg. in a standalone object server. AdapterFactory adapterFactory = new AdapterFactory(objectFactory.getName(), netService); finder.addFactory(adapterFactory); objectFactory.configure(); } if (identifyVisitID(display) == EXIT_OK) { return EXIT_OK; } //set workspace before creating items in call to createObjectFactory as the latter //may cause the accessing of preferences which is not possible until the workspace is set final String workspacePath = getWorkSpacePath(); createVisitBasedWorkspace(workspacePath); createObjectFactory(display, localObjectsOnly); // To break the dependency of uk.ac.gda.common.BeansFactory of RCP/Eclipse, we // manually force initialisation here. In the object server this is handled // by Spring, in Eclipse we use the registry try { BeansFactoryInit.initBeansFactory(); } catch (Exception e) { logger.error("Failed to initalize Beans Factory", e); throw new RuntimeException("Failed to initalize Beans Factory", e); } IPreferenceStore preferenceStore = GDAClientActivator.getDefault().getPreferenceStore(); if (preferenceStore.getBoolean(PreferenceConstants.GDA_USE_SCANDATAPOINT_SERVICE)) { createScanDataPointService(); } fixVisitID(); int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor()); // the workbench doesn't support relaunch yet (bug 61809) so // for now restart is used, and exit data properties are checked // here to substitute in the relaunch return code if needed if (returnCode != PlatformUI.RETURN_RESTART) { return EXIT_OK; } // if the exit code property has been set to the relaunch code, then // return that code now, otherwise this is a normal restart return EXIT_RELAUNCH.equals(Integer.getInteger(PROP_EXIT_CODE)) ? EXIT_RELAUNCH : EXIT_RESTART; } catch (Throwable ne) { logger.error("Cannot start client", ne); String problem = ne.getMessage(); String resolution = "Please contact your GDA support representative."; if (problem.contains("Could not initialise NetService: org.omg.CORBA.TRANSIENT")) { resolution = "It is likely that the server hardware has been updated and GDA server has not been started since. " + "The usual remedy is to Restart GDA Server from the panel (or 'gdaservers' in a terminal)." + "\n\nIf the problem cannot be remedied, please contact your GDA support representative."; } else if (problem.contains("NetService: init org.omg.CORBA.ORBPackage.InvalidName")) { resolution = "It is likely that the existing workspace is incompatible with an updated client. " + "The usual remedy is to start GDA client from a terminal with 'gdaclient --reset'." + "\n\nIf the problem cannot be remedied, please contact your GDA support representative."; } MessageDialog.openError(new Shell(display), "Cannot Start Client", "The GDA Client cannot start.\n\n'" + problem + "'\n\n" + resolution); return EXIT_OK; } finally { if (display != null && !display.isDisposed()) { try { display.dispose(); } catch (Throwable ignored) { // Exit time, if exception thrown here, user gets message. } } } } /** * Sets the visit ID of this Client process in the local JSF instance. * <p> * This must be done after the Workbench has started as the JSF makes connections to the server which can cause * issues if this is done before the workbench is created. */ private void fixVisitID() { logger.info("User " + UserAuthentication.getUsername() + " running GDA client using visit " + LocalProperties.get(LocalProperties.RCP_APP_VISIT)); InterfaceProvider.getBatonStateProvider().changeVisitID(LocalProperties.get(LocalProperties.RCP_APP_VISIT)); } private boolean createLocalObjectsIfRequired() { boolean localObjectsOnly = LocalProperties.check("gda.localObjectsOnly"); if (localObjectsOnly) { // we need to add a mock jython server facade until we can add a Command Server to the GUI. MockJythonServerFacade mockJythonServerFacade = new MockJythonServerFacade(); InterfaceProvider.setCommandRunnerForTesting(mockJythonServerFacade); InterfaceProvider.setCurrentScanControllerForTesting(mockJythonServerFacade); InterfaceProvider.setTerminalPrinterForTesting(mockJythonServerFacade); InterfaceProvider.setScanStatusHolderForTesting(mockJythonServerFacade); InterfaceProvider.setJythonNamespaceForTesting(mockJythonServerFacade); InterfaceProvider.setAuthorisationHolderForTesting(mockJythonServerFacade); InterfaceProvider.setScriptControllerForTesting(mockJythonServerFacade); InterfaceProvider.setPanicStopForTesting(mockJythonServerFacade); InterfaceProvider.setCurrentScanInformationHolderForTesting(mockJythonServerFacade); InterfaceProvider.setJythonServerNotiferForTesting(mockJythonServerFacade); InterfaceProvider.setDefaultScannableProviderForTesting(mockJythonServerFacade); InterfaceProvider.setScanDataPointProviderForTesting(mockJythonServerFacade); InterfaceProvider.setBatonStateProviderForTesting(mockJythonServerFacade); InterfaceProvider.setJSFObserverForTesting(mockJythonServerFacade); InterfaceProvider.setAliasedCommandProvider(mockJythonServerFacade); } return localObjectsOnly; } private void createScanDataPointService() { try { UIScanDataPointEventService.getInstance(); } catch (Exception ne) { logger.error("Cannot start scan data point service", ne); } } /* * sets the chosenVisit attribute to the default visit java property */ private void setToDefaultVisit() { //TODO show popup explaining that DICAT may be down and that it will use 0-0 unless local contact sets defVisit to correct value and add them to the $BEAMLINE-config/xml/beamlinestaff.xml LocalProperties.set(LocalProperties.RCP_APP_VISIT, LocalProperties.get("gda.defVisit", "0-0")); } /* * only sets the private chosenVisit attribute */ private int identifyVisitID(Display display) throws Exception { if (!IcatProvider.getInstance().icatInUse()) { logger.info("Icat database not in use. Using the default visit defined by property " + LocalProperties.GDA_DEF_VISIT); setToDefaultVisit(); return 1; } // test if the result has multiple entries String user = UserAuthentication.getUsername(); VisitEntry[] visits; try { visits = IcatProvider.getInstance().getMyValidVisits(user); } catch (Exception e) { logger.info( e.getMessage() + " - using default visit defined by property " + LocalProperties.GDA_DEF_VISIT, e); setToDefaultVisit(); return 1; } boolean isStaff = false; try { if (AuthoriserProvider.getAuthoriser().isLocalStaff(user)) { isStaff = true; } } catch (ClassNotFoundException e) { logger.error("Problem checking if user is staff. Assuming user IS staff.", e); isStaff = true; } // if no valid visit ID then do same as the cancel button if (visits == null || visits.length == 0) { if (!isStaff) { logger.info( "No visits found for user " + user + " at this time on this beamline. GUI will not start."); return EXIT_OK; } logger.info("No visits found for user " + user + " at this time on this beamline. Will use default visit as ID listed as a member of staff."); setToDefaultVisit(); } else if (visits.length == 1) { LocalProperties.set(LocalProperties.RCP_APP_VISIT, visits[0].getVisitID()); } else { // send array of visits to dialog to pick one String[][] visitInfo = new String[visits.length][]; int i = 0; for (VisitEntry visit : visits) { visitInfo[i] = new String[] { visit.getVisitID(), visit.getTitle() }; i++; } final VisitIDDialog visitDialog = new VisitIDDialog(display, visitInfo); if (visitDialog.open() == IDialogConstants.CANCEL_ID) { logger.info("Cancel pressed in visit chooser dialog. GUI will not continue."); return EXIT_OK; } if (visitDialog.getChoosenID() == null) { logger.info("Visit not resolved from visit chooser dialog. GUI will not start."); return EXIT_OK; } LocalProperties.set(LocalProperties.RCP_APP_VISIT, visitDialog.getChoosenID()); } return 1; } @Override public void stop() { final IWorkbench workbench = PlatformUI.getWorkbench(); if (workbench == null) return; final Display display = workbench.getDisplay(); if (display == null) return; if (!display.isDisposed()) display.syncExec(new Runnable() { @Override public void run() { if (!display.isDisposed()) { System.out.println("closing the workbench"); workbench.close(); System.out.println("closed the workbench"); } } }); } /** * Exits if no good */ private void authenticateUser(final Display display) { // if java property not set, then use the process' user. if (LocalProperties.get(Authenticator.AUTHENTICATORCLASS_PROPERTY, null) == null) { UserAuthentication.setToUseOSAuthentication(); return; } String userID = System.getProperty("user.name"); // change to AuthenticationDialog_old to use an alternative which looks like the old swing version of the GDA // login final AuthenticationDialog dialog = new AuthenticationDialog(display, SWT.OPEN, "Login to the Data Acquisition Client", userID, null); final GenericDialog.PasswordChecker checker = new GenericDialog.PasswordChecker() { @Override public boolean isValid() { if (!dialog.isAutomatic()) { UserAuthentication.setToNotUseOSAuthentication(dialog.getUsername(), dialog.getPassword()); // test if info given and user was authenticated if (UserAuthentication.getUsername() == null || UserAuthentication.getPassword() == null || UserAuthentication.getUsername().length() == 0 || UserAuthentication.getPassword().length() == 0) { dialog.setErrorMessage("Please enter a user name and password"); return false; } try { if (!UserAuthentication.isAuthenticated()) { dialog.setErrorMessage("Please enter a correct user name and password"); return false; } return true; } catch (Exception e) { Logger logger = LoggerFactory.getLogger(ElogEntry.class); logger.error(e.getMessage(), e); System.exit(0); } } else { UserAuthentication.setToUseOSAuthentication(); return true; } return false; } }; dialog.setChecker(checker); final Object ob = dialog.open(); if (ob == null) System.exit(0); return; } /* * Launch the ObjectServer to create the client implementation (as the AcquisitionFrame would do in original gda) */ private static boolean started = false; @SuppressWarnings("unused") private static void createClientObjects() throws FactoryException { if (!started) { ObjectServer.createClientImpl(); started = true; } } /** * * @param workspacePath * @throws Exception if the workspace failed to be set */ private void createVisitBasedWorkspace(final String workspacePath) throws Exception { boolean newWorkspace = false; final File workspace = new File(workspacePath); if (!workspace.exists()) { // New workspace newWorkspace = true; if (!workspace.mkdirs()) { final String msg = "Cannot create workspace in " + workspace.getAbsolutePath() + " not setting workspace"; throw new Exception(msg); } } workspace.setWritable(true); workspace.setReadable(true); if (!workspace.canRead() || !workspace.canWrite() || !workspace.canExecute()) { final String msg = "Not setting workspace to " + workspace.getAbsolutePath() + " due to insufficient permissions."; throw new Exception(msg); } URL url; Location instanceLocation; try { url = workspace.toURI().toURL(); instanceLocation = Platform.getInstanceLocation(); if (!instanceLocation.isSet()) { instanceLocation.set(url, false); } else { // generally it is expected that the instanceLocation is only set in development environment if (instanceLocation.getURL().equals(instanceLocation.getDefault())) { // If you get this exception you have hit a race condition similar to what was reported in GDA-3414. // This has probably occurred because some Eclipse component was run prior to the workbench being // fully initialised. To track down the area, place a breakpoint in BasicLocation#getUrl()'s if statement // that controls if the Location has not been initialised. Once you know who is calling getUrl too early, // figure out how to delay it until the workbench has been started. // NOTE: this check may be brittle as it is dependent on current implementation of LocationManager#initializeLocations throw new Exception( "Workspace has already been set when trying to set visit based workspace location."); } logger.info("Workspace instance location has been set with -data command line argument to " + instanceLocation.getURL()); // for correct reporting further on url = instanceLocation.getURL(); } } catch (Exception e) { final String msg = "Cannot set workspace to " + workspace.getAbsolutePath(); throw new Exception(msg, e); } if (instanceLocation.lock()) { logger.info("Workspace set to " + url); } else { throw new Exception( "Workspace at " + url + " is locked.\n Is another instance of GDA already running?"); } GDAClientActivator.getDefault().getPreferenceStore().setValue(PreferenceConstants.NEW_WORKSPACE, newWorkspace); } private String getWorkSpacePath() { // ensure we do not take these values from the metadata when defining the workspace that this client will use HashMap<String, String> metadataOverrides = new HashMap<String, String>(); metadataOverrides.put("visit", LocalProperties.get(LocalProperties.RCP_APP_VISIT)); metadataOverrides.put("federalid", UserAuthentication.getUsername()); metadataOverrides.put("user", UserAuthentication.getUsername()); String path = null; try { path = PathConstructor.createFromProperty("gda.rcp.workspace", metadataOverrides); } catch (Exception ne) { path = null; } if (path == null) { final String varDir = LocalProperties.getVarDir(); final String username = UserAuthentication.getUsername(); final String visit = LocalProperties.get(LocalProperties.RCP_APP_VISIT); final String template = String.format("%s/.workspace-%s-%s", varDir, username, visit); path = PathConstructor.createFromTemplate(template); } return path; } /** * Returns the path to the location of the XML project, e.g. As used for the storage location of the EXAFS project. * The intention is this project is stored outside the workspace to allow the workspace to be deleted without losing * the user created XML files. The path can be set via the gda.rcp.xmlproject property, or it will be created in the * users visit. * * @return the path to the xmlproject */ public static String getXmlPath() { String path = null; try { path = PathConstructor.createFromProperty("gda.rcp.xmlproject"); } catch (Exception ne) { path = null; } if (path == null) { path = PathConstructor.createFromRCPProperties() + "/xml/"; } return path; } private static void createObjectFactory(final Display display, final boolean localObjectsOnly) { try { if (!started) { String gda_gui_beans = LocalProperties.get(LocalProperties.GDA_GUI_BEANS_XML, LocalProperties.get(LocalProperties.GDA_GUI_XML)); if (gda_gui_beans != null) { // remove existing factories first Finder.getInstance().removeAllFactories(); SpringObjectServer s = new SpringObjectServer(new File(gda_gui_beans), localObjectsOnly); s.configure(); } started = true; } } catch (Exception ne) { logger.error("Error in createObjectFactory", ne);// Representative MessageDialog.openError(new Shell(display), "Error starting GDA Client", "The GDA Client cannot start.\n\nPlease contact your GDA support representative.\n\n'" + ne.getMessage() + "'"); if (!ClientManager.isTestingMode()) System.exit(-1); } } }