Week 3: Eureka! Context Conquered


馃搮 June 14 - June 20
What I Planned to Do
Invoke
getRepresentationDescription()
using no-args constructor and return proper valuesClean up the class loader for any redundant classes or any old / primitive methods used
Try to find a way to bypass the context needs for the methods we are trying to invoke
What I Worked On
This was my first week after a brief hiatus owing to my college exams, I took a rest day and got to familiarizing myself again with the project specifics
I had a recap meet with my mentors Chi Bong Ho and Herman Muhereza, where we discussed where I was stuck, what I was supposed to do and what new approaches I can try now that I have had time away
I got working on the context issue I was struggling with, and after suggestions from the meet I read through the
RestConstants
file and why is it making an issue because it is a pretty straightforward class, but when tried to be invoked it threw various errors, first aboutServletException
due to a missing Spring Web dependencyAfter adding that it required a Spring API dependency and after that it threw a context error due to Administrative services not being available (
Service not found: interface org.openmrs.api.AdministrationService
)Taking my mentor鈥檚 feedback, after looking at RestConstant, eureka! There was a static variable
URI_PREFIX
which initialized a static block where it called a method fromRestUtil
as follows:public static String URI_PREFIX; static { RestUtil.setUriPrefix(); }
This further accessed Administrative services which led to all the context errors. This was a big breakthrough and I looked for any ways to bypass it and I found one where I can disable context through making
contextEnabled
variable as falseThis opened a huge door where I was then successful in invoking any resource class from the omod module which returned the required values!
This was the biggest objective in the project and this is what was supposed to take the longest amount of time and I was successful in starting to carve at it
The result looked like:
[INFO] --- openapi-generator:1.0.0-SNAPSHOT:openapi (default-cli) @ webservices.rest-omod-1.8 --- [INFO] === OPENAPI GENERATION STARTED === [INFO] Added project output directory: D:\Work\openmrs\openmrs-module-webservices.rest\omod-1.8\target\classes [INFO] ClassLoader setup complete: 22 URLs [INFO] === Disabling OpenMRS Context for Build-Time Use === [INFO] SUCCESS: OpenMRS Context disabled successfully [INFO] Loaded class: org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.CohortResource1_8 [INFO] SUCCESS! PatientResource1_8 instance created successfully! [INFO] Instance class: org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.CohortResource1_8 [INFO] Instance toString: org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.CohortResource1_8@2da16263 [INFO] Found getRepresentationDescription method: public org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.CohortResource1_8.getRepresentationDescription(org.openmrs.module.webservices.rest.web.representation.Representation) [INFO] === Testing getRepresentationDescription Method === [INFO] DEFAULT result: org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription@1084f78c [INFO] FULL result: org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription@25f723b0 [INFO] === Extracting Schema from DEFAULT === [INFO] Found 6 properties: [INFO] - uuid -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@40d60f2 [INFO] - display -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@3382cf68 [INFO] - name -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@2f74900b [INFO] - description -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@6d8796c1 [INFO] - voided -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@2e26173 [INFO] - memberIds -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@27be17c8 [INFO] === Extracting Schema from FULL === [INFO] Found 7 properties: [INFO] - uuid -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@2c413ffc [INFO] - display -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@7d21852b [INFO] - name -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@6cd98a05 [INFO] - description -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@3b8ec001 [INFO] - memberIds -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@1e95b653 [INFO] - voided -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@6f6c6077 [INFO] - auditInfo -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@4fc5563d [INFO] ============== [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 9.023 s [INFO] Finished at: 2025-06-20T20:38:16+05:30 [INFO] ------------------------------------------------------------------------
I documented the results, pushed the commit for my mentors to check and analyze the code for further modifications
After feedback, I corrected the versioning according to OpenMRS standards in the pom file that I had incorrectly assumed, corrected the indentation, used the
slf4j
logger library instead of the existing one, removed all unnecessary comments and cleaned up the processConceptResource1_8
is an exception in the sense that it doesn鈥檛 follow standard OpenMRS resource patterns where both default and full representations are handled bygetRepresentationDescription(
)Instead it is handled by
fullRepresentationDescription()
, which prompted me to make a generic method which scans for@RepHandler
annotations within the same resource so we don鈥檛 miss any resource-specific configuration likeConceptResource
doesAfter this, I changed my focus on the next step, to get the types for the properties that I have found
If you remember you would know that I had made a schema introspector that looks at a resource class, analyzes its generic supertypes and gets all the possible properties you can call through custom representation
I utilized its logic to work hand in hand with my maven plugin and I started working on integrating them so that I can get the types flawlessly
I started working on a crude OpenAPI schema so I can have some kind of rough example to go on to perfect, so I started working on a method which generates that schema in terminal for the time being
Also, I have made a PoC where it is able to get the types of the more established resource classes like
PersonResource
[INFO] ------------< org.openmrs.module:webservices.rest-omod-1.8 >------------ [INFO] Building Rest Web Services 1.8 OMOD 2.50.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- openapi-generator:1.0.0-SNAPSHOT:proper-openapi (default-cli) @ webservices.rest-omod-1.8 --- [INFO] === OpenMRS OpenAPI Generator - Proper Implementation === [INFO] Target: org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.PersonResource1_8 [INFO] ? Classpath setup complete [INFO] ? SUCCESS: OpenMRS Context disabled for build-time execution [INFO] === Extracting All Representations === [INFO] --- Processing DefaultRepresentation --- [INFO] ? Phase 1: DefaultRepresentation found via getRepresentationDescription() - 13 properties [INFO] --- Processing FullRepresentation --- [INFO] ? Phase 1: FullRepresentation found via getRepresentationDescription() - 16 properties [INFO] --- Processing RefRepresentation --- [INFO] ? Phase 1: RefRepresentation returned null, checking Phase 2... [WARNING] ? RefRepresentation not found in either phase [INFO] === Phase 3: Type Inference === [INFO] Total unique properties found: 16 [INFO] Delegate type: Person [INFO] Found 46 properties in delegate type [INFO] Found 8 annotated properties in resource [INFO] Type inference complete - 50/16 properties have known types [INFO] === Enhanced OpenAPI Schema Generation === [INFO] === Schema: PersonResource1_8Full === [INFO] components: [INFO] schemas: [INFO] PersonResource1_8Full: [INFO] type: object [INFO] properties: [INFO] addresses: [INFO] type: array items: $ref: '#/components/schemas/PersonAddressRef' [INFO] birthdate: [INFO] type: string format: date-time [INFO] gender: [INFO] type: string [INFO] display: [INFO] type: string [INFO] dead: [INFO] type: boolean [INFO] uuid: [INFO] type: string [INFO] preferredAddress: [INFO] $ref: '#/components/schemas/PersonAddressDefault' [INFO] auditInfo: [INFO] $ref: '#/components/schemas/SimpleObjectDefault' [INFO] birthdateEstimated: [INFO] type: boolean [INFO] names: [INFO] type: array items: $ref: '#/components/schemas/PersonNameRef' [INFO] deathDate: [INFO] type: string format: date-time [INFO] attributes: [INFO] type: array items: $ref: '#/components/schemas/PersonAttributeRef' [INFO] voided: [INFO] type: boolean [INFO] preferredName: [INFO] $ref: '#/components/schemas/PersonNameDefault' [INFO] causeOfDeath: [INFO] $ref: '#/components/schemas/ConceptDefault' [INFO] age: [INFO] type: integer [INFO] [INFO] === Schema: PersonResource1_8Default === [INFO] components: [INFO] schemas: [INFO] PersonResource1_8Default: [INFO] type: object [INFO] properties: [INFO] birthdate: [INFO] type: string format: date-time [INFO] gender: [INFO] type: string [INFO] display: [INFO] type: string [INFO] dead: [INFO] type: boolean [INFO] uuid: [INFO] type: string [INFO] preferredAddress: [INFO] $ref: '#/components/schemas/PersonAddressDefault' [INFO] birthdateEstimated: [INFO] type: boolean [INFO] deathDate: [INFO] type: string format: date-time [INFO] attributes: [INFO] type: array items: $ref: '#/components/schemas/PersonAttributeRef' [INFO] voided: [INFO] type: boolean [INFO] preferredName: [INFO] $ref: '#/components/schemas/PersonNameDefault' [INFO] causeOfDeath: [INFO] $ref: '#/components/schemas/ConceptDefault' [INFO] age: [INFO] type: integer [INFO] [INFO] === FINAL RESULTS === [INFO] ? DefaultRepresentation: 13 properties [INFO] - birthdate : Date [INFO] - gender : String [INFO] - display : String [INFO] - dead : Boolean [INFO] - uuid : String [INFO] - preferredAddress : PersonAddress [INFO] - birthdateEstimated : Boolean [INFO] - deathDate : Date [INFO] - attributes : List<PersonAttribute> [INFO] - voided : Boolean [INFO] - preferredName : PersonName [INFO] - causeOfDeath : Concept [INFO] - age : Integer [INFO] ? FullRepresentation: 16 properties [INFO] - addresses : Set<PersonAddress> [INFO] - birthdate : Date [INFO] - gender : String [INFO] - display : String [INFO] - dead : Boolean [INFO] - uuid : String [INFO] - preferredAddress : PersonAddress [INFO] - auditInfo : SimpleObject [INFO] - birthdateEstimated : Boolean [INFO] - names : Set<PersonName> [INFO] - deathDate : Date [INFO] - attributes : List<PersonAttribute> [INFO] - voided : Boolean [INFO] - preferredName : PersonName [INFO] - causeOfDeath : Concept [INFO] - age : Integer [WARNING] ? RefRepresentation: MISSING [INFO] === SUMMARY === [INFO] Standard Representations: Default=?, Full=?, Ref=? [INFO] Type Coverage: 16/16 properties (%.1f%%) [WARNING] ? Some standard representations are missing [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.036 s [INFO] Finished at: 2025-06-20T20:57:26+05:30 [INFO] ------------------------------------------------------------------------
What you see in the result is me successfully retrieving the default and full representations of
PersonResource
, me generating a basic schema of their properties and listing the concerned properties with their types, I consider this PoC a huge success which I aim to build onDiving in the schema format more, I mapped schema types from int to Integer, and all lists are type: array with their types being smartly shown in $ref
Challenges Faced
I was facing a lot of issues trying to disable the context, but consistent hammering and right nudges by my mentors helped me immensely
Integrating the schema introspector with my maven plugin in build time was a mammoth task, and cleverly getting the types right every single time took a lot of head scratching time
Further, getting the openAPI schema to print properly and have a standard to go on was difficult, I had to read up on the current standards, familiarize myself with unknown concepts and get it working
What I Learned
Learnt immensely about static classes and initialization in detail, how context can be so tricky to tackle from a build-time standpoint
Learnt how reflection can benefit me in aiding getting the types of the properties I wish to print in the terminal
Learnt in depth about OpenAPI schema, current standards, and methods to get it working
Commits/PRs This Week
PR Title: Initial implementation of OpenAPI Generator Maven Plugin with JavaParser integration
Status:
Draft
Plans for Next Week
Handle the edge cases, particularly the
ConceptResource1_8
where there exist multiple representations like fullchildrenPerfect the schema format better and consult and understand how to export it in a YAML or JSON format
Notes/Discussion Points
Don鈥檛 want to get my mind stuck in one same approach, ask for suggestions from the community and include more tests in my PRs
Getting consistent results with the amount of time I put, and generate robust results
Thank you!
Subscribe to my newsletter
Read articles from Marvin directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Marvin
Marvin
21 | cs sophomore @ SPIT Mumbai | GSoC 2025 Contributor @ OpenMRS | Backend Dev | Java, Python, Spring Boot, Maven