blob: f9d433bc383c22a1a4b18f9ab274977e4d59ae9c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.modules.autoupdate.services;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.InvalidException;
import org.netbeans.Module;
import org.netbeans.ModuleManager;
import org.netbeans.api.autoupdate.*;
import org.netbeans.api.autoupdate.InstallSupport.Installer;
import org.netbeans.api.autoupdate.InstallSupport.Validator;
import org.netbeans.api.autoupdate.OperationContainer.OperationInfo;
import org.netbeans.api.autoupdate.OperationSupport.Restarter;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.spi.autoupdate.CustomInstaller;
import org.netbeans.spi.autoupdate.CustomUninstaller;
import org.openide.LifecycleManager;
import org.openide.modules.ModuleInfo;
import org.openide.util.Mutex.ExceptionAction;
import org.openide.util.MutexException;
import org.openide.util.NbBundle;
/**
* @author Jiri Rechtacek, Radek Matous
*/
public abstract class OperationSupportImpl {
private static final OperationSupportImpl FOR_INSTALL = new ForInstall();
private static final OperationSupportImpl FOR_ENABLE = new ForEnable();
private static final OperationSupportImpl FOR_DISABLE = new ForDisable();
private static final OperationSupportImpl FOR_DIRECT_DISABLE = new ForDirectDisable();
private static final OperationSupportImpl FOR_UNINSTALL = new ForUninstall();
private static final OperationSupportImpl FOR_DIRECT_UNINSTALL = new ForDirectUninstall();
private static final OperationSupportImpl FOR_CUSTOM_INSTALL = new ForCustomInstall ();
private static final OperationSupportImpl FOR_CUSTOM_UNINSTALL = new ForCustomUninstall ();
private static final Logger LOGGER = Logger.getLogger ("org.netbeans.modules.autoupdate.services.OperationSupportImpl");
public static OperationSupportImpl forInstall() {
return FOR_INSTALL;
}
public static OperationSupportImpl forUninstall() {
return FOR_UNINSTALL;
}
public static OperationSupportImpl forDirectUninstall() {
return FOR_DIRECT_UNINSTALL;
}
public static OperationSupportImpl forEnable() {
return FOR_ENABLE;
}
public static OperationSupportImpl forDisable() {
return FOR_DISABLE;
}
public static OperationSupportImpl forDirectDisable() {
return FOR_DIRECT_DISABLE;
}
public static OperationSupportImpl forCustomInstall () {
return FOR_CUSTOM_INSTALL;
}
public static OperationSupportImpl forCustomUninstall () {
return FOR_CUSTOM_UNINSTALL;
}
public abstract Boolean doOperation(ProgressHandle progress/*or null*/, OperationContainer<?> container) throws OperationException;
public abstract void doCancel () throws OperationException;
public abstract void doRestart (Restarter restarter, ProgressHandle progress) throws OperationException;
public abstract void doRestartLater (Restarter restarter);
/** Creates a new instance of OperationContainer */
private OperationSupportImpl() {
}
private static class ForEnable extends OperationSupportImpl {
@Override
public synchronized Boolean doOperation(ProgressHandle progress,
OperationContainer<?> container) throws OperationException {
boolean needsRestart = false;
try {
if (progress != null) {
progress.start();
}
ModuleManager mm = null;
List<? extends OperationInfo> elements = container.listAll();
Set<ModuleInfo> moduleInfos = new HashSet<ModuleInfo> ();
for (OperationInfo operationInfo : elements) {
UpdateElementImpl impl = Trampoline.API.impl (operationInfo.getUpdateElement ());
moduleInfos.addAll(impl.getModuleInfos(true));
}
final Set<Module> modules = new HashSet<Module>();
for (ModuleInfo info : moduleInfos) {
Module m = Utilities.toModule (info);
if (Utilities.canEnable (m)) {
modules.add(m);
LOGGER.log (Level.FINE, "Module will be enabled " + m.getCodeNameBase ());
}
if (mm == null) {
mm = m.getManager();
}
}
assert mm != null;
needsRestart = mm.hasToEnableCompatModules(modules);
final ModuleManager fmm = mm;
try {
fmm.mutex ().writeAccess (new ExceptionAction<Boolean> () {
@Override
public Boolean run () throws Exception {
return enable(fmm, modules);
}
});
} catch (MutexException ex) {
Exception x = ex.getException ();
assert x instanceof OperationException : x + " is instanceof OperationException";
if (x instanceof OperationException) {
throw (OperationException) x;
}
}
} finally {
if (progress != null) {
progress.finish();
}
}
return needsRestart;
}
@Override
public void doCancel () throws OperationException {
assert false : "Not supported yet";
}
private static boolean enable(ModuleManager mm, Set<Module> toRun) throws OperationException {
boolean retval = false;
try {
mm.enable(toRun);
retval = true;
} catch(IllegalArgumentException ilae) {
throw new OperationException(OperationException.ERROR_TYPE.ENABLE, ilae);
} catch(InvalidException ie) {
throw new OperationException(OperationException.ERROR_TYPE.ENABLE, ie);
}
return retval;
}
@Override
public void doRestart (Restarter restarter, ProgressHandle progress) throws OperationException {
LifecycleManager.getDefault().markForRestart();
LifecycleManager.getDefault().exit();
}
@Override
public void doRestartLater (Restarter restarter) {
LifecycleManager.getDefault().markForRestart();
}
}
private static class ForDisable extends OperationSupportImpl {
private Collection<File> controlFileForDisable = null;
private Collection<UpdateElement> affectedModules = null;
@Override
public synchronized Boolean doOperation(ProgressHandle progress,
OperationContainer<?> container) throws OperationException {
try {
if (progress != null) {
progress.start();
}
ModuleManager mm = null;
List<? extends OperationInfo> elements = container.listAll();
affectedModules = new HashSet<UpdateElement> ();
Set<ModuleInfo> moduleInfos = new HashSet<ModuleInfo> ();
for (OperationInfo operationInfo : elements) {
UpdateElementImpl impl = Trampoline.API.impl (operationInfo.getUpdateElement ());
affectedModules.add (operationInfo.getUpdateElement ());
moduleInfos.addAll (impl.getModuleInfos ());
}
Set<ModuleInfo> modules = new HashSet<ModuleInfo> ();
for (ModuleInfo info : moduleInfos) {
Module m = Utilities.toModule (info);
if (Utilities.canDisable (m)) {
modules.add(m);
LOGGER.log (Level.FINE, "Mark module " + m.getCodeNameBase () + " for disable.");
}
if (mm == null) {
mm = m.getManager();
}
}
assert mm != null;
ModuleDeleterImpl deleter = new ModuleDeleterImpl ();
controlFileForDisable = deleter.markForDisable (modules, progress);
} finally {
if (progress != null) {
progress.finish();
}
}
return true;
}
@Override
public void doCancel () throws OperationException {
if (controlFileForDisable != null) {
controlFileForDisable = null;
}
if (affectedModules != null) {
affectedModules = null;
}
}
@Override
public void doRestart (Restarter restarter, ProgressHandle progress) throws OperationException {
// write files marked to delete (files4remove) into temp file
// Updater will handle it
Utilities.writeFileMarkedForDisable (controlFileForDisable);
// restart IDE
Utilities.deleteAllDoLater ();
LifecycleManager.getDefault ().exit ();
// if exit&restart fails => use restart later as fallback
doRestartLater (restarter);
}
@Override
public void doRestartLater (Restarter restarter) {
// write files marked to delete (files4remove) into temp file
// Updater will handle it
Utilities.writeFileMarkedForDisable (controlFileForDisable);
// shedule module for restart
for (UpdateElement el : affectedModules) {
UpdateUnitFactory.getDefault().scheduleForRestart (el);
}
// write deactivate_later.txt
Utilities.writeDeactivateLater (controlFileForDisable);
}
}
private static class ForDirectDisable extends OperationSupportImpl {
@Override
public synchronized Boolean doOperation(ProgressHandle progress,
OperationContainer<?> container) throws OperationException {
try {
if (progress != null) {
progress.start();
}
ModuleManager mm = null;
List<? extends OperationInfo> elements = container.listAll();
Set<ModuleInfo> moduleInfos = new HashSet<ModuleInfo> ();
for (OperationInfo operationInfo : elements) {
UpdateElementImpl impl = Trampoline.API.impl (operationInfo.getUpdateElement ());
moduleInfos.addAll (impl.getModuleInfos ());
}
final Set<Module> modules = new HashSet<Module>();
for (ModuleInfo info : moduleInfos) {
Module m = Utilities.toModule (info);
if (Utilities.canDisable (m)) {
modules.add(m);
LOGGER.log (Level.FINE, "Module will be disabled " + m.getCodeNameBase ());
}
if (mm == null) {
mm = m.getManager();
}
}
assert mm != null;
final ModuleManager fmm = mm;
try {
fmm.mutex ().writeAccess (new ExceptionAction<Boolean> () {
@Override
public Boolean run () throws Exception {
return disable(fmm, modules);
}
});
} catch (MutexException ex) {
Exception x = ex.getException ();
assert x instanceof OperationException : x + " is instanceof OperationException";
if (x instanceof OperationException) {
throw (OperationException) x;
}
}
} finally {
if (progress != null) {
progress.finish();
}
}
return false;
}
private static boolean disable (ModuleManager mm, Set<Module> toRun) throws OperationException {
boolean retval = false;
try {
mm.disable (toRun);
retval = true;
} catch(IllegalArgumentException ilae) {
throw new OperationException(OperationException.ERROR_TYPE.ENABLE, ilae);
}
return retval;
}
@Override
public void doCancel () throws OperationException {
assert false : "Not supported yet";
}
@Override
public void doRestart (Restarter restarter, ProgressHandle progress) throws OperationException {
throw new UnsupportedOperationException ("Not supported yet.");
}
@Override
public void doRestartLater (Restarter restarter) {
throw new UnsupportedOperationException ("Not supported yet.");
}
}
private static class ForUninstall extends OperationSupportImpl {
private Collection<File> files4remove = null;
private Collection<UpdateElement> affectedModules = null;
@Override
public synchronized Boolean doOperation(ProgressHandle progress,
OperationContainer<?> container) throws OperationException {
try {
if (progress != null) {
progress.start();
}
ModuleDeleterImpl deleter = new ModuleDeleterImpl();
List<? extends OperationInfo> infos = container.listAll ();
Set<ModuleInfo> moduleInfos = new HashSet<ModuleInfo> ();
affectedModules = new HashSet<UpdateElement> ();
for (OperationInfo operationInfo : infos) {
UpdateElement updateElement = operationInfo.getUpdateElement ();
UpdateElementImpl updateElementImpl = Trampoline.API.impl (updateElement);
switch (updateElementImpl.getType ()) {
case KIT_MODULE :
case MODULE :
moduleInfos.add (((ModuleUpdateElementImpl) updateElementImpl).getModuleInfo ());
affectedModules.add (updateElementImpl.getUpdateElement ());
break;
case STANDALONE_MODULE :
case FEATURE :
for (ModuleUpdateElementImpl moduleImpl : ((FeatureUpdateElementImpl) updateElementImpl).getContainedModuleElements ()) {
moduleInfos.add (moduleImpl.getModuleInfo ());
if (moduleImpl.getUpdateUnit ().getInstalled () != null) {
affectedModules.add (moduleImpl.getUpdateElement ());
}
}
break;
case CUSTOM_HANDLED_COMPONENT :
break;
default:
assert false : "Not supported for impl " + updateElementImpl;
}
}
try {
files4remove = deleter.markForDelete (moduleInfos, progress);
} catch(IOException iex) {
throw new OperationException(OperationException.ERROR_TYPE.UNINSTALL, iex);
}
} finally {
if (progress != null) {
progress.finish();
}
}
return true;
}
@Override
public void doCancel () throws OperationException {
if (files4remove != null) {
files4remove = null;
}
if (affectedModules != null) {
affectedModules = null;
}
}
@Override
public void doRestart (Restarter restarter, ProgressHandle progress) throws OperationException {
// write files marked to delete (files4remove) into temp file
// Updater will handle it
Utilities.writeFileMarkedForDelete (files4remove);
// restart IDE
Utilities.deleteAllDoLater ();
LifecycleManager.getDefault ().exit ();
// if exit&restart fails => use restart later as fallback
doRestartLater (restarter);
}
@Override
public void doRestartLater (Restarter restarter) {
// write files marked to delete (files4remove) into temp file
// Updater will handle it
Utilities.writeFileMarkedForDelete (files4remove);
// shedule module for restart
for (UpdateElement el : affectedModules) {
UpdateUnitFactory.getDefault().scheduleForRestart (el);
}
// write deactivate_later.txt
Utilities.writeDeactivateLater (files4remove);
}
}
private static class ForDirectUninstall extends OperationSupportImpl {
@Override
public synchronized Boolean doOperation(ProgressHandle progress,
OperationContainer<?> container) throws OperationException {
try {
if (progress != null) {
progress.start();
}
ModuleDeleterImpl deleter = new ModuleDeleterImpl();
List<? extends OperationInfo> infos = container.listAll ();
Set<ModuleInfo> moduleInfos = new HashSet<ModuleInfo> ();
Set<UpdateUnit> affectedModules = new HashSet<UpdateUnit> ();
Set<UpdateUnit> affectedFeatures = new HashSet<UpdateUnit> ();
for (OperationInfo operationInfo : infos) {
UpdateElement updateElement = operationInfo.getUpdateElement ();
UpdateElementImpl updateElementImpl = Trampoline.API.impl (updateElement);
switch (updateElementImpl.getType ()) {
case KIT_MODULE :
case MODULE :
moduleInfos.add (((ModuleUpdateElementImpl) updateElementImpl).getModuleInfo ());
affectedModules.add (updateElementImpl.getUpdateUnit ());
break;
case STANDALONE_MODULE :
case FEATURE :
for (ModuleUpdateElementImpl moduleImpl : ((FeatureUpdateElementImpl) updateElementImpl).getContainedModuleElements ()) {
moduleInfos.add (moduleImpl.getModuleInfo ());
if (moduleImpl.getUpdateUnit ().getInstalled () != null) {
affectedModules.add (moduleImpl.getUpdateUnit ());
}
}
affectedFeatures.add (updateElement.getUpdateUnit ());
break;
default:
assert false : "Not supported for impl " + updateElementImpl;
}
}
try {
deleter.delete (moduleInfos.toArray (new ModuleInfo[0]), progress);
} catch(IOException iex) {
throw new OperationException(OperationException.ERROR_TYPE.UNINSTALL, iex);
}
for (UpdateUnit unit : affectedModules) {
assert unit.getInstalled () != null : "Module " + unit + " is installed while doing uninstall.";
LOGGER.log (Level.FINE, "Module was uninstalled " + unit.getCodeName ());
UpdateUnitImpl impl = Trampoline.API.impl (unit);
impl.setAsUninstalled();
}
for (UpdateUnit unit : affectedFeatures) {
assert unit.getInstalled () != null : "Feature " + unit + " is installed while doing uninstall.";
LOGGER.log (Level.FINE, "Feature was uninstalled " + unit.getCodeName ());
UpdateUnitImpl impl = Trampoline.API.impl (unit);
impl.setAsUninstalled();
}
} finally {
if (progress != null) {
progress.finish();
}
}
return false;
}
@Override
public void doCancel () throws OperationException {
assert false : "Not supported yet";
}
@Override
public void doRestart (Restarter restarter, ProgressHandle progress) throws OperationException {
throw new UnsupportedOperationException ("Not supported yet.");
}
@Override
public void doRestartLater (Restarter restarter) {
throw new UnsupportedOperationException ("Not supported yet.");
}
}
private static class ForInstall extends OperationSupportImpl {
@Override
public synchronized Boolean doOperation(ProgressHandle progress,
OperationContainer container) throws OperationException {
OperationContainer<InstallSupport> containerForUpdate = OperationContainer.createForUpdate();
List<? extends OperationInfo> infos = container.listAll();
for (OperationInfo info : infos) {
containerForUpdate.add(info.getUpdateUnit(), info.getUpdateElement());
}
assert containerForUpdate.listInvalid().isEmpty();
Validator v = containerForUpdate.getSupport().doDownload(ProgressHandleFactory.createHandle(OperationSupportImpl.class.getName()), null, false);
Installer i = containerForUpdate.getSupport().doValidate(v, ProgressHandleFactory.createHandle(OperationSupportImpl.class.getName()));
InstallSupportImpl installSupportImpl = Trampoline.API.impl(containerForUpdate.getSupport());
Boolean needRestart = installSupportImpl.doInstall(i, ProgressHandleFactory.createHandle(OperationSupportImpl.class.getName()), true);
return needRestart;
}
@Override
public void doCancel () throws OperationException {
assert false : "Not supported yet";
}
@Override
public void doRestart (Restarter restarter, ProgressHandle progress) throws OperationException {
throw new UnsupportedOperationException ("Not supported yet.");
}
@Override
public void doRestartLater (Restarter restarter) {
throw new UnsupportedOperationException ("Not supported yet.");
}
}
private static class ForCustomInstall extends OperationSupportImpl {
private Collection<UpdateElement> affectedModules = null;
@Override
public synchronized Boolean doOperation(ProgressHandle progress,
OperationContainer<?> container) throws OperationException {
boolean success = false;
boolean started = false;
try {
List<? extends OperationInfo> infos = container.listAll ();
List<NativeComponentUpdateElementImpl> customElements = new ArrayList<NativeComponentUpdateElementImpl> ();
for (OperationInfo operationInfo : infos) {
UpdateElementImpl impl = Trampoline.API.impl (operationInfo.getUpdateElement ());
assert impl instanceof NativeComponentUpdateElementImpl : "Impl of " + operationInfo.getUpdateElement () + " instanceof NativeComponentUpdateElementImpl.";
customElements.add ((NativeComponentUpdateElementImpl) impl);
}
assert customElements != null : "Some elements with custom installer found.";
if(progress!=null) {
progress.start(customElements.size());
}
started = true;
int index = 0;
affectedModules = new HashSet<UpdateElement> ();
for (NativeComponentUpdateElementImpl impl : customElements) {
if(progress!=null) {
progress.progress(NbBundle.getMessage(OperationSupportImpl.class, "OperationSupportImpl_Custom_Install", impl.getDisplayName()), ++index);
}
CustomInstaller installer = impl.getInstallInfo ().getCustomInstaller ();
assert installer != null : "CustomInstaller must found for " + impl.getUpdateElement ();
ProgressHandle handle = ProgressHandleFactory.createHandle("Installing " + impl.getDisplayName());
//handle.start();
success = installer.install (impl.getCodeName (),
impl.getSpecificationVersion () == null ? null : impl.getSpecificationVersion ().toString (),
handle);
try {
handle.finish();
} catch (IllegalStateException e) {
LOGGER.log(Level.FINE, "Can`t stop progress handle, likely was not started ", e);
}
if (success) {
UpdateUnitImpl unitImpl = Trampoline.API.impl (impl.getUpdateUnit ());
unitImpl.setInstalled (impl.getUpdateElement ());
affectedModules.add(impl.getUpdateElement());
} else {
throw new OperationException (OperationException.ERROR_TYPE.INSTALL, impl.getDisplayName ());
}
}
} finally {
if (progress != null && started) {
progress.finish ();
}
}
return success;
}
@Override
public void doCancel () throws OperationException {
assert false : "Not supported yet";
}
@Override
public void doRestart (Restarter restarter, ProgressHandle progress) throws OperationException {
markForRestart();
LifecycleManager.getDefault ().exit ();
// if exit&restart fails => use restart later as fallback
doRestartLater (restarter);
}
@Override
public void doRestartLater (Restarter restarter) {
// shedule module for restart
markForRestart();
if(affectedModules!=null) {
for (UpdateElement el : affectedModules) {
UpdateUnitFactory.getDefault().scheduleForRestart (el);
}
}
}
}
private static class ForCustomUninstall extends OperationSupportImpl {
private Collection<UpdateElement> affectedModules = null;
@Override
public synchronized Boolean doOperation(ProgressHandle progress,
OperationContainer<?> container) throws OperationException {
boolean success = false;
boolean started = false;
try {
List<? extends OperationInfo> infos = container.listAll ();
List<NativeComponentUpdateElementImpl> customElements = new ArrayList<NativeComponentUpdateElementImpl> ();
for (OperationInfo operationInfo : infos) {
UpdateElementImpl impl = Trampoline.API.impl (operationInfo.getUpdateElement ());
assert impl instanceof NativeComponentUpdateElementImpl : "Impl of " + operationInfo.getUpdateElement () + " instanceof NativeComponentUpdateElementImpl.";
customElements.add ((NativeComponentUpdateElementImpl) impl);
}
assert customElements != null : "Some elements with custom installer found.";
progress.start(customElements.size());
started = true;
int index = 0;
affectedModules = new HashSet<UpdateElement> ();
for (NativeComponentUpdateElementImpl impl : customElements) {
progress.progress(NbBundle.getMessage(OperationSupportImpl.class, "OperationSupportImpl_Custom_Uninstall", impl.getDisplayName()), ++index);
CustomUninstaller uninstaller = impl.getNativeItem ().getUpdateItemDeploymentImpl ().getCustomUninstaller ();
assert uninstaller != null : "CustomInstaller must found for " + impl.getUpdateElement ();
ProgressHandle handle = ProgressHandleFactory.createHandle("Installing " + impl.getDisplayName());
success = uninstaller.uninstall (impl.getCodeName (),
impl.getSpecificationVersion () == null ? null : impl.getSpecificationVersion ().toString (),
handle);
handle.finish();
if (success) {
UpdateUnitImpl unitImpl = Trampoline.API.impl (impl.getUpdateUnit ());
unitImpl.setAsUninstalled ();
affectedModules.add(impl.getUpdateElement());
} else {
throw new OperationException (OperationException.ERROR_TYPE.UNINSTALL, impl.getDisplayName ());
}
}
} finally {
if (progress != null && started) {
progress.finish ();
}
}
return success;
}
@Override
public void doCancel () throws OperationException {
assert false : "Not supported yet";
}
@Override
public void doRestart (Restarter restarter, ProgressHandle progress) throws OperationException {
markForRestart();
LifecycleManager.getDefault ().exit ();
// if exit&restart fails => use restart later as fallback
doRestartLater (restarter);
}
@Override
public void doRestartLater (Restarter restarter) {
// shedule module for restart
markForRestart();
if(affectedModules!=null) {
for (UpdateElement el : affectedModules) {
UpdateUnitFactory.getDefault().scheduleForRestart (el);
}
}
}
}
private static void markForRestart() {
try {
LifecycleManager.getDefault().markForRestart();
} catch (UnsupportedOperationException x) {
LOGGER.log(Level.INFO, null, x);
}
}
}