[
https://issues.apache.org/jira/browse/FELIX-4847?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14487040#comment-14487040
]
Tuomas Kiviaho commented on FELIX-4847:
---------------------------------------
In my case the temporal service remained unavailable (used dm shell to verify
this) until the actual service arrived. This is the behavior expected from
{{required}} temporal service dependency and I see now that tampering with what
{{isAvailable()}} returns - as per 1) proposal - would break the backwards
compatibility. I did not understand that the intention was to actually wait
until there is a service before dependency becomes available.
Therefore my only working proposal is 2) that is allowing the temporal service
to be optional as well. What I'd like to happen in this mode is for the
dependency to be available immediately and for the proxy to block/wait for the
services first appearance if user happens to access it prematurely. This is
indeed what would happen if optionality would not have been explicitly
forbidden in {{setRequired}} method. DefaultNullObject used by non-temporal
service dependency instead would return null immediately causing NPE in my
usecase.
This is a snippet from my PDE JUnit test case (magic happens inside a rule
below that understands DM 3.x annotations) that relies on optional temporal
service. Otherwise I'd have to manually track/wait Foobar service after
asynchronous CM update (with a custom default impl)
{code}
public class FoobarIT
{
@Rule
public TestRule testRule = new DependencyManagerTestRule() { protected
Object getWrapped() { return FoobarIT.this; }};
@Inject
private BundleContext bundleContext;
@ServiceDependency(name = Foobar.PID, timeout = 60000)
private Foobar foobar;
@ServiceDependency
private ConfigurationAdmin configurationAdmin;
@ResourceDependency(filter = "(" + ResourceHandler.PATH + "=/META-INF/"
+ Foobar.PID + ".cfg)")
private URL url;
@Init
Map<String, ?> init() throws IOException
{
Bundle bundle = this.bundleContext.getBundle();
String location = bundle.getLocation();
Configuration configuration =
this.configurationAdmin.getConfiguration(Foobar.PID, location);
Properties properties = new Properties();
try (InputStream inputStream = this.url.openStream();)
{
properties.load(inputStream);
}
configuration.update((Dictionary) properties);
return Collections.singletonMap(Foobar.PID + ".required",
Boolean.FALSE);
}
@Test
public void test() {
this.foobar.blockUntilConfigured();
}
}
{code}
The verification that JUnit test case is ready for evaluation
(required->available) is done inside evaluate method. I got it working by
patching {{TemporalServiceDependencyImpl}} just by removing the setRequired()
method that would have been executed->failed when {{init()}} method of the test
returned.
ComponentProvider is still proprietary piece of code that functions in place of
runtime bundle (reading annotations directly from the classes themselves), but
I guess it's clear what it does.
{code}
public abstract class DependencyManagerTestRule implements TestRule
{
private DependencyManager dependencyManager;
public DependencyManagerTestRule()
{
Object wrapped = this.getWrapped();
Bundle bundle = FrameworkUtil.getBundle(wrapped.getClass());
BundleContext bundleContext = bundle.getBundleContext();
this.dependencyManager = new DependencyManager(bundleContext);
}
protected abstract Object getWrapped();
@Override
public Statement apply(final Statement wrapped, Description description)
{
Class<? extends Object> testClass = description.getTestClass();
ComponentProvider componentProvider = new ComponentProvider(
this.dependencyManager, testClass);
final Component component = componentProvider.get();
Object implementation = this.getWrapped();
component.setImplementation(implementation);
Statement statement = new Statement()
{
@Override
public void evaluate() throws Throwable
{
DependencyManagerTestRule.this.dependencyManager.add(component);
@SuppressWarnings("unchecked")
List<Dependency> dependencies = component.getDependencies();
for (Dependency dependency : dependencies)
{
boolean required = dependency.isRequired();
boolean available = dependency.isAvailable();
if (!available && required)
{
ComponentDependencyDeclaration
componentDependencyDeclaration = (ComponentDependencyDeclaration) dependency;
String type = componentDependencyDeclaration.getType();
String name = componentDependencyDeclaration.getName();
throw new IllegalStateException(type + ": " + name);
}
}
try
{
wrapped.evaluate();
}
finally
{
DependencyManagerTestRule.this.dependencyManager.remove(component);
}
}
};
return statement;
}
}
{code}
> Make TemporalServiceDependency always available (and/or allow it to be
> optional)
> --------------------------------------------------------------------------------
>
> Key: FELIX-4847
> URL: https://issues.apache.org/jira/browse/FELIX-4847
> Project: Felix
> Issue Type: Wish
> Components: Dependency Manager
> Affects Versions: dependencymanager-3.2.0
> Reporter: Tuomas Kiviaho
>
> I wanted to use temporal service to wait for CM update thread to finish what
> it's doing (because the spec doesn't have a non-parallel version).
> Everything worked fine until JUnit test rule said that the component isn't
> ready yet. I was merely checking that every required dependency was also
> available and to my surprise the temporal service was marked unavailable
> until the CM had completed what it was doing.
> 1) Shouldn't temporal service be always available externally via available
> property and keep track on the actual state only internally? This approach
> might not be backwards compatible.
> 2) Could temporal service be allowed to be marked as optional. This would
> suit my use case, but it feels like a 'golden hammer' approach because it
> alters component's state machine behavior a bit which in turn can be harmful
> for other use cases.
> As a workaround I'd have to differentiate the dependencies somehow from each
> other, but I see that the 4.x has removed the dedicated interface that I was
> thinking of relying upon to.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)