blob: 70b35f2fe29f58943dca80398c1c0b43feeabfcb [file] [log] [blame]
/*******************************************************************************
* 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);
}
}
}