package com.atlassian.labs.speakeasy.descriptor;
import com.atlassian.labs.speakeasy.data.SpeakeasyData;
import com.atlassian.labs.speakeasy.descriptor.external.ConditionGenerator;
import com.atlassian.labs.speakeasy.descriptor.external.DescriptorGenerator;
import com.atlassian.labs.speakeasy.descriptor.external.DescriptorGeneratorManager;
import com.atlassian.labs.speakeasy.descriptor.external.GroupsConditionGenerator;
import com.atlassian.labs.speakeasy.descriptor.external.UsersConditionGenerator;
import com.atlassian.labs.speakeasy.manager.SettingsManager;
import com.atlassian.plugin.ModuleDescriptor;
import com.atlassian.plugin.PluginAccessor;
import com.atlassian.plugin.PluginController;
import com.atlassian.plugin.util.WaitUntil;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.atlassian.labs.speakeasy.util.BundleUtil.findBundleForPlugin;
import static com.google.common.collect.Lists.newArrayList;
/**
*
*/
public class DescriptorGeneratorManagerImpl implements DescriptorGeneratorManager
{
private final SpeakeasyData data;
private final SettingsManager settingsManager;
private final PluginAccessor pluginAccessor;
private final BundleContext bundleContext;
private final Map<String, Registration> registrations;
private final PluginController pluginController;
public DescriptorGeneratorManagerImpl(SpeakeasyData data, PluginAccessor pluginAccessor, BundleContext bundleContext, PluginController pluginController, SettingsManager settingsManager)
{
this.data = data;
this.pluginAccessor = pluginAccessor;
this.bundleContext = bundleContext;
this.pluginController = pluginController;
this.settingsManager = settingsManager;
this.registrations = new ConcurrentHashMap<String, Registration>();
}
public void registerGenerator(String pluginKey, String descriptorKey, DescriptorGenerator<? extends ModuleDescriptor> descriptorGenerator)
{
// unregister any existing services
List<ModuleDescriptor> unregisteredDescriptors = unregisterGenerator(pluginKey, descriptorKey);
waitUntilModulesAreDisabled(unregisteredDescriptors);
// generate and register new services
Bundle targetBundle = findBundleForPlugin(bundleContext, pluginKey);
List<ModuleDescriptor> generatedDescriptors = new ArrayList<ModuleDescriptor>();
List<ServiceRegistration> serviceRegistrations = newArrayList();
ConditionGenerator conditionGenerator = data.isGlobalExtension(pluginKey) ? new GroupsConditionGenerator(settingsManager.getSettings().getAccessGroups()) :
new UsersConditionGenerator(data.getUsersList(pluginKey));
for (ModuleDescriptor generatedDescriptor : descriptorGenerator.getDescriptorsToExposeForUsers(conditionGenerator, targetBundle.getLastModified()))
{
ServiceRegistration reg = targetBundle.getBundleContext().registerService(ModuleDescriptor.class.getName(), generatedDescriptor, null);
serviceRegistrations.add(reg);
generatedDescriptors.add(generatedDescriptor);
}
registrations.put(getKey(pluginKey, descriptorKey), new Registration(
pluginKey,
descriptorKey,
descriptorGenerator,
serviceRegistrations
));
waitUntilModulesAreEnabled(generatedDescriptors);
}
public List<ModuleDescriptor> unregisterGenerator(String pluginKey, String descriptorKey)
{
String keyToRemove = getKey(pluginKey, descriptorKey);
List<ModuleDescriptor> removedDescriptors = new ArrayList<ModuleDescriptor>();
Registration registration = registrations.remove(keyToRemove);
if (registration != null)
{
for (ServiceRegistration reg : registration.getServiceRegistrations())
{
try
{
removedDescriptors.add((ModuleDescriptor) bundleContext.getService(reg.getReference()));
reg.unregister();
}
catch (IllegalStateException ex)
{
// no worries, this only means the bundle was already shut down so the services aren't valid anymore
}
}
}
return removedDescriptors;
}
public void refreshGeneratedDescriptorsForPlugin(String pluginKey)
{
for (Registration reg : findRegistrationsForPlugin(pluginKey))
{
registerGenerator(reg.getPluginKey(), reg.getDescriptorKey(), reg.getDescriptorGenerator());
}
}
private Iterable<Registration> findRegistrationsForPlugin(String key)
{
List<Registration> result = newArrayList();
String keyPrefix = key + ":";
for (String completeKey : newArrayList(registrations.keySet()))
{
if (completeKey.startsWith(keyPrefix))
{
result.add(registrations.get(completeKey));
}
}
return result;
}
public static String getStatefulKey(String descriptorKey, long state)
{
return descriptorKey + "-" + state;
}
private String getKey(String pluginKey, String descriptorKey)
{
return pluginKey + ":" + descriptorKey;
}
private void waitUntilModulesAreDisabled(final List<ModuleDescriptor> descriptors)
{
WaitUntil.invoke(new WaitUntil.WaitCondition()
{
public boolean isFinished()
{
for (ModuleDescriptor descriptor : descriptors)
{
ModuleDescriptor<?> module = pluginAccessor.getEnabledPluginModule(descriptor.getCompleteKey());
if (module != null)
{
return false;
}
}
return true;
}
public String getWaitMessage()
{
return "Waiting until the enabled plugins are available";
}
});
}
private void waitUntilModulesAreEnabled(final List<ModuleDescriptor> generatedDescriptors)
{
WaitUntil.invoke(new WaitUntil.WaitCondition()
{
public boolean isFinished()
{
for (ModuleDescriptor descriptor : generatedDescriptors)
{
ModuleDescriptor<?> module = pluginAccessor.getPluginModule(descriptor.getCompleteKey());
if (module == null)
{
return false;
}
else
{
if (!pluginAccessor.isPluginModuleEnabled(descriptor.getCompleteKey()))
{
// This is necessary as Confluence will handle unregistered descriptors as an explicit
// disabling persisted in local state
pluginController.enablePluginModule(descriptor.getCompleteKey());
return true;
}
}
}
return true;
}
public String getWaitMessage()
{
return "Waiting until the enabled plugins are available";
}
});
}
private static class Registration
{
private final String pluginKey;
private final String descriptorKey;
private final DescriptorGenerator descriptorGenerator;
private final List<ServiceRegistration> serviceRegistrations;
public Registration(String pluginKey, String descriptorKey, DescriptorGenerator descriptorGenerator, List<ServiceRegistration> serviceRegistrations)
{
this.pluginKey = pluginKey;
this.descriptorKey = descriptorKey;
this.descriptorGenerator = descriptorGenerator;
this.serviceRegistrations = serviceRegistrations;
}
public String getPluginKey()
{
return pluginKey;
}
public String getDescriptorKey()
{
return descriptorKey;
}
public DescriptorGenerator getDescriptorGenerator()
{
return descriptorGenerator;
}
public List<ServiceRegistration> getServiceRegistrations()
{
return serviceRegistrations;
}
}
}