-
-
Notifications
You must be signed in to change notification settings - Fork 2k
[proposal] Split ObjectFactory in Container and Lookup #1117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I'm not too familiar with the design of CDI frameworks, specifically Arquillian, and from the implementations in Can you describe which methods each interface would have and how the Lookup would interact with the Container? Can you describe why this is needed with Arquillian? I.e. is there a reason you can't wrap the Arquillian parts in an ObjectFactory? |
Yes, arquillian is a runner you cant easily change to make a lifecycle manager. Also cdi, spring etc.. separate the lookup from the container lifecycle in term of api. Also you can combine container lookups but start a single one (spring in ee). For these 3 reasons splitting both concerns would be good for cucumber and help cukespace |
I understand your desire and I understand that other frameworks also make the same separation but I don't understand the technical reasoning behind that separation. Nor do I understand how such a separation would be implemented. Perhaps I am asking to many questions at the same time. Lets start with the most important one. Can you describe which methods each interface would have and how the Lookup would interact with the Container? |
Sure, just before sharing some API example keep in mind cucumber-jvm assumes it manages the container which is not always the case and can require to implement a full object factory where only a Lookup or Container would work. In CukeSpace case, original idea is to integrate cucumber-jvm with arquillian. Main advantage of arquillian is a deep integration with containers (startup/deployment/undeployment/shutdown) so in this case Container part of the API is pointless and in the integration pollutes the understanding. In term of split i would just define lookup as:
And the Container as ObjectFactory minus Lookup. |
Okay. This would let you use the Cucumber requires that each Scenario is executed with a clean dependency injection context. So some form of container management is needed. Do you intended to create your own back-end that uses |
This is more or less what is done being said most of the time container = context/lifecycle management. World only makes sense for cucumber managed instance which is less true/a constraint in a container. |
What keeps you from reusing the existing JavaBackend? From a design perspective I'd rather see that you reuse that then some specific parts. The JavaBackend and parts aren't really designed for reuse (for example look at the way java 8 step defintions are loaded). |
This is what is done actually. All the scanning is replaced cause in the container you don't know if you can scan but the blocking part is really how to get step instances in a container friendly way portably. Users asked to not just rely on injections to handle the context but also to make steps beans themself. Naturally we made configurable the object factory which owns it but then we have all the container API part which is ignores and can even prevent to reuse existing object factories. That is why from a design perspective, splitting the objectfactory to made obvious its 2 concerns would be beneficial. |
Sorry. I should have stated that more clearly. Currently cuckespace is extending the JavaBackend and overriding several methods. The JavaBackend is not designed for this. So I'd rather see that cuckespace didn't extend it. I'm thinking you'd be better served by abstracting the class path scanning and let you instantiate the JavaBackend with that instead. |
Think that's what is done if I get you right, or at least it was the intent. What was not an option was to scan in the container (ie from the backend) since we can not have the right filesystem handling (jndi://, vfs://, ...) IIRC the inheritance is there just for the snippets and not for any other handling. |
I think that if you were to implement your own backend rather then extend the |
It is a bit more complicated because cukespace must also ensure the test instance is registered properly so handle the "lookup" part but yes, being able to provide a manually built registry can help. Not sure INSTANCE is the way to go (AFAIK it is mainly a workaround for Java 8 which also has the issue to use the constructor -> see Lambda in cukespace to workaround it) . Rephrased the needs can be split as such:
|
Okay. We can at least extract the StepDefinitionRegistry interface then. That said I still don't understand the need to separate the lookup from the container or even how you'd implement this. They appear to be intrinsically linked - for each call made getInstance, at the very least the stop method would need to do some cleanup and as such reference a common data structure somewhere. Please make this concrete. How would your and our code end up looking?
Given the current syntax there is no way around registering lambda step definitions to a static instance. The current syntax requires the use of either static methods or default methods. The former can only access a static context, the latter can access a static context and its own methods.
Can you provide a reproducer? |
@mpkorstanje are you familiar with Arquillian? It is basically the same kind of split: the container will just handle the lifecycle of the beans and the lookup the way they are retrieved. It is indeed linked but splitting both allows to reuse them but also enrich them. Typically I can reuse weld-container (or spring-container) and enrich the weld-lookup (spring-lookup) with some custom lookups instead of having to delegate the container lifecycle too. In the case of cukespace it is worse: the container is managed by arquillian so only the lookup part is important for cucumber integration. The INSTANCE issue is more about the fact it is not really an internal at the moment but it should, so I'm not very tempted to rely on it a lot - I didn't say it was wrong since I agree there is no real alternative to hide it to end users, just that using it in cukespace is not that great. However the fact to use constructor instead of a define() lifecycle hook is quite bad because it prevent some usage in during the step definition. For instance compare these two step def style (old vs j8 one):
Simple solution is to just do:
|
I was looking for an example of this, but the explanation is now sufficiently clear. Unfortunately this not a concept used by cucumber and provides no benefit to our code base so I am hesitant to introduce it. By appearing to be without use it might be re-factored out again in the future and break things you've come to rely upon. As such I'd recommend wrapping the ObjectFactory provided through the constructor with a decorator that implements Container and Lookup and proceed with that decorator as if it was the ObjectFactory.
I see the problem. Though using precomputation in step definitions is a bad practice. If done at all, this should be done in the before hook. |
This is what has been done but half of the API is ignored and therefore it is weird for end users - that's why i opened this issue.
Yes and no, I get it for the "old" way but for the java 8 way it is easier and more natural than using only hooks to set instance variables which is not natural. Also note that some impl will just bypass the constructor (completely, using Unsafe) and being lazy you will never register any step. Does the define() solution hurt that much? |
I would rather not add anything to cucumber that is not also used by
cucumber. I think that the burden of dealing and maintaining special cases
should lie with the those who use and benefit from them. As such the
Arquillian backend gets to solve the problems of dealing with Arquillian.
Can you provide an example or documentation reference of a DI container
used by cucumber that routinely bypasses the constructor?
On Oct 3, 2017 10:13, "Romain Manni-Bucau" <[email protected]> wrote:
As such I'd recommend wrapping the ObjectFactory provided through the
constructor with a decorator that implements Container and Lookup and
proceed with that decorator as if it was the ObjectFactory.
This is what has been done but half of the API is ignored and therefore it
is weird for end users - that's why i opened this issue.
Though using precomputation in step definitions is a bad practice.
Yes and no, I get it for the "old" way but for the java 8 way it is easier
and more natural than using only hooks to set instance variables which is
not natural. Also note that some impl will just bypass the constructor
(completely, using Unsafe) and being lazy you will never register any step.
Does the define() solution hurts that much?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1117 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGoAZ5lvaSOcuCpQCmUYqnGnHsAC-cwTks5soezFgaJpZM4NEYhn>
.
|
If you use a step definition which is an EJB with openejb (which is perfectly fine for cucumber and would allow to execute steps in transactions) then the constructor will not be called. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed in a week if no further activity occurs. |
Please dont close |
Okay. It took me a long time to understand this. But now that I am looking into sharing the object factory between different back ends it starts to make sense. |
By sharing the object factory between different backends it becomes possible to use the same test context in different languages. This is very useful when mixing Kotlin and Java, or Java and Java 8. This also requires that the backend no longer manages the object factory life-cycle. To end a container and lookup have been extracted from the object factory. Closes #1117.
Implemented by aa76421. It will be available in v5. |
By sharing the object factory between different backends it becomes possible to use the same test context in different languages. This is very useful when mixing Kotlin and Java, or Java and Java 8. This also requires that the backend no longer manages the object factory life-cycle. To end a container and lookup have been extracted from the object factory. Closes #1117.
Hi @mpkorstanje Is the inheritance right? Container should have start/addClass/stop and Lookup just getInstance ObjectFactory would extend both but lookup and container have no inheritance between them, they are used by composition in cucumber. Also objectfactory shouldnt be used in the code anymore, it would just be an api for lib integration - api sugar, cucumber itself would just see a container and a lookup with the shortcut that if both are using the same class then it leads to the same instance. Wdyt? |
Ah right. No I didn't set it up like that. In the long run I'm looking at reusing a lot of concepts from JUnit5 and introduce an At this point the |
Well with junit 5 - and thanks platform abstraction - you should be able to have N>1 containers with M>1 corresponding lookups and be able to compose them at need instead of rewriting a lookup each time. So think it would make sense to enable to configure the container on steps. Would enable more composition and reuse of steps for advanced envs. |
That will mostly be up to us. The junit platform doesn't put any restrictions on the shape of the exectuion context. |
Agree but then the commit does not solve this issue which was about breaking that inheritance and the api is not that smooth with junit5 |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Summary
ObjectFactory has 2 concerns:
Context & Motivation
In CukeSpace the container is handled by Arquillian "by design" so reusing ObjectFactory is weird and it makes hard to reuse cucumber-jvm goodness.
Your Environment
CukeSpace
Proposal
Make ObjectFactory being:
This will keep current code working and allow to split the way the container is handled from the way instances are retrieved. This is quite common for CDI, Spring, ... so think it can benefit users.
The text was updated successfully, but these errors were encountered: