[NETBEANS-1147,NETBEANS-1298] Autoupdated fragment restarts only if its host is already ON
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
index 656f929..e70e04c 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
@@ -34,6 +34,7 @@
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.logging.Level;
@@ -46,8 +47,12 @@
 import org.netbeans.api.autoupdate.OperationContainer.OperationInfo;
 import org.netbeans.api.autoupdate.OperationSupport.Restarter;
 import org.netbeans.api.progress.ProgressHandle;
+import org.netbeans.modules.autoupdate.updateprovider.AutoupdateInfoParser;
+import org.netbeans.modules.autoupdate.updateprovider.ModuleItem;
 import org.netbeans.modules.autoupdate.updateprovider.NetworkAccess;
 import org.netbeans.modules.autoupdate.updateprovider.NetworkAccess.Task;
+import org.netbeans.modules.autoupdate.updateprovider.UpdateItemImpl;
+import org.netbeans.spi.autoupdate.UpdateItem;
 import org.netbeans.updater.ModuleDeactivator;
 import org.netbeans.updater.ModuleUpdater;
 import org.netbeans.updater.UpdateTracking;
@@ -59,6 +64,7 @@
 import org.openide.util.Exceptions;
 import org.openide.util.NbBundle;
 import org.openide.util.NbCollections;
+import org.xml.sax.SAXException;
 
 /**
  *
@@ -1083,6 +1089,8 @@
                         break;
                 }
             }
+            updateFragmentStatus(impl, nbmFile);
+            
         } catch (IOException ioe) {
             LOG.log (Level.INFO, ioe.getMessage (), ioe);
             res = "BAD_DOWNLOAD";
@@ -1099,6 +1107,45 @@
         return el.getDownloadSize ();
     }
     
+    private void updateFragmentStatus(UpdateElementImpl el, File nbmFile) throws IOException {
+        UpdateItemImpl impl = el.getInstallInfo().getUpdateItemImpl();
+        if (!(impl instanceof ModuleItem)) {
+            return;
+        }
+        ModuleItem mod = (ModuleItem)impl;
+        if (mod.isFragment()) {
+            String fhost = mod.getFragmentHost();
+            Module m = Utilities.toModule(fhost);
+            if (m != null && m.isEnabled()) {
+                impl.setNeedsRestart(Boolean.TRUE);
+            }
+        }
+
+        Map<String, UpdateItem> items;
+        try {
+            items = AutoupdateInfoParser.getUpdateItems(nbmFile);
+        } catch (SAXException ex) {
+            throw new IOException(ex);
+        }
+        for (UpdateItem realItem : items.values()) {
+            UpdateItemImpl realImpl = Trampoline.SPI.impl(realItem);
+            if (realImpl instanceof ModuleItem) {
+                ModuleItem realMod = (ModuleItem)realImpl;
+                if (!realMod.getCodeName().equals(el.getCodeName())) {
+                    continue;
+                }
+                String fhost = realMod.getFragmentHost();
+                if (fhost != null && !impl.isFragment()) {
+                    mod.setFragmentHost(fhost);
+                    Module m = Utilities.toModule(fhost);
+                    if (m != null && m.isEnabled()) {
+                        impl.setNeedsRestart(Boolean.TRUE);
+                    }
+                }
+            }
+        }
+    }
+    
     private boolean needsRestart (boolean isUpdate, UpdateElementImpl toUpdateImpl, File dest) {
         return InstallManager.needsRestart (isUpdate, toUpdateImpl, dest);
     }
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationContainerImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationContainerImpl.java
index 06f8797..6cbe539 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationContainerImpl.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationContainerImpl.java
@@ -207,6 +207,8 @@
             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;
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java
index 8c3c440..7c05dd2 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java
@@ -557,6 +557,14 @@
         return getModuleInstance(uUnit.getCodeName(), null); // XXX
     }
     
+    public static Module toModule(String codeNameBase) {
+        ModuleInfo mi = ModuleCache.getInstance().find(codeNameBase);
+        if (mi instanceof Module) {
+            return (Module)mi;
+        }
+        return null;
+    }
+    
     public static Module toModule(String codeNameBase, SpecificationVersion specificationVersion) {
         return getModuleInstance(codeNameBase, specificationVersion);
     }
@@ -604,6 +612,8 @@
             Set<Dependency> deps = new HashSet<Dependency> (el.getModuleInfo ().getDependencies ());
             Set<ModuleInfo> availableInfos = new HashSet<ModuleInfo> (infos);
             
+            maybeAddImplicitHostDependency(element, deps);
+            
             int max_counter = el.getType().equals(UpdateManager.TYPE.KIT_MODULE) ? 2 : 1;
             int counter = max_counter;
             boolean aggressive = topAggressive && counter > 0;
@@ -618,8 +628,14 @@
                     UpdateUnit uu = toUpdateUnit(dep.getName());
                     if (uu != null && uu.getInstalled() != null) {
                         ModuleUpdateElementImpl em = (ModuleUpdateElementImpl) Trampoline.API.impl(uu.getInstalled());
-                        if (em.getInstallInfo().getUpdateItemImpl().isFragment()) {
-                            el.getInstallInfo().getUpdateItemImpl().setNeedsRestart(true);
+                        // fragments which are installed, but not enabled yet, and have live host module will cause
+                        // restart
+                        if (em.getInstallInfo().getUpdateItemImpl().isFragment() && !uu.getInstalled().isEnabled()) {
+                            String fh = em.getInstallInfo().getUpdateItemImpl().getFragmentHost();
+                            Module m = Utilities.toModule(fh);
+                            if (m != null && m.isEnabled()) {
+                                el.getInstallInfo().getUpdateItemImpl().setNeedsRestart(true);
+                            }
                         }
                     }
                 }
@@ -736,6 +752,7 @@
                                 Set<Dependency> deps = new HashSet<Dependency>(tryUpdated.getDependencies());
                                 Set<ModuleInfo> availableInfos = new HashSet<ModuleInfo>(forInstall);
                                 Set<Dependency> newones;
+                                maybeAddImplicitHostDependency(tryUE, deps);
                                 while (!(newones = processDependencies(deps, moreRequested, availableInfos, brokenDependencies, tryUE, aggressive, null, false)).isEmpty()) {
                                     deps = newones;
                                 }
@@ -784,6 +801,20 @@
 
         return moreRequested;
     }
+    
+    private static boolean maybeAddImplicitHostDependency(UpdateElement el, Set<Dependency> deps) {
+        
+        // check fragment, add implicit dependencies
+        UpdateElementImpl elImpl = Trampoline.API.impl(el);
+        UpdateItemImpl uiImpl = elImpl.getInstallInfo().getUpdateItemImpl();
+        if (!uiImpl.isFragment()) {
+            return false;
+        }
+        String fhost = uiImpl.getFragmentHost();
+        Collection<Dependency> hostDep = Dependency.create(Dependency.TYPE_MODULE, fhost);
+        err.fine(hostDep.toString());
+        return deps.addAll(hostDep);
+    }
 
     private static Set<Dependency> processDependencies (final Set<Dependency> original,
             Set<UpdateElement> retval,
@@ -814,6 +845,7 @@
                     availableInfos.add (reqM.getModuleInfo ());
                     retval.add (req);
                     res.addAll (reqM.getModuleInfo ().getDependencies ());
+                    maybeAddImplicitHostDependency(req, res);
                 }
             }
         }
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java
index 692fb66..d95e1b4 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java
@@ -34,12 +34,12 @@
 import java.util.zip.GZIPInputStream;
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
+import org.netbeans.Module;
 import org.netbeans.modules.autoupdate.services.Trampoline;
 import org.netbeans.modules.autoupdate.services.UpdateLicenseImpl;
 import org.netbeans.modules.autoupdate.services.Utilities;
 import org.netbeans.spi.autoupdate.UpdateItem;
 import org.netbeans.spi.autoupdate.UpdateLicense;
-import org.openide.util.Exceptions;
 import org.xml.sax.*;
 import org.xml.sax.helpers.DefaultHandler;
 
@@ -510,7 +510,7 @@
         private URI base;
         private String catalogDate;
         
-        private boolean isFragment = false;
+        private String fragmentHost;
         
         private static ModuleDescriptor md = null;
         
@@ -545,7 +545,7 @@
             String autoload = module.getValue (MODULE_ATTR_AUTOLOAD);
             String preferred = module.getValue(MODULE_ATTR_IS_PREFERRED_UPDATE);
                         
-            needsRestart = isFragment || needsrestart == null || needsrestart.trim ().length () == 0 ? null : Boolean.valueOf (needsrestart);
+            needsRestart = needsrestart == null || needsrestart.trim ().length () == 0 ? null : Boolean.valueOf (needsrestart);
             isGlobal = global == null || global.trim ().length () == 0 ? null : Boolean.valueOf (global);
             isEager = Boolean.parseBoolean (eager);
             isAutoload = Boolean.parseBoolean (autoload);
@@ -561,10 +561,9 @@
         
         public void appendManifest (Attributes manifest) {
             specVersion = manifest.getValue (MANIFEST_ATTR_SPECIFICATION_VERSION);
-            String fragmentHost = manifest.getValue(MANIFEST_ATTR_FRAGMENT_HOST);            
-            isFragment = fragmentHost != null && !fragmentHost.isEmpty();
-            if (isFragment) {
-                needsRestart = true;
+            fragmentHost = manifest.getValue(MANIFEST_ATTR_FRAGMENT_HOST);   
+            if (fragmentHost != null && fragmentHost.isEmpty()) {
+                fragmentHost = null;
             }
             mf = getManifest (manifest);
             id = moduleCodeName + '_' + specVersion; // NOI18N
@@ -600,7 +599,9 @@
             // read module notification
             UpdateItemImpl impl = Trampoline.SPI.impl(res);
             ((ModuleItem) impl).setModuleNotification (notification);
-            
+            if (fragmentHost != null) {
+                ((ModuleItem) impl).setFragmentHost (fragmentHost);
+            }
             // clean-up ModuleDescriptor
             cleanUp ();
             
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateInfoParser.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateInfoParser.java
index a09461a..6082f3a 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateInfoParser.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateInfoParser.java
@@ -145,6 +145,7 @@
     private static final String MODULE_ATTR_LICENSE = "license";
     
     private static final String MANIFEST_ATTR_SPECIFICATION_VERSION = "OpenIDE-Module-Specification-Version";
+    private static final String MANIFEST_ATTR_FRAGMENT_HOST = "OpenIDE-Module-Fragment-Host";
     
     private static final String L10N_ATTR_LOCALE = "langcode";
     private static final String L10N_ATTR_BRANDING = "brandingcode";
@@ -296,6 +297,7 @@
         private String author;
         private String publishDate;
         private String notification;
+        private String fragmentHost;
 
         private Boolean needsRestart;
         private Boolean isGlobal;
@@ -342,6 +344,7 @@
         
         public void appendManifest (Attributes manifest) {
             specVersion = manifest.getValue (MANIFEST_ATTR_SPECIFICATION_VERSION);
+            fragmentHost = manifest.getValue(MANIFEST_ATTR_FRAGMENT_HOST);
             mf = getManifest (manifest);
         }
         
@@ -381,6 +384,7 @@
             // read module notification
             UpdateItemImpl impl = Trampoline.SPI.impl(res);
             ((ModuleItem) impl).setModuleNotification (notification);
+            ((ModuleItem) impl).setFragmentHost(fragmentHost);
             
             return res;
         }
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/InstalledModuleItem.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/InstalledModuleItem.java
index 2aa44c5..99e557a 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/InstalledModuleItem.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/InstalledModuleItem.java
@@ -129,7 +129,8 @@
     } 
    
     @Override
-    public boolean isFragment() {
-        return info.getAttribute("OpenIDE-Module-Fragment-Host") != null; // NOI18N
+    public String getFragmentHost() {
+        Object o = info.getAttribute("OpenIDE-Module-Fragment-Host"); // NOI18N
+        return o instanceof String ? (String)o : null;
     }
 }
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/ModuleItem.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/ModuleItem.java
index a6ee368..fc4f8b2 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/ModuleItem.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/ModuleItem.java
@@ -49,6 +49,8 @@
     private boolean isPreferedUpdate;
     private String moduleNotification = null;
     
+    private String fragmentHost;
+    
     private URL distribution;
     private Manifest manifest;
 
@@ -182,6 +184,14 @@
     void setModuleNotification (String notification) {
         this.moduleNotification = notification;
     }
+    
+    public void setFragmentHost(String fhost) {
+        this.fragmentHost = fhost;
+    }
+
+    public String getFragmentHost() {
+        return fragmentHost;
+    }
 
     @Override
     public void setUpdateLicenseImpl (UpdateLicenseImpl licenseImpl) {
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java
index eb895f0..0f639dd 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java
@@ -58,6 +58,10 @@
     public abstract void setNeedsRestart(Boolean needsRestart);
     
     public boolean isFragment(){
-        return false;
+        return getFragmentHost() != null;
+    }
+    
+    public String getFragmentHost() {
+        return null;
     }
 }
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/DefaultTestCase.java b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/DefaultTestCase.java
index 7f0cb75..5d62d1c 100644
--- a/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/DefaultTestCase.java
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/DefaultTestCase.java
@@ -68,6 +68,10 @@
         }
     }
     
+    protected InputStream updateCatalogContents() {
+        return TestUtils.class.getResourceAsStream("data/updates.xml");
+    }
+    
     protected void setUp() throws Exception {
         super.setUp();
         this.clearWorkDir ();
@@ -76,7 +80,7 @@
             catalogFile.createNewFile();
         }
         catalogURL = org.openide.util.Utilities.toURI(catalogFile).toURL();
-        populateCatalog(TestUtils.class.getResourceAsStream("data/updates.xml"));
+        populateCatalog(updateCatalogContents());
         
         TestUtils.setUserDir (getWorkDirPath ());
         TestUtils.testInit();
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/data/org-yourorghere-fragment.nbm b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/data/org-yourorghere-fragment.nbm
new file mode 100644
index 0000000..267d0eb
--- /dev/null
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/data/org-yourorghere-fragment.nbm
Binary files differ
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/data/updates-bad-fragment.xml b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/data/updates-bad-fragment.xml
new file mode 100644
index 0000000..0687b72
--- /dev/null
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/data/updates-bad-fragment.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!DOCTYPE module_updates PUBLIC "-//NetBeans//DTD Autoupdate Catalog 2.6//EN" "http://www.netbeans.org/dtds/autoupdate-catalog-2_6.dtd">
+<!--
+
+    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.
+
+-->
+<module_updates timestamp="10/28/20/17/11/2017">
+
+    <module codenamebase="org.yourorghere.brokendepending" distribution="nbresloc:/org/netbeans/api/autoupdate/data/org-yourorghere-brokendepending.nbm" downloadsize="3208" homepage="" license="AD9FBBC9" moduleauthor="Steffen Dietz" needsrestart="false" releasedate="2017/11/17">
+        <manifest AutoUpdate-Show-In-Client="true" OpenIDE-Module="org.yourorghere.brokendepending" OpenIDE-Module-Implementation-Version="171117" OpenIDE-Module-Java-Dependencies="Java &gt; 1.7" OpenIDE-Module-Module-Dependencies="org.yourorghere.unreachable &gt; 1.0" OpenIDE-Module-Name="brokendepending" OpenIDE-Module-Requires="org.openide.modules.ModuleFormat1" OpenIDE-Module-Specification-Version="1.0"/>
+    
+    </module>
+
+    <module codenamebase="org.yourorghere.depending" distribution="nbresloc:/org/netbeans/api/autoupdate/data/org-yourorghere-depending.nbm" downloadsize="3186" homepage="" license="AD9FBBC9" moduleauthor="Steffen Dietz" needsrestart="false" releasedate="2017/11/17">
+        <manifest AutoUpdate-Show-In-Client="true" OpenIDE-Module="org.yourorghere.depending" OpenIDE-Module-Implementation-Version="171117" OpenIDE-Module-Java-Dependencies="Java &gt; 1.7" OpenIDE-Module-Module-Dependencies="org.yourorghere.engine &gt; 1.0, org.yourorghere.independent &gt; 1.0" OpenIDE-Module-Name="depending" OpenIDE-Module-Requires="org.openide.modules.ModuleFormat1" OpenIDE-Module-Specification-Version="1.0"/>
+    
+    </module>
+
+    <module codenamebase="org.yourorghere.depending_on_new_one_engine" distribution="nbresloc:/org/netbeans/api/autoupdate/data/org-yourorghere-depending_on_new_one_engine.nbm" downloadsize="3263" homepage="" license="AD9FBBC9" moduleauthor="Steffen Dietz" needsrestart="false" releasedate="2017/11/17">
+        <manifest AutoUpdate-Show-In-Client="true" OpenIDE-Module="org.yourorghere.depending_on_new_one_engine" OpenIDE-Module-Implementation-Version="171117" OpenIDE-Module-Java-Dependencies="Java &gt; 1.7" OpenIDE-Module-Module-Dependencies="org.yourorghere.engine &gt; 1.0" OpenIDE-Module-Name="depending_on_new_one_engine" OpenIDE-Module-Requires="org.openide.modules.ModuleFormat1" OpenIDE-Module-Specification-Version="1.0"/>
+    
+    </module>
+
+    <module codenamebase="org.yourorghere.engine" distribution="nbresloc:/org/netbeans/api/autoupdate/data/org-yourorghere-engine.nbm" downloadsize="3136" homepage="" license="AD9FBBC9" moduleauthor="Steffen Dietz" needsrestart="false" releasedate="2017/11/17">
+        <manifest AutoUpdate-Show-In-Client="true" OpenIDE-Module="org.yourorghere.engine" OpenIDE-Module-Implementation-Version="171117" OpenIDE-Module-Java-Dependencies="Java &gt; 1.7" OpenIDE-Module-Module-Dependencies="org.yourorghere.independent &gt; 1.0" OpenIDE-Module-Name="engine" OpenIDE-Module-Requires="org.openide.modules.ModuleFormat1" OpenIDE-Module-Specification-Version="1.0"/>
+
+    </module>
+
+    <module codenamebase="org.yourorghere.engine" distribution="nbresloc:/org/netbeans/api/autoupdate/data/org-yourorghere-engine-1-1.nbm" downloadsize="3136" homepage="" license="AD9FBBC9" moduleauthor="Steffen Dietz" needsrestart="false" releasedate="2017/11/17">
+        <manifest AutoUpdate-Show-In-Client="true" OpenIDE-Module="org.yourorghere.engine" OpenIDE-Module-Implementation-Version="171117" OpenIDE-Module-Java-Dependencies="Java &gt; 1.7" OpenIDE-Module-Module-Dependencies="org.yourorghere.independent &gt; 1.0" OpenIDE-Module-Name="engine" OpenIDE-Module-Requires="org.openide.modules.ModuleFormat1" OpenIDE-Module-Specification-Version="1.1"/>
+    
+    </module>
+
+    <module codenamebase="org.yourorghere.independent" distribution="nbresloc:/org/netbeans/api/autoupdate/data/org-yourorghere-independent.nbm" downloadsize="3128" homepage="" license="AD9FBBC9" moduleauthor="Steffen Dietz" needsrestart="false" releasedate="2017/11/17">
+        <manifest AutoUpdate-Show-In-Client="true" OpenIDE-Module="org.yourorghere.independent" OpenIDE-Module-Implementation-Version="171117" OpenIDE-Module-Java-Dependencies="Java &gt; 1.7" OpenIDE-Module-Name="independent" OpenIDE-Module-Requires="org.openide.modules.ModuleFormat1" OpenIDE-Module-Specification-Version="1.0"/>
+    
+    </module>
+
+    <module codenamebase="org.yourorghere.refresh_providers_test" distribution="nbresloc:/org/netbeans/api/autoupdate/data/org-yourorghere-refresh_providers_test.nbm" downloadsize="3206" homepage="" license="AD9FBBC9" moduleauthor="Steffen Dietz" needsrestart="true" releasedate="2017/11/17">
+        <manifest AutoUpdate-Show-In-Client="true" OpenIDE-Module="org.yourorghere.refresh_providers_test" OpenIDE-Module-Implementation-Version="171117" OpenIDE-Module-Java-Dependencies="Java &gt; 1.7" OpenIDE-Module-Name="refresh_providers_test" OpenIDE-Module-Requires="org.openide.modules.ModuleFormat1" OpenIDE-Module-Specification-Version="1.0"/>
+    
+    </module>
+
+    <module codenamebase="com.example.testmodule.cluster" distribution="nbresloc:/org/netbeans/api/autoupdate/data/com-example-testmodule-cluster.nbm" downloadsize="0" homepage="" license="AD9FBBC9" moduleauthor="Steffen Dietz" needsrestart="false" releasedate="2017/11/17" targetcluster="test">
+        <manifest AutoUpdate-Show-In-Client="true" OpenIDE-Module="com.example.testmodule.cluster" OpenIDE-Module-Implementation-Version="171117" OpenIDE-Module-Java-Dependencies="Java &gt; 1.7" OpenIDE-Module-Name="testmodule_cluster" OpenIDE-Module-Requires="org.openide.modules.ModuleFormat1" OpenIDE-Module-Specification-Version="1.0"/>
+
+    </module>
+
+    <module codenamebase="org.yourorghere.fragment" distribution="nbresloc:/org/netbeans/api/autoupdate/data/org-yourorghere-fragment.nbm" downloadsize="0" homepage="" license="AD9FBBC9" moduleauthor="Steffen Dietz" needsrestart="false" releasedate="2018/10/01">
+        <manifest AutoUpdate-Show-In-Client="true" OpenIDE-Module="org.yourorghere.fragment" OpenIDE-Module-Implementation-Version="171117" OpenIDE-Module-Java-Dependencies="Java &gt; 1.7" OpenIDE-Module-Name="fragment" OpenIDE-Module-Requires="org.openide.modules.ModuleFormat1" OpenIDE-Module-Specification-Version="1.0"/>
+    </module>
+
+<license name="AD9FBBC9">[NO LICENSE SPECIFIED]
+</license>
+</module_updates>
+
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/data/updates.xml b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/data/updates.xml
index f9c9fac..fe4783d 100644
--- a/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/data/updates.xml
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/data/updates.xml
@@ -63,6 +63,10 @@
 
     </module>
 
+    <module codenamebase="org.yourorghere.fragment" distribution="nbresloc:/org/netbeans/api/autoupdate/data/org-yourorghere-fragment.nbm" downloadsize="0" homepage="" license="AD9FBBC9" moduleauthor="Steffen Dietz" needsrestart="false" releasedate="2018/10/01">
+        <manifest AutoUpdate-Show-In-Client="true" OpenIDE-Module="org.yourorghere.fragment" OpenIDE-Module-Implementation-Version="171117" OpenIDE-Module-Java-Dependencies="Java &gt; 1.7" OpenIDE-Module-Fragment-Host="org.yourorghere.engine" OpenIDE-Module-Name="fragment" OpenIDE-Module-Requires="org.openide.modules.ModuleFormat1" OpenIDE-Module-Specification-Version="1.0"/>
+    </module>
+
 <license name="AD9FBBC9">[NO LICENSE SPECIFIED]
 </license>
 </module_updates>
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentDisabledNoRestartTest.java b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentDisabledNoRestartTest.java
new file mode 100644
index 0000000..8c83863
--- /dev/null
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentDisabledNoRestartTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.util.concurrent.atomic.AtomicReference;
+import org.netbeans.api.autoupdate.OperationContainer;
+import org.netbeans.api.autoupdate.OperationSupport;
+import org.netbeans.api.autoupdate.UpdateUnit;
+
+/**
+ * Checks that a fragment installed on top of already enabled module will
+ * cause a restart.
+ * 
+ * @author sdedic
+ */
+public class FragmentDisabledNoRestartTest extends FragmentModuleTestBase {
+    
+    public FragmentDisabledNoRestartTest(String testName) {
+        super(testName);
+    }
+
+    @Override
+    public void testSelf() throws Exception {
+        UpdateUnit hostUnit = UpdateManagerImpl.getInstance().getUpdateUnit("org.yourorghere.engine");
+        installModule(hostUnit, null);
+        
+        OperationContainer<OperationSupport> container = OperationContainer.createForDirectDisable ();
+        container.add(hostUnit.getInstalled());
+        OperationSupport support = container.getSupport ();
+        support.doOperation (null);
+
+        UpdateUnit toInstall = UpdateManagerImpl.getInstance().getUpdateUnit(moduleCodeNameBaseForTest());
+        
+        AtomicReference<OperationSupport.Restarter> restarter = new AtomicReference<>();
+        installModuleWithRestart(toInstall, null, restarter);
+        
+        assertNull ("Module must not cause restart, host is disabled", restarter.get());
+    }
+    
+}
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentIEnabledRestartTest.java b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentIEnabledRestartTest.java
new file mode 100644
index 0000000..55cd3b4
--- /dev/null
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentIEnabledRestartTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.util.concurrent.atomic.AtomicReference;
+import org.netbeans.api.autoupdate.OperationSupport;
+import org.netbeans.api.autoupdate.UpdateUnit;
+
+/**
+ * Checks that a fragment installed on top of already enabled module will
+ * cause a restart.
+ * 
+ * @author sdedic
+ */
+public class FragmentIEnabledRestartTest extends FragmentModuleTestBase {
+    
+    public FragmentIEnabledRestartTest(String testName) {
+        super(testName);
+    }
+
+    @Override
+    public void testSelf() throws Exception {
+        installModule(UpdateManagerImpl.getInstance().getUpdateUnit("org.yourorghere.engine"), null);
+
+        UpdateUnit toInstall = UpdateManagerImpl.getInstance().getUpdateUnit(moduleCodeNameBaseForTest());
+        
+        AtomicReference<OperationSupport.Restarter> restarter = new AtomicReference<>();
+        installModuleWithRestart(toInstall, null, restarter);
+        assertNotNull(restarter.get());
+    }
+    
+}
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentInstallHostModuleTest.java b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentInstallHostModuleTest.java
new file mode 100644
index 0000000..d104697
--- /dev/null
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentInstallHostModuleTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.util.concurrent.atomic.AtomicReference;
+import org.netbeans.api.autoupdate.OperationSupport;
+import org.netbeans.api.autoupdate.UpdateUnit;
+import org.openide.modules.ModuleInfo;
+
+/**
+ * Checks that a fragment module will force install its host as a dependency
+ * even though the dependency is not declared
+ * 
+ * @author sdedic
+ */
+public class FragmentInstallHostModuleTest extends FragmentModuleTestBase {
+    
+    public FragmentInstallHostModuleTest(String testName) {
+        super(testName);
+    }
+
+    @Override
+    public void testSelf() throws Exception {
+        UpdateUnit toInstall = UpdateManagerImpl.getInstance().getUpdateUnit(moduleCodeNameBaseForTest());
+        AtomicReference<OperationSupport.Restarter> restarter = new AtomicReference<>();
+        installModuleWithRestart(toInstall, null, restarter);
+        assertNull("Fragment host will be newly installed, no restart is needed", restarter.get());
+        
+        ModuleInfo transitiveMod = Utilities.toModule("org.yourorghere.independent");
+        ModuleInfo hostMod = Utilities.toModule("org.yourorghere.engine");
+        
+        assertNotNull(transitiveMod);
+        assertNotNull(hostMod);
+    }
+    
+}
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentMissingInCatalogRestartTest.java b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentMissingInCatalogRestartTest.java
new file mode 100644
index 0000000..c685acf
--- /dev/null
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentMissingInCatalogRestartTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.InputStream;
+import java.util.concurrent.atomic.AtomicReference;
+import org.netbeans.api.autoupdate.OperationSupport;
+import org.netbeans.api.autoupdate.TestUtils;
+import org.netbeans.api.autoupdate.UpdateUnit;
+
+/**
+ * Checks that a fragment on top of enabled module causes restart although
+ * the catalog XML does not list its Fragment-Host: attribute. 
+ * 
+ * @author sdedic
+ */
+public class FragmentMissingInCatalogRestartTest extends FragmentModuleTestBase {
+    
+    public FragmentMissingInCatalogRestartTest(String testName) {
+        super(testName);
+    }
+
+    protected InputStream updateCatalogContents() {
+        return TestUtils.class.getResourceAsStream("data/updates-bad-fragment.xml");
+    }
+    
+    @Override
+    public void testSelf() throws Exception {
+        installModule(UpdateManagerImpl.getInstance().getUpdateUnit("org.yourorghere.engine"), null);
+
+        UpdateUnit toInstall = UpdateManagerImpl.getInstance().getUpdateUnit(moduleCodeNameBaseForTest());
+        
+        AtomicReference<OperationSupport.Restarter> restarter = new AtomicReference<>();
+        installModuleWithRestart(toInstall, null, restarter);
+        assertNotNull(restarter.get());
+    }
+    
+}
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentModuleTestBase.java b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentModuleTestBase.java
new file mode 100644
index 0000000..e82c529
--- /dev/null
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/FragmentModuleTestBase.java
@@ -0,0 +1,93 @@
+/*
+ * 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.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.fail;
+import org.netbeans.api.autoupdate.InstallSupport;
+import org.netbeans.api.autoupdate.OperationContainer;
+import org.netbeans.api.autoupdate.OperationException;
+import org.netbeans.api.autoupdate.OperationSupport;
+import org.netbeans.api.autoupdate.UpdateElement;
+import org.netbeans.api.autoupdate.UpdateUnit;
+import org.netbeans.modules.autoupdate.updateprovider.InstalledModuleProvider;
+import org.openide.modules.ModuleInfo;
+
+/**
+ *
+ * @author sdedic
+ */
+public abstract class FragmentModuleTestBase extends OperationsTestImpl {
+
+    public FragmentModuleTestBase(String testName) {
+        super(testName);
+    }
+
+    protected String moduleCodeNameBaseForTest() {
+        return "org.yourorghere.fragment";//NOI18N
+    } 
+    
+    protected Map<String, ModuleInfo> getModuleInfos () {
+        return InstalledModuleProvider.getInstalledModules ();
+    }
+    
+    protected UpdateElement installModuleWithRestart(UpdateUnit toInstall, UpdateElement installElement, AtomicReference<OperationSupport.Restarter> restarterOut) throws Exception {
+        installElement = (installElement != null) ? installElement : toInstall.getAvailableUpdates ().get (0);
+        assertNull (getModuleInfos ().get (toInstall.getCodeName ()));
+        assertNotNull (toInstall);
+
+        OperationSupport.Restarter r = null;
+
+        OperationContainer<InstallSupport> container = OperationContainer.createForInstall ();
+        OperationContainer.OperationInfo<InstallSupport> info = container.add (installElement);
+        assertNotNull (info);
+        container.add (info.getRequiredElements ());
+
+        InstallSupport support = container.getSupport ();
+        assertNotNull (support);
+
+        InstallSupport.Validator v = support.doDownload (null, false);
+        assertNotNull (v);
+        InstallSupport.Installer i = support.doValidate (v, null);
+        assertNotNull (i);
+        assertNull (support.getCertificate (i, installElement)); // Test NBM is not signed nor certificate
+        assertFalse (support.isTrusted (i, installElement));
+        assertFalse (support.isSigned (i, installElement));
+        try {
+            r = support.doInstall (i, null);
+        } catch (OperationException ex) {
+            if (OperationException.ERROR_TYPE.INSTALL == ex.getErrorType ()) {
+                // can ingore
+                // module system cannot load the module either
+            } else {
+                fail (ex.toString ());
+            }
+        }
+        if (restarterOut != null) {
+            restarterOut.set(r);
+        }
+        return installElement;
+    }
+    
+    
+}