Can a module service a "core" URL?

Hey @wyclif @dkayiwa @darius @raff and Dev/5 developers

CC: @burke

I have a created a new module for OpenMRS Legacy UI.

  1. I have moved a part of the Legacy UI to the new module and removed it from the core
  2. The builds work fine
  3. When I run the module, I will obviously be redirected to core URL’s.

Suppose, http://localhost:8080/openmrs/admin/patients/index.htm But, now as the patients index lies in my module the link should be redirected to modules URL http://localhost:8080/openmrs/module/legacyui/admin/patients/index.htm to get it running.

But, as there will be many people depending on the cores’ URL’s, we want to display the core URL instead of the modules URL :slight_smile:

I want to find a way out here. Any suggestions on how to proceed?

1 Like

There is definitely going to be some painful things that have to be done as a result of this move, e.g we have to change all links to point to the ones in your module, all redirects in controllers, URL mapping entries in openmrs-servlet.xml file and this is going to be very tedious, not sure how to simplify it. Same applies to all URLs in modules that are pointing to pages in core

@wyclif, Thank you :smiley:

But, I think it is an important specification of the module as there will be millions of people depending on them. I will get working once we are sure. I can do it. :blush:

But if possible, a quick start on how this could be done technically will be great to begin with. Any suggestions will definitely help me do right :smile:

I think you can just put a “core” url in a module (either in an old-school xml file, or in a @RequestHandler annotation) and it should just work. You don’t have to do anything special.

This is an example of a @RequestParam mapping for a URL that isn’t in the namespace of the module:

1 Like

To avoid the pain, why not keep the old URLs for the legacy pages, and create new URLs for the new UI ? Seems easier.

1 Like

I would think the approach would be to not change any URL mapping entries or redirects, but rather to move the entire 'urlMapping' bean from openmrs-servlet.xml, along with all controller and view definitions, to the module along with the code.

Actually, I wonder what would happen if you basically just attempted to cut and paste everything from the openmrs-servlet.xml file into the moduleApplicationContext.xml (or brought over the entire file and included it). Since core and the modules are essentially building 1 Spring application context, couldn’t you almost ‘lift and shift’ bean definitions at will? Then you’d have to satisfy some dependencies back in core to get it to run on its own, and pull all the code out, but it could be a way to start. Even the old admin page might be able to come over untouched?

I may be completely missing something here in the intent of the legacyui module or the technical limitations of the approach, but if the idea as I understand is simply to remove this unnecessary code from core, isn’t Spring (and Java classloading) doing most of the work to just let us move the proper dependencies from one jar to another without changing much at all?

1 Like

I’m assuming that things would get messier if we took the entire UI plumbing (complete application context) into the module, since web service modules that we want to remain active even with the legacy UI removed (REST Web Services & FHIR) depend on the plumbing (e.g., the main filter, open/closing the hibernate session, basic auth).

My suggestion to @tharunya was to start with the admin page and, with that working, tackle each of the admin sections in the legacy UI. Ideally, by the 2nd or 3rd, migrating more of them should be relatively straightforward. Then we could go after the patient dashboard. In the end, we may end up with nearly everything migrated to the legacyui module, but I’m guessing there will be some left behind (e.g., the main filter and code for managing the legacy UI’s extension points). Moving the last bits may involve hacking the module service to allow the legacyui module code to be visible to all other modules without having to be declared as a dependency).

2 Likes

I think Burke is right that it’s best to start with a couple of pieces, but generally speaking, what Kristopher says is correct: this ought to mainly be a task of moving blocks of xml.

1 Like

Hey @lluismf

Agreed :smile:
However, the task is to keep the old URL, i.e the core URL but functionally redirect to modules URL as the component is removed from core and placed in the module in the respective location.

@kristopherschmidt @darius @burke

Thank you all for sparing time on this and giving your opinions, I will try it this way and update soon :smiley:

@tharunya based on your last comment to Lluis, I’m still not sure we’re all agreeing. You said ‘the task is to keep the old URL…but functionally redirect to modules URL’. The way I’m thinking of it, there won’t be a ‘module URL’. Core URLs like /admin/patients/index.htm will continue to work as-is; URLs like /legacyui/admin/patients/index.htm won’t exist.

To be extremely concrete, the legacyui module can define a new bean of type SimpleUrlHandlerMapping as in this example:

… and proceed to move a few mappings and dependencies from core to the module at a time, per Burke’s suggestion.

@kristopherschmidt

Hey :smile:

Yes, core URL’s should continue to work. But, core will no more contain any code in those URL’s, as we will rip out the UI from core. So, when the URL directs to core from the module we get an exception. Am I making sense??

Please correct me incase I am making a blunder :thumbsup: Happy to learn! :blush:

Thank you for the reference, will go through that xml right now :slight_smile:

I think the main problem you are referring to is when we start to move jsps from core into the legacyui module, that the view resolution will not work. URL mappings and controllers to say “/admin/patients/patient.form” can be defined in the legacyui module with no problem, but the names of views and how they resolve are different in modules.

Can any dev please explain to me how module jsps are resolved when the core jspViewResolver bean is looking for files under WEB-INF/view?

Let’s take a very concrete example of admin/patients/index.htm. Today in core, that page has a view name of admin/patients/index and resolves a jsp under WEB-INF/view/admin/patients/index.jsp (openmrs-servlet.xml maps **/*.htm to springController which is a UrlFilenameViewController, which maps /admin/patients/index.htm to a viewname ‘admin/patients/index’, which the jspViewResolver looks for in WEB-INF/view/admin/patients/index.jsp)

On the other hand, if admin/patients/index.htm was moved into a module, apparently a viewname of module/legacyui/admin/patients/index is required to map to the appropriate resource. But we’re trying to keep the viewname of admin/patients/index. So…although the wiki page shows how additional url mappings and redirects, or controller redefinitions, can be used to override core URLs, that’s a ton of work as @wyclif pointed out.

What I would propose is to first understand how the module view resolution works today (whether by packaging or Spring ViewResolver), and change the view resolution for the legacyui module to accept the original viewnames. When that is done it should be possible to simply copy the admin/patients/index.jsp file over somewhere in legacyui (with any include dependencies?) and have it serve out of the box without configuring a redirect.

1 Like

Looks like different modules do view resolution in different ways – some define jsps under WEB-INF/view/module/, some copy them into WEB-INF/view during the build, some keep them under src/main/webapp and copy them under web/module (like legacyui - I’m actually not sure how those resolve).

However the point is that there are several ways to put jsps into the legacyui module that will be backwards-compatible with existing URL paths. For instance look at lines 219 and 220:

Legacyui could create src/main/webapp/admin/patients/index.jsp and copy src/main/webapp directory over to WEB-INF/view, (leaving off the undesired /module/legacyui), and then URL admin/patients/index.htm should just work.

Thanks Kris,

For clarity on the calculation module (and others that take a similar approach), the lines you point to below are actually in a profile called “deploy-web”, which is purely something used historically during development to speed up the develop/build/test cycle. In an environment where a developer is running Tomcat locally, it copies the web resources from the source project into the deployed webapp which allows the developer to be able to test out their jsp/css/js changes in near real-time. But this is purely a development tool, and it is built simply knowing where a module’s resources will be deployed when the OpenMRS module framework loads the omod.

Cheers, Mike

1 Like

Thanks for the clarification Mike.

Turns out that WebModuleUtil.java (starting line 160) checks for content in a module under web/module and copies it into WEB-INF/view/module/ (lines 173, 178). That explains how the jspViewResolver can find the module jsps under WEB-INF/view, and why the “module/” is traditionally a part of the path.

So the easiest way to move the jsps from core into legacyui would be to slightly alter WebModuleUtil such that it would handle the content in legacyui’s web/module directory a little bit differently, putting it directly under WEB-INF/view. Then all of the jsps could be copied into legacyui’s src/main/webapp directory and all of the files would continue to serve under the original URLs + viewnames.

It is a little bit of an invasive code change but it’s a couple of line fix that will save a TON of work both in core and in modules that reference core UI paths.

2 Likes

By the way, I just wanted to mention that besides WEB-INF/view, there will eventually be the need to bring over the taglibs, tag and template directories too.

The fact that a lot of these files are directly referenced in the jsps using include and taglib directives is one reason why I think patching WebModuleUtil is the most reliable way to go. I considered an alternative of legacyui wrapping the jspViewResolver so that the jsps could be found under /module/legacyui, which would work for top-level jsps. However this approach wouldn’t work with other content that isn’t being referenced through Spring.

The best way to make this work backwards-compatibly is to ensure that the packaging of all files under WEB-INF remains 100% the same whether the files are coming out of core or out of legacyui.

Thanks @kristopherschmidt for researching and pointing out some of the things that are going to be problematic, especially taglibs.

Taking a step back, though, I think the purpose of this summer project is to see if we can pull all reference to the legacy UI out of openmrs-core entirely, so that it ends up “clean” so it would be much better if we can avoid special-casing the legacyui module within core code.

Back to the details…Tharunya, when you say this…

when the URL directs to core from the module we get an exception

I think there may be a specific piece you’re missing: in reality the idea of a “core” url vs a “module” url is just by convention, but the way that Spring handles url mappings, the core ones are not actually special.

Burke probably used “redirect” in his descriptions of the project, but no actual redirection needs to happen. The task is actually moving the functionality.

For example you should be able to take this mapping out of openmrs-servlet.xml, and move it verbatim to your module’s webModuleApplicationContext.xml:

<prop key="admin/patients/patient.form">patientForm</prop>

To go along with this, you also need to move the “patientForm” bean to your module:

<bean id="patientForm" class="org.openmrs.web.controller.patient.PatientFormController">
    <property name="commandName"><value>patient</value></property>
    <property name="formView"><value>/admin/patients/patientForm</value></property>
    <property name="successView"><value>patient.form</value></property>
</bean>

in this case you do need to change something, i.e. the formView property value needs to be changed to point to the new place in your module that the JSP has moved to.

You can do similar things with the (very few?) @Controller-driven pages.

Hopefully this helps. @tharunya, a lot has been said on this thread, so I’m going to hold off on saying anymore until you’ve had a chance to digest this, and ask something new…

2 Likes

@darius,

Sure, I will keep it in mind that there is no actual redirection that needs to happen but rather the task is to move the functionality. Thank you for the bean example. I have been trying with various other beans. Now, will try this directly instead. GreatHelp! :blush:

@everyone who replied here, I am really thankful for the detailed explanations you have given to help me move ahead :blush:

In fact, all of your replies and suggestions on this post helped me big time. I will keep in mind all the guidance, and come out with a working functionality soon :slight_smile: