org.netbeans.modules.autoupdate.services.OperationContainerImpl Maven / Gradle / Ivy
The newest version!
/*
* 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.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.autoupdate.*;
import org.netbeans.api.autoupdate.OperationContainer.OperationInfo;
import org.openide.modules.Dependency;
import org.openide.modules.ModuleInfo;
/**
*
* @author Radek Matous, Jiri Rechtacek
*/
public final class OperationContainerImpl {
private boolean upToDate = false;
private File unpack200;
private OperationContainerImpl () {}
public static final Logger LOGGER = Logger.getLogger (OperationContainerImpl.class.getName ());
private final List> operations = new CopyOnWriteArrayList>();
private Throwable lastModified;
private final Collection> affectedEagers = new HashSet> ();
public static OperationContainerImpl createForInstall () {
return new OperationContainerImpl (OperationType.INSTALL);
}
public static OperationContainerImpl createForInternalUpdate () {
return new OperationContainerImpl (OperationType.INTERNAL_UPDATE);
}
public static OperationContainerImpl createForUpdate () {
return new OperationContainerImpl (OperationType.UPDATE);
}
public static OperationContainerImpl createForDirectInstall() {
OperationContainerImpl impl = new OperationContainerImpl(OperationType.INSTALL);
impl.delegate = OperationContainer.createForUpdate();
return impl;
}
public static OperationContainerImpl createForDirectUpdate() {
OperationContainerImpl impl = new OperationContainerImpl(OperationType.UPDATE);
impl.delegate = OperationContainer.createForUpdate();
return impl;
}
public static OperationContainerImpl createForUninstall () {
return new OperationContainerImpl (OperationType.UNINSTALL);
}
public static OperationContainerImpl createForDirectUninstall () {
return new OperationContainerImpl (OperationType.DIRECT_UNINSTALL);
}
public static OperationContainerImpl createForEnable () {
return new OperationContainerImpl (OperationType.ENABLE);
}
public static OperationContainerImpl createForDisable () {
return new OperationContainerImpl (OperationType.DISABLE);
}
public static OperationContainerImpl createForDirectDisable () {
return new OperationContainerImpl (OperationType.DIRECT_DISABLE);
}
public static OperationContainerImpl createForInstallNativeComponent () {
return new OperationContainerImpl (OperationType.CUSTOM_INSTALL);
}
public static OperationContainerImpl createForUninstallNativeComponent () {
return new OperationContainerImpl (OperationType.CUSTOM_UNINSTALL);
}
@SuppressWarnings({"unchecked"})
public OperationInfo add (UpdateUnit updateUnit, UpdateElement updateElement) throws IllegalArgumentException {
OperationInfo retval = null;
boolean isValid = isValid (updateUnit, updateElement);
if (UpdateUnitFactory.getDefault().isScheduledForRestart (updateElement)) {
LOGGER.log (Level.INFO, updateElement + " is scheduled for restart IDE.");
throw new IllegalArgumentException (updateElement + " is scheduled for restart IDE.");
}
if (!isValid) {
throw new IllegalArgumentException("Invalid " + updateUnit + " for operation " + type);
}
if (isValid) {
switch (type) {
case UNINSTALL :
case DIRECT_UNINSTALL :
case CUSTOM_UNINSTALL :
case ENABLE :
case DISABLE :
case DIRECT_DISABLE :
if (updateUnit.getInstalled () != updateElement) {
throw new IllegalArgumentException (updateUnit.getInstalled () +
" and " + updateElement + " must be same for operation " + type);
}
break;
case INSTALL :
case UPDATE :
case CUSTOM_INSTALL:
if (updateUnit.getInstalled () == updateElement) {
throw new IllegalArgumentException (updateUnit.getInstalled () +
" and " + updateElement + " cannot be same for operation " + type);
}
break;
case INTERNAL_UPDATE:
/*
if (updateUnit.getInstalled () != updateElement) {
throw new IllegalArgumentException (updateUnit.getInstalled () +
" and " + updateElement + " must be same for operation " + type);
}*/
break;
default:
assert false : "Unknown type of operation " + type;
}
}
synchronized(this) {
if (!contains (updateUnit, updateElement)) {
retval = Trampoline.API.createOperationInfo (new OperationInfoImpl (updateUnit, updateElement));
assert retval != null : "Null support for " + updateUnit + " and " + updateElement;
changeState (operations.add (retval));
boolean asserts = false;
assert asserts = true;
if (asserts) {
lastModified = new Exception("Added operation: " + retval);
}
}
}
return retval;
}
public boolean remove (UpdateElement updateElement) {
OperationInfo toRemove = find (updateElement);
if (toRemove != null) {
remove (toRemove);
}
return toRemove != null;
}
public boolean contains (UpdateElement updateElement) {
return find (updateElement) != null;
}
private OperationInfo find (UpdateElement updateElement) {
OperationInfo toRemove = null;
for (OperationInfo info : listAll ()) {
if (info.getUpdateElement ().equals (updateElement)) {
toRemove = info;
break;
}
}
return toRemove;
}
private boolean contains (UpdateUnit unit, UpdateElement element) {
List> infos = operations;
for (OperationInfo info : infos) {
if (info.getUpdateElement ().equals (element) ||
info.getUpdateUnit ().equals (unit)) {
return true;
}
}
return false;
}
private List> listAll () {
return Collections.unmodifiableList(operations);
}
public synchronized List> listAllWithPossibleEager () {
if (upToDate) {
return listAll();
}
clearCache ();
//if operations contains only first class modules - don`t search for eagers.
boolean checkEagers = false;
for (OperationInfo> i : operations) {
if(!Utilities.isFirstClassModule(i.getUpdateElement())) {
checkEagers = true;
break;
}
}
// handle eager modules
if ((type == OperationType.INSTALL || type == OperationType.UPDATE || type==OperationType.INTERNAL_UPDATE) && checkEagers) {
Collection all = new HashSet (operations.size ());
for (OperationInfo> i : operations) {
all.add(i.getUpdateElement());
}
for (OperationInfo> i : operations) {
all.addAll(i.getRequiredElements());
}
// TODO: fragment modules are somewhat eager: they need to enable with their hosting module. They are not handled now,
// so unless they are also eager, they won't be autoincluded.
for (UpdateElement eagerEl : UpdateManagerImpl.getInstance ().getAvailableEagers ()) {
if(eagerEl.getUpdateUnit().isPending() || eagerEl.getUpdateUnit().getAvailableUpdates().isEmpty()) {
continue;
}
UpdateElementImpl impl = Trampoline.API.impl (eagerEl);
List infos = new ArrayList ();
if(impl instanceof ModuleUpdateElementImpl) {
ModuleUpdateElementImpl eagerImpl = (ModuleUpdateElementImpl) impl;
infos.add(eagerImpl.getModuleInfo ());
} else if (impl instanceof FeatureUpdateElementImpl) {
FeatureUpdateElementImpl eagerImpl = (FeatureUpdateElementImpl) impl;
infos.addAll(eagerImpl.getModuleInfos ());
} else {
assert false : eagerEl + " must instanceof ModuleUpdateElementImpl or FeatureUpdateElementImpl";
}
for(ModuleInfo mi: infos) {
Set reqs = new HashSet ();
for (Dependency dep : mi.getDependencies ()) {
Collection requestedElements = Utilities.handleDependency (eagerEl, dep, Collections.singleton (mi), new HashSet (),
type == OperationType.UPDATE || type == OperationType.INTERNAL_UPDATE);
if (requestedElements != null) {
for (UpdateElement req : requestedElements) {
reqs.add (req);
}
}
}
if ((! reqs.isEmpty() && all.containsAll(reqs) && ! all.contains (eagerEl)) ||
(reqs.isEmpty() && impl.getUpdateUnit().getInstalled()!=null && type == OperationType.UPDATE && operations.size() > 0)) {
// adds affectedEager into list of elements for the operation
OperationInfo i = null;
try {
if(impl instanceof ModuleUpdateElementImpl) {
i = add (eagerEl.getUpdateUnit (), eagerEl);
} else if (impl instanceof FeatureUpdateElementImpl) {
FeatureUpdateElementImpl eagerImpl = (FeatureUpdateElementImpl) impl;
for (UpdateElementImpl contained : eagerImpl.getContainedModuleElements()) {
if (contained.isEager()) {
i = add (contained.getUpdateUnit (), contained.getUpdateElement());
}
}
}
} catch (IllegalArgumentException e) {
//investigate the reason of 172220, 171975, 169588
boolean firstCondition = (! reqs.isEmpty() && all.containsAll (reqs) && ! all.contains (eagerEl));
boolean secondCondition = reqs.isEmpty() && impl.getUpdateUnit().getInstalled()!=null && type == OperationType.UPDATE && operations.size() > 0;
StringBuilder sb = new StringBuilder();
sb.append("\nIAE while adding eager element to the ").append(type).append(" container\n");
sb.append("\nEager: ").append(eagerEl);
sb.append("\nFirst condition : ").append(firstCondition);
sb.append("\nSecond condition : ").append(secondCondition);
sb.append("\nInstalled: ").append(impl.getUpdateUnit().getInstalled());
sb.append("\nPending: ").append(impl.getUpdateUnit().isPending());
sb.append("\nreqs: ").append(reqs).append(" (total : ").append(reqs.size()).append(")");
sb.append("\nall: ").append(all).append(" (total : ").append(all.size()).append(")");
sb.append("\noperation: ").append(operations).append(" (total: ").append(operations.size());
sb.append("\neager available updates: ").append(eagerEl.getUpdateUnit().getAvailableUpdates());
sb.append("\nUpdateElements in operations:");
for (OperationInfo> op : operations) {
sb.append("\n ").append(op.getUpdateElement());
}
sb.append("\nUpdateElements in all:");
for (UpdateElement elem : all) {
sb.append("\n ").append(elem);
}
sb.append("\n");
LOGGER.log(Level.INFO, sb.toString(), e);
throw e;
}
if (i != null) {
affectedEagers.add (i);
}
}
}
}
}
if (LOGGER.isLoggable (Level.FINE)) {
LOGGER.log (Level.FINE, "== do listAllWithPossibleEager for " + type + " operation ==");
for (OperationInfo info : operations) {
LOGGER.log (Level.FINE, "--> " + info.getUpdateElement ());
}
if (affectedEagers != null) {
LOGGER.log (Level.FINE, " == includes affected eagers for " + type + " operation ==");
for (OperationInfo eagerInfo : affectedEagers) {
LOGGER.log (Level.FINE, " --> " + eagerInfo.getUpdateElement ());
}
LOGGER.log (Level.FINE, " == done eagers. ==");
}
LOGGER.log (Level.FINE, "== done. ==");
}
upToDate = true;
return listAll();
}
public List> listInvalid () {
List> retval = new ArrayList>();
List> infos = listAll ();
for (OperationInfo oii: infos) {
// find type of operation
// differ primary element and required elements
// primary use-case can be Install but could required update of other elements
if (!isValid (oii.getUpdateUnit (), oii.getUpdateElement ())) {
retval.add (oii);
}
}
return retval;
}
public boolean isValid (UpdateUnit updateUnit, UpdateElement updateElement) {
if (updateElement == null) {
throw new IllegalArgumentException ("UpdateElement cannot be null for UpdateUnit " + updateUnit);
} else if (updateUnit == null) {
throw new IllegalArgumentException ("UpdateUnit cannot be null for UpdateElement " + updateElement);
}
boolean isValid;
switch (type) {
case INSTALL :
isValid = OperationValidator.isValidOperation (type, updateUnit, updateElement);
// at least first add must pass and respect type of operation
if (! isValid && operations.size () > 0) {
// try Update
isValid = OperationValidator.isValidOperation (OperationType.UPDATE, updateUnit, updateElement);
}
break;
case UPDATE :
isValid = OperationValidator.isValidOperation (type, updateUnit, updateElement);
// at least first add must pass and respect type of operation
if (! isValid && operations.size () > 0) {
// try Update
isValid = OperationValidator.isValidOperation (OperationType.INSTALL, updateUnit, updateElement);
}
break;
case INTERNAL_UPDATE:
isValid = OperationValidator.isValidOperation (type, updateUnit, updateElement);
// at least first add must pass and respect type of operation
if (! isValid && operations.size () > 0) {
// try Update
isValid = OperationValidator.isValidOperation (OperationType.UPDATE, updateUnit, updateElement);
}
if (! isValid && operations.size () > 0) {
// try Install
isValid = OperationValidator.isValidOperation (OperationType.INSTALL, updateUnit, updateElement);
}
break;
default:
isValid = OperationValidator.isValidOperation (type, updateUnit, updateElement);
}
return isValid;
}
public synchronized void remove (OperationInfo op) {
synchronized(this) {
changeState (operations.remove (op));
changeState (operations.removeAll (affectedEagers));
affectedEagers.clear ();
boolean asserts = false;
assert asserts = true;
if (asserts) {
lastModified = new Exception("Removed " + op); // NOI18N
}
}
}
public synchronized void removeAll () {
synchronized(this) {
changeState (true);
operations.clear ();
affectedEagers.clear ();
boolean asserts = false;
assert asserts = true;
if (asserts) {
lastModified = new Exception("Removed all"); // NOI18N
}
}
}
@Override
public String toString() {
StringWriter sb = new StringWriter();
PrintWriter pw = new PrintWriter(sb);
pw.print(super.toString());
if (lastModified != null) {
pw.println();
lastModified.printStackTrace(pw);
}
pw.flush();
return sb.toString();
}
private void clearCache () {
OperationValidator.clearMaps ();
}
private void changeState (boolean changed) {
if (changed) {
clearCache ();
}
upToDate = upToDate && ! changed;
}
public class OperationInfoImpl {
private final UpdateElement updateElement;
private final UpdateUnit uUnit;
private Set brokenDeps = null;
private OperationInfoImpl (UpdateUnit uUnit, UpdateElement updateElement) {
this.updateElement = updateElement;
this.uUnit = uUnit;
}
public UpdateElement/*or null*/ getUpdateElement () {
return updateElement;
}
public UpdateUnit/*or null*/ getUpdateUnit () {
return uUnit;
}
private List requiredElements;
public List getRequiredElements (){
if (upToDate && requiredElements != null) {
return requiredElements;
}
List moduleInfos = new ArrayList();
for (OperationContainer.OperationInfo oii : listAll ()) {
UpdateElementImpl impl = Trampoline.API.impl (oii.getUpdateElement ());
List infos = impl.getModuleInfos ();
assert infos != null : "ModuleInfo for UpdateElement " + oii.getUpdateElement () + " found.";
moduleInfos.addAll (infos);
}
brokenDeps = new HashSet ();
Set recommeded = new HashSet();
requiredElements = OperationValidator.getRequiredElements (type, getUpdateElement (), moduleInfos, brokenDeps, recommeded);
if (! brokenDeps.isEmpty() && ! recommeded.isEmpty()) {
brokenDeps = new HashSet ();
requiredElements = OperationValidator.getRequiredElements (type, getUpdateElement (), moduleInfos, brokenDeps, recommeded);
}
return requiredElements;
}
public Set getBrokenDependencies () {
if (! upToDate) {
brokenDeps = null;
}
if (brokenDeps != null) {
return brokenDeps;
}
// shortcut, because OperationValidator.getRequiredElements is NOT consistent with
// OperationValidator.getBrokenDependencies: while getRequiredElements ignores breakages of
// feature contents, getBrokenDependencies wille verify them in full, so feature's optional (platform-specific) module
// will report the whole feature as broken on activation or install.
if (requiredElements != null) {
getRequiredElements();
if (brokenDeps != null) {
return brokenDeps;
}
}
List moduleInfos = new ArrayList();
Set brokenContents = new HashSet<>();
for (OperationContainer.OperationInfo oii : listAll ()) {
UpdateElementImpl impl = Trampoline.API.impl (oii.getUpdateElement ());
Collection infos = impl.getModuleInfos ();
assert infos != null : "ModuleInfo for UpdateElement " + oii.getUpdateElement () + " found.";
moduleInfos.addAll (infos);
if (impl.getType() == UpdateManager.TYPE.FEATURE) {
// add unknown tokens / codenames as broken content.
brokenContents.addAll(((FeatureUpdateElementImpl)impl).getMissingElements());
}
}
brokenContents.addAll(OperationValidator.getBrokenDependencies (type, getUpdateElement (), moduleInfos));
return brokenContents;
}
/**
* Reports missing parts of the feature.
* @return missing/unknown directly contained codenames
*/
public Set getMissingParts() {
Set brokenContents = new HashSet<>();
for (OperationContainer.OperationInfo oii : listAll ()) {
UpdateElementImpl impl = Trampoline.API.impl (oii.getUpdateElement ());
if (impl instanceof FeatureUpdateElementImpl) {
brokenContents.addAll(((FeatureUpdateElementImpl)impl).getMissingElements());
}
}
return brokenContents;
}
}
/** Creates a new instance of OperationContainer */
private OperationContainerImpl (OperationType type) {
this.type = type;
}
public OperationType getType () {
return type;
}
public static enum OperationType {
/** Install UpdateElement
*/
INSTALL,
/** Uninstall UpdateElement
*/
UNINSTALL,
/** Internally update installed UpdateElement
without version increase */
INTERNAL_UPDATE,
/** Uninstall UpdateElement
on-the-fly */
DIRECT_UNINSTALL,
/** Update installed UpdateElement
to newer version. */
UPDATE,
/** Rollback installed UpdateElement
to previous version. */
REVERT,
/** Enable UpdateElement
*/
ENABLE,
/** Disable UpdateElement
*/
DIRECT_DISABLE,
/** Disable UpdateElement
on-the-fly */
DISABLE,
/** Install UpdateElement
with custom installer. */
CUSTOM_INSTALL,
/** Uninstall UpdateElement
with custom installer. */
CUSTOM_UNINSTALL
}
private OperationType type;
private OperationContainer delegate;
/**
* @return the unpack200 executable or {@code null}
*/
public final File getUnpack200() {
NO_PACK: if (unpack200 == null) {
final String jreHome = System.getProperty("java.home"); // NOI18N
if (jreHome == null) {
break NO_PACK;
}
File javaHome = new File(jreHome);
File pack200ux = new File(new File(javaHome, "bin"), "unpack200"); // NOI18N
if (pack200ux.canExecute()) {
return pack200ux;
}
File pack200exe = new File(new File(javaHome, "bin"), "unpack200.exe"); // NOI18N
if (pack200exe.canExecute()) {
return pack200exe;
}
}
return unpack200;
}
/**
* @param pack200 the pack200 to set
*/
public final void setUnpack200(File pack200) {
this.unpack200 = pack200;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy