| /******************************************************************************* |
| * Copyright (c) 2013 Boeing. |
| * 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: |
| * Boeing - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osee.framework.ui.workspacebundleloader; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.io.OutputStreamWriter; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.nio.channels.FileChannel; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.logging.Level; |
| |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtension; |
| import org.eclipse.core.runtime.IExtensionPoint; |
| import org.eclipse.core.runtime.IExtensionRegistry; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.osee.framework.core.operation.AbstractOperation; |
| import org.eclipse.osee.framework.core.operation.Operations; |
| import org.eclipse.osee.framework.jdk.core.util.Lib; |
| import org.eclipse.osee.framework.logging.OseeLog; |
| import org.eclipse.osee.framework.plugin.core.util.OseeData; |
| import org.eclipse.osee.framework.ui.workspacebundleloader.internal.Activator; |
| import org.eclipse.osee.framework.ui.workspacebundleloader.internal.ManagedFolderArea; |
| import org.eclipse.ui.IMemento; |
| import org.eclipse.ui.IPerspectiveDescriptor; |
| import org.eclipse.ui.IViewPart; |
| import org.eclipse.ui.IViewReference; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.WorkbenchException; |
| import org.eclipse.ui.XMLMemento; |
| import org.eclipse.ui.views.IViewDescriptor; |
| import org.eclipse.ui.views.IViewRegistry; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.FrameworkEvent; |
| import org.osgi.framework.FrameworkListener; |
| import org.osgi.framework.FrameworkUtil; |
| import org.osgi.framework.wiring.FrameworkWiring; |
| |
| public class WorkspaceBundleLoadCoordinator { |
| |
| private static final String TAG_VIEW = "view"; |
| private static final String TAG_PERSPECTIVE = "perspective"; |
| private static final String TAG_OTE_PRECOMPILED = "OTEPrecompiled"; |
| private static final String OTE_MEMENTO = "OTEMemento"; |
| |
| private ManagedFolderArea managedFolderArea; |
| private Set<String> bundlesToCheck; |
| private BundleCollection managedArea = new BundleCollection(); |
| private FrameworkWiring wiring; |
| |
| public WorkspaceBundleLoadCoordinator(File temporaryBundleLocationFolder) { |
| bundlesToCheck = new HashSet<>(); |
| this.managedFolderArea = new ManagedFolderArea(temporaryBundleLocationFolder); |
| managedFolderArea.initialize(); |
| this.wiring = getFrameworkWiring(); |
| |
| Thread th = new Thread(new Runnable() { |
| @Override |
| public void run() { |
| int lastSize = 0; |
| boolean extraWait = false; |
| while(true){ |
| try { |
| if(extraWait){ |
| Thread.sleep(15000); |
| extraWait = false; |
| } else { |
| Thread.sleep(5000); |
| } |
| } catch (InterruptedException e) { |
| } |
| if(lastSize == bundlesToCheck.size()){ |
| if(lastSize != 0){ |
| if(bundlesToCheck.size() > 0){ |
| lastSize = 0; |
| Operations.executeAsJob(new RefreshWorkspaceBundles(), false); |
| try { |
| Thread.sleep(1000*60); //give time to load so we don't get called twice |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| } else { |
| if(bundlesToCheck.size() - lastSize > 5){ |
| extraWait = true;//big import allow for extra time for file imports |
| } |
| lastSize = bundlesToCheck.size(); |
| } |
| } |
| } |
| }); |
| th.setName("OTE BundleLoad Check"); |
| th.setDaemon(true); |
| th.start(); |
| } |
| |
| private FrameworkWiring getFrameworkWiring() { |
| FrameworkWiring frameworkWiring = null; |
| Bundle bundle = FrameworkUtil.getBundle(getClass()); |
| for(Bundle findit:bundle.getBundleContext().getBundles()){ |
| frameworkWiring = findit.adapt(FrameworkWiring.class); |
| if(frameworkWiring != null){ |
| break; |
| } |
| } |
| return frameworkWiring; |
| } |
| |
| |
| |
| private class RefreshWorkspaceBundles extends AbstractOperation { |
| |
| public RefreshWorkspaceBundles() { |
| super("Update Precompiled", Activator.BUNDLE_ID); |
| } |
| |
| @Override |
| protected void doWork(IProgressMonitor monitor) throws Exception { |
| updateBundles(monitor); |
| installLatestBundles(monitor); |
| } |
| |
| } |
| |
| public void saveAndCloseViews(){ |
| PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable(){ |
| @Override |
| public void run() { |
| saveAndCloseManagedViews(determineManagedViews(), true); |
| } |
| }); |
| } |
| |
| private void closeUpdatedViews(final List<BundleInfoLite> uninstallList){ |
| PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable(){ |
| @Override |
| public void run() { |
| saveAndCloseManagedViews(determineManagedViews(uninstallList), true); |
| } |
| }); |
| } |
| |
| |
| |
| public synchronized void uninstallBundles(){ |
| if(managedArea.getInstalledBundles().size() > 0){ |
| saveAndCloseViews(); |
| for(BundleInfoLite info:managedArea.getInstalledBundles()){ |
| try { |
| info.uninstall(); |
| } catch (BundleException e) { |
| OseeLog.log(WorkspaceBundleLoadCoordinator.class, Level.WARNING, e); |
| } |
| } |
| if(wiring != null){ |
| wiring.refreshBundles(null); |
| } |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| if (workbench != null && workbench.getActiveWorkbenchWindow() != null){ |
| IViewRegistry registry = workbench.getViewRegistry(); |
| forceViewRegistryReload(workbench, registry); |
| } |
| waitForViewsToBeRegistered(null); |
| } |
| } |
| |
| |
| private void saveAndCloseManagedViews(Set<String> managedViewIds, boolean save) { |
| |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| if (managedArea.getInstalledBundles().size() > 0 && workbench != null){ |
| IWorkbenchPage page = null; |
| if(PlatformUI.getWorkbench().getActiveWorkbenchWindow() == null){ |
| IWorkbenchWindow[] windows = workbench.getWorkbenchWindows(); |
| for(IWorkbenchWindow win :windows){ |
| page = win.getActivePage(); |
| if(page != null){ |
| break; |
| } |
| } |
| } else { |
| page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
| } |
| if(page == null){ |
| return; |
| } |
| IPerspectiveDescriptor originalPerspective = page.getPerspective(); |
| XMLMemento memento = XMLMemento.createWriteRoot(TAG_OTE_PRECOMPILED); |
| //find the view in other perspectives |
| IPerspectiveDescriptor[] pd = page.getOpenPerspectives(); |
| for (int i = 0; i < pd.length; i++) { |
| try { |
| page.setPerspective(pd[i]); |
| } catch (Exception ex) { |
| // Ignore, this can get an NPE in Eclipse, see bug 4454 |
| } |
| IMemento perspectiveMemento = null; |
| try{ |
| perspectiveMemento = memento.createChild(TAG_PERSPECTIVE); |
| perspectiveMemento.putString("id", pd[i].getId()); |
| } catch (Exception ex){ |
| //Ignore, the perspective id is invalid xml |
| } |
| IViewReference[] activeReferences = page.getViewReferences(); |
| for (IViewReference viewReference : activeReferences) { |
| int index = viewReference.getId().indexOf(":"); |
| String id = null; |
| if(index>0){ |
| id = viewReference.getId().substring(0, index); |
| } else { |
| id = viewReference.getId(); |
| } |
| if (managedViewIds.contains(id)){ |
| if(perspectiveMemento != null){ |
| try{ |
| IMemento viewMemento = perspectiveMemento.createChild(TAG_VIEW); |
| viewMemento.putString("id", id); |
| String secondaryId = viewReference.getSecondaryId(); |
| if(secondaryId != null){ |
| viewMemento.putString("secondId", secondaryId); |
| } |
| IWorkbenchPart part = viewReference.getPart(false); |
| if(part instanceof IViewPart){ |
| IViewPart viewPart = (IViewPart)part; |
| viewPart.saveState(viewMemento); |
| } |
| } catch (Exception ex){ |
| //Ignore, we failed during view save |
| } |
| } |
| try{ |
| page.hideView(viewReference); |
| } catch (Throwable th){ |
| } |
| } |
| } |
| } |
| if(save){ |
| saveMementoToFile(memento); |
| } |
| page.setPerspective(originalPerspective); |
| } |
| } |
| |
| private Set<String> determineManagedViews() { |
| return determineManagedViews(null); |
| } |
| |
| private Set<String> determineManagedViews(List<BundleInfoLite> uninstallList){ |
| Set<String> managedViewIds = new HashSet<>(); |
| IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry(); |
| IExtensionPoint extensionPoint = extensionRegistry.getExtensionPoint("org.eclipse.ui.views"); |
| IExtension[] extensions = extensionPoint.getExtensions(); |
| for(IExtension ex:extensions){ |
| String name = ex.getContributor().getName(); |
| if(managedArea.getByBundleName(name) != null){ |
| IConfigurationElement[] elements = ex.getConfigurationElements(); |
| for(IConfigurationElement el:elements){ |
| if(el.getName().equals(TAG_VIEW)){ |
| String id = el.getAttribute("id"); |
| if(id != null){ |
| if(uninstallList != null){ |
| for(BundleInfoLite infoLite:uninstallList){ |
| if(name.equals(infoLite.getSymbolicName())){ |
| managedViewIds.add(id); |
| break; |
| } |
| } |
| } else { |
| managedViewIds.add(id); |
| } |
| } |
| } |
| } |
| } |
| } |
| return managedViewIds; |
| } |
| |
| private boolean saveMementoToFile(XMLMemento memento) { |
| File stateFile = OseeData.getFile(OTE_MEMENTO); |
| if (stateFile == null) { |
| return false; |
| } |
| try { |
| FileOutputStream stream = new FileOutputStream(stateFile); |
| OutputStreamWriter writer = new OutputStreamWriter(stream, "utf-8"); //$NON-NLS-1$ |
| memento.save(writer); |
| writer.close(); |
| } catch (IOException e) { |
| stateFile.delete(); |
| return false; |
| } |
| return true; |
| } |
| |
| public static void copyFile(File source, File destination) throws IOException { |
| final FileChannel in = new FileInputStream(source).getChannel(); |
| try { |
| final FileChannel out; |
| if (destination.isDirectory()) { |
| out = new FileOutputStream(new File(destination, source.getName())).getChannel(); |
| } else { |
| if (destination.exists()) { |
| destination.delete(); // to work around some file permission problems |
| } |
| out = new FileOutputStream(destination).getChannel(); |
| } |
| try { |
| long position = 0; |
| long size = in.size(); |
| while (position < size) { |
| position += in.transferTo(position, size, out); |
| } |
| } finally { |
| Lib.close(out); |
| } |
| } finally { |
| Lib.close(in); |
| } |
| } |
| |
| |
| |
| private List<BundleInfoLite> determineDeltasBetweenBundlesToLoad() { |
| List<BundleInfoLite> bundlesToOperateOn = new ArrayList<>(); |
| for(String urlString:bundlesToCheck){ |
| try { |
| URL newURL; |
| try{ |
| newURL = new URL(urlString); |
| } catch(MalformedURLException ex){ |
| newURL = new File(urlString).toURI().toURL(); |
| } |
| |
| BundleInfoLite bundleInfo = new BundleInfoLite(newURL); |
| List<BundleInfoLite> bundleList = managedArea.getByBundleName(bundleInfo.getSymbolicName()); |
| if(bundleList == null){ |
| bundlesToOperateOn.add(bundleInfo); |
| } else { |
| boolean newBundle=true; |
| if(bundleList != null && bundleList.size() > 0){ |
| byte[] digest1 = bundleInfo.getMd5Digest(); |
| for(BundleInfoLite bundle:bundleList){ |
| byte[] digest2 = bundle.getMd5Digest(); |
| if (Arrays.equals(digest1, digest2)) { |
| newBundle = false; |
| new File(bundle.getSystemLocation().getFile()).setLastModified(System.currentTimeMillis()); |
| } |
| } |
| } |
| if(newBundle){ |
| bundlesToOperateOn.add(bundleInfo); |
| } |
| } |
| } catch (MalformedURLException e) { |
| OseeLog.log(WorkspaceBundleLoadCoordinator.class, Level.WARNING, e); |
| } catch (IOException e) { |
| OseeLog.log(WorkspaceBundleLoadCoordinator.class, Level.WARNING, e); |
| } |
| } |
| bundlesToCheck.clear(); |
| return bundlesToOperateOn; |
| } |
| |
| public synchronized void addBundleToCheck(String urlString){ |
| this.bundlesToCheck.add(urlString); |
| } |
| |
| public synchronized void updateBundles(IProgressMonitor monitor){ |
| List<BundleInfoLite> deltas = determineDeltasBetweenBundlesToLoad(); |
| monitor.worked(Operations.calculateWork(Operations.TASK_WORK_RESOLUTION, 0.15)); |
| List<BundleInfoLite> bundlesToAdd = managedFolderArea.copyDeltasToManagedFolder(deltas); |
| monitor.worked(Operations.calculateWork(Operations.TASK_WORK_RESOLUTION, 0.30)); |
| for(BundleInfoLite bundle:bundlesToAdd){ |
| managedArea.add(bundle); |
| } |
| monitor.worked(Operations.calculateWork(Operations.TASK_WORK_RESOLUTION, 0.05)); |
| } |
| |
| public synchronized void installLatestBundles(final IProgressMonitor subMonitor){ |
| final List<BundleInfoLite> bundles = managedArea.getLatestBundles(); |
| Collection<Bundle> bundlesToRefresh = new ArrayList<>(); |
| List<BundleInfoLite> uninstallListAll = new ArrayList<>(); |
| for(BundleInfoLite info:bundles){ |
| if(!info.isInstalled()){ |
| List<BundleInfoLite> uninstallList = managedArea.getByBundleName(info.getSymbolicName()); |
| if(uninstallList.size() > 1){ |
| for(BundleInfoLite toUninstall:uninstallList){ |
| if(toUninstall.isInstalled()){ |
| uninstallListAll.add(toUninstall); |
| } |
| } |
| } |
| } |
| } |
| |
| closeUpdatedViews(uninstallListAll); |
| for(BundleInfoLite toUninstall:uninstallListAll){ |
| try { |
| Bundle bundle = toUninstall.uninstall(); |
| bundlesToRefresh.add(bundle); |
| } catch (BundleException e) { |
| OseeLog.log(WorkspaceBundleLoadCoordinator.class, Level.WARNING, e); |
| } |
| } |
| |
| if(wiring != null && bundlesToRefresh.size() > 0){ |
| final Object waitForLoad = new Object(); |
| wiring.refreshBundles(bundlesToRefresh, new FrameworkListener(){ |
| @Override |
| public void frameworkEvent(FrameworkEvent event) { |
| if(FrameworkEvent.PACKAGES_REFRESHED == event.getType()){ |
| startBundles(bundles, subMonitor); |
| waitForViewsToBeRegistered(subMonitor); |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable(){ |
| @Override |
| public void run() { |
| restoreStateFromMemento(subMonitor); |
| } |
| }); |
| synchronized (waitForLoad) { |
| waitForLoad.notifyAll(); |
| } |
| } |
| } |
| }); |
| synchronized (waitForLoad) { |
| try { |
| waitForLoad.wait(20000); |
| } catch (InterruptedException e) { |
| } |
| } |
| } else { |
| startBundles(bundles, subMonitor); |
| waitForViewsToBeRegistered(subMonitor); |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable(){ |
| @Override |
| public void run() { |
| restoreStateFromMemento(subMonitor); |
| } |
| }); |
| } |
| } |
| |
| private boolean waitForViewsToBeRegistered(IProgressMonitor monitor){ |
| if(monitor != null) |
| monitor.setTaskName("Waiting for views to register."); |
| for(int i = 0; i < 10; i++){ |
| if(monitor != null) |
| monitor.worked(1); |
| CheckViewsRegistered check = new CheckViewsRegistered(); |
| PlatformUI.getWorkbench().getDisplay().syncExec(check); |
| if(check.isLoaded()){ |
| return true; |
| } else { |
| try { |
| Thread.sleep(5000); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| return false; |
| } |
| |
| private class CheckViewsRegistered implements Runnable { |
| |
| private volatile boolean isLoaded = false; |
| |
| @Override |
| public void run() { |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| if (managedArea.getInstalledBundles().size() > 0 && workbench != null && workbench.getActiveWorkbenchWindow() != null){ |
| IViewRegistry registry = workbench.getViewRegistry(); |
| forceViewRegistryReload(workbench, registry); |
| Set<String> managedViews = determineManagedViews(); |
| for(String viewId:managedViews){ |
| try{ |
| IViewDescriptor desc = registry.find(viewId); |
| if(desc == null){ |
| return; |
| } |
| } catch (Exception ex){ |
| return; |
| } |
| } |
| isLoaded = true; |
| } else { //no workspace bundles to load, so don't wait |
| isLoaded = true; |
| } |
| |
| } |
| |
| public boolean isLoaded(){ |
| return isLoaded; |
| } |
| } |
| |
| @SuppressWarnings({ "rawtypes" }) |
| private void forceViewRegistryReload(IWorkbench workbench, IViewRegistry registry){ |
| try{ |
| Field field1 = registry.getClass().getDeclaredField("descriptors"); |
| Field field2 = registry.getClass().getDeclaredField("stickyDescriptors"); |
| Field field3 = registry.getClass().getDeclaredField("categories"); |
| |
| field1.setAccessible(true); |
| field2.setAccessible(true); |
| field3.setAccessible(true); |
| |
| ((Map)field1.get(registry)).clear(); |
| ((List)field2.get(registry)).clear(); |
| ((Map)field3.get(registry)).clear(); |
| |
| field1.setAccessible(false); |
| field2.setAccessible(false); |
| field3.setAccessible(false); |
| |
| Method[] methods = registry.getClass().getDeclaredMethods(); |
| Method method = null; |
| for(Method m:methods){ |
| if(m.getName().equals("postConstruct")){ |
| method = m; |
| break; |
| } |
| } |
| if(method != null){ |
| boolean access = method.isAccessible(); |
| method.setAccessible(true); |
| try{ |
| method.invoke(registry); |
| } finally { |
| method.setAccessible(access); |
| } |
| } |
| } catch (Throwable th){ |
| OseeLog.log(this.getClass(), Level.SEVERE, th); |
| } |
| } |
| |
| |
| private void restoreStateFromMemento(IProgressMonitor restore) { |
| File mementoFile = OseeData.getFile(OTE_MEMENTO); |
| if(mementoFile.exists()){ |
| try { |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| if (managedArea.getInstalledBundles().size() > 0 && workbench != null && workbench.getActiveWorkbenchWindow() != null){ |
| IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
| IPerspectiveDescriptor originalPerspective = page.getPerspective(); |
| IPerspectiveDescriptor[] pds = page.getOpenPerspectives(); |
| |
| XMLMemento memento = XMLMemento.createReadRoot(new FileReader(mementoFile)); |
| IMemento[] perspectives = memento.getChildren(TAG_PERSPECTIVE); |
| if(perspectives != null){ |
| for(IMemento perspective:perspectives){ |
| IMemento[] views = perspective.getChildren(TAG_VIEW); |
| if(views != null && views.length > 0){ |
| String perspectiveId = perspective.getString("id"); |
| for(IPerspectiveDescriptor pd:pds){ |
| if(pd.getId().equals(perspectiveId)){ |
| page.setPerspective(pd); |
| for(IMemento view:views){ |
| String viewId = view.getString("id"); |
| String secondId = view.getString("secondId"); |
| if(viewId != null){ |
| //show view |
| try { |
| page.showView(viewId, secondId, IWorkbenchPage.VIEW_ACTIVATE); |
| } catch (PartInitException ex) { |
| System.err.println("COULD NOT FIND " + viewId + ", with ID # = " + secondId); |
| ex.printStackTrace(); |
| } |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| page.setPerspective(originalPerspective); |
| } |
| |
| } catch (WorkbenchException e) { |
| e.printStackTrace(); |
| } catch (FileNotFoundException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| private void startBundles(Collection<BundleInfoLite> bundles, IProgressMonitor subMonitor){ |
| BundleContext context = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); |
| subMonitor.setTaskName("Installing Bundles"); |
| double workPercentage = 0.50 / (bundles.size()*2); |
| int workAmount = Operations.calculateWork(Operations.TASK_WORK_RESOLUTION, workPercentage); |
| for(BundleInfoLite info:bundles){ |
| if(!info.isInstalled()){ |
| try { |
| info.install(context); |
| } catch (BundleException e) { |
| } catch (IOException e) { |
| } |
| } |
| subMonitor.worked(workAmount*2); |
| } |
| for(BundleInfoLite info:bundles){ |
| if(!info.isStarted()){ |
| try { |
| info.start(context); |
| } catch (BundleException e) { |
| } |
| } |
| subMonitor.worked(workAmount); |
| } |
| } |
| |
| } |