Week 3: Eureka! Context Conquered

MarvinMarvin
8 min read

馃搮 June 14 - June 20

What I Planned to Do

  • Invoke getRepresentationDescription() using no-args constructor and return proper values

  • Clean 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 about ServletException due to a missing Spring Web dependency

  • After 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 from RestUtil 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 false

  • This 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 process

  • ConceptResource1_8 is an exception in the sense that it doesn鈥檛 follow standard OpenMRS resource patterns where both default and full representations are handled by getRepresentationDescription()

  • 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 like ConceptResource does

  • After 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 on

  • Diving 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


Plans for Next Week

  • Handle the edge cases, particularly the ConceptResource1_8 where there exist multiple representations like fullchildren

  • Perfect 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!

0
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