-
Notifications
You must be signed in to change notification settings - Fork 4
#78 Added functionality for accessing the current ModelInstanceRepository implementation outside of the GenerationContext #80
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
Conversation
089a1d0
to
41591f1
Compare
* class to lookup | ||
* @return instance of that class | ||
*/ | ||
public static <V> V getMetamodelRepositoryWithExtensions(Class<V> type) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
S: This approach feels like it leaves a lot of room for ambiguity about what you're going to get. If you have multiple repositories that extend some common class, and then call this method with that common class, you arbitrarily get what comes first.
I propose we overload setRepository
with a version that takes the repository and the class to register it as. So you'd call something like setRepository(BaseRepo.class, new MyExtensionRepo())
. Then on lookup, it's completely unambiguous. BaseRepo
should always return MyExtensionRepo
and only MyExtensionRepo
. Of course it's still possible that the method be called multiple times with the same key, but that's just the nature of lookup tables/maps I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also has the benefit of not forcing all implementers to change their calls of getMetamodelInstanceRepository
with getMetamodelInstanceRepositoryWithExtension
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setRepository()
is called as part of fermenter-mda/src/main/java/org/technologybrewery/fermenter/mda/GenerateSourcesHelper.java
so it has no way of knowing the base class and extension class as it only has access to metadataRepositoryImpl
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could interrogate the superclass from the current instance but I think that might break as more layers are added. Need to look into it more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given MIR_A implements AbstractModelInstanceRepository
, MIR_B extends MIR_A
, and MIR_C extends MIR_B
.
When we're executing with the MIR_C
repository as our current implementation, we would write:
setRepository(MIR_B.class, MIR_C)
This would still cause breakage in the MIR_A
repository on the calls:
getMetamodelRepository(MIR_A.class)
Unless we want to continue interrogating the given impl class until we hit AbstractModelInstanceRepository
. Then we would need multiple writes to the set repository for each layer:
setRepository(MIR_C.class, MIR_C)
setRepository(MIR_B.class, MIR_C)
setRepository(MIR_A.class, MIR_C)
This would allow all calls to get the same MIR_C
instance:
getMetamodelRepository(MIR_A.class) -> MIR_C
getMetamodelRepository(MIR_B.class) -> MIR_C
getMetamodelRepository(MIR_C.class) -> MIR_C
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think from a usage perspective you're telling Fermenter what impl class should be used for a given repository type. I don't think it's a huge deal that you'd have to set it explicitly at multiple levels, because I don't think that would be the correct solution in 99% of cases.
If I register MIR_B
as the impl for MIR_A
, I should still write my consuming code at the MIR_A
level, even in the source that's "aware" of the B impl. And so if B were then extended with C, again it's just replacing the impl of A and all code should be written against A.
We could enforce something around it being an interface if that would feel more natural. That would call out that there is a single interface being programmed against and the impl is decided/provided at runtime. My only concern with requiring an interface would be old code. We'd either need old code to be migrated to be interface-driven, or we'd need to allow both interface-driven and concrete class which seems like it could lead to a lot of edge cases/ambiguity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if I follow correctly, you're saying that even in the MIR_B
and MIR_C
repos, they should still really only be using:
getMetamodelRepository(MIR_A.class)
Given we want to preserve the existing setRepository()
rather than make it an interface and introduce a breaking change, we would still need to interrogate the impl until we find the base class. If we're doing that interrogation I don't see any harm in adding all the intermediate classes along the way:
public static void setRepository(ModelInstanceRepository repository) {
Map<String, Object> instanceMap = threadBoundInstance.get();
instanceMap.put(repository.getClass().toString(), repository);
Class<?> repositorySuperClass = repository.getClass().getSuperclass();
while (!repositorySuperClass.getName().equals("org.technologybrewery.fermenter.mda.metamodel.AbstractModelInstanceRepository")) {
instanceMap.put(repositorySuperClass.toString(), repository);
repositorySuperClass = repositorySuperClass.getSuperclass();
}
}
6b38b0e
to
1e1ca5c
Compare
|
||
/** | ||
* Adds a repository. Only one repository of each type will be kept. | ||
* Adds a repository and creates references to it for the current impl class and any super |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A:
* Adds a repository and creates references to it for the current impl class and any super | |
* Sets a repository as the repository implementation for its own class and any super |
…tory implementation outside of the GenerationContext
1e1ca5c
to
504014b
Compare
Some metamodel classes (ex: RecordFieldTypeElement.java) need access to the current
ModelInstanceRepository
before theGenerationContext
has been created.Given
MIR_A implements ModelInstanceRepository
andMIR_B extends MIR_A
, the old way fails when the implementation class for an execution isMIR_B
but the upstream fermenter project still contains references toMIR_A
. The new way allows for those references to remain intact while allowing others to extend their project.Old way would return a NPE as
MIR_A.class
doesn't exist in the map, onlyMIR_B.class
: