Spring application URL in scheduled job - java

Is it possible to get the application URL in spring scheduled job (#Scheduled annotated)?
I want to create a job which sends an email with URL to specific page on the application, let's say on http://localhost:8080/appName/some/specific/url. The problem is that the part http://localhost:8080/ will be different in each environment (local,dev,production etc).
Is there any simple way to get a base URL in server-side method which is executed by spring scheduler?

I do that with a properties file. This tutorial tells you how you can do it.
The only complex part is you need a way to change the value the properties file is referencing for each of your different environments.

There is no direct way to get the base url within your scheduler. You may want to look at work arounds for this, like
Use a properties file to store url for each environment
Have a Configurartion bean which implements ServletContextAware. This bean would be automatically notified when a web context is initialised.
public class AppConfig implements ServletContextAware{
private String baseUrl;
public String getBaseUrl(){
return baseUrl;
}
public void setServletContext(ServletContext servletContext){
this.baseUrl=servletContext.getRealPath("/");
}
}

Related

Get current domain name in an OSGi service component in AEM on activation

I am writing an OSGI service component in AEM.
I want to fetch current domain name in the activate method of the service component.
Currently, I'm writing a construct method, to get request from referring class/service/model/servlet to initialize the 'request' class object and using this request object to get the server name
private SlingHttpServletRequest request;
private String domainName;
#Override
public void construct(final SlingHttpServletRequest request) {
this.request = request;
}
#Override
public void setDomainName(){
this.domainName = request.getServerName();
}
And this.domainName is used in multiple service method implementations.
So, I have to call 2 extra service method,
Construct - to initialize global request object
setDomainName - to initialize domainName global object to be used across all the other service methods
Is there anyway to get domainName in activate method, so that i do not have to call the above two methods in order to use the service.
Note:- I cannot create an OSGI config for domain name, as this domain name is already being used as key property to identify the OSGI config of a given factory
Since AEM publish servers might be used for several domains, there is no way to "know" the right domain without getting the request. There might also be some magic being done by the web server and the CDN before the request is even reaching AEM.
On top of that, the activate method is not called each time the service is used, since those components are used multiple times.
So I think no, there is no way to guess what the domain of the next incoming request will be when the component is activated.
BR,
Oliver
To add to #OliverGeberts answer, this information can be added to the content (i.e. page properties of the language root) or some sort of tenant configuration.

Mocking REST API calls with SpringBoot profiles

I have recently started out with Spring and am unsure about how to approach this issue. I have a Spring boot program which makes calls to remote REST APIs. For example an AddressService class with getAddress(String user) method, which makes a HTTP call and returns a JSON response. I would like to set up Spring profiles for development purposes local, dev, uat, prod.
When the program is running with the local profile, I would like to "mock" these external API calls with an expected JSON response, so I can just test logic, but when it is run in any of the other profiles I would like to make the actual calls. How can I go about doing this? From what I read, there's many ways people approach this, using WireMock, RestTemplate, Mockito etc. I'm confused about which is the way to go.
Any advice would be greatly appreciated. Thanks.
WireMock,Mockit is for unittest, to mock the real request. Example here:
How do I mock a REST template exchange?
When you need a running implementation with a mock, i think the easiest way is that you have a interface
public interface AdressAdapter {
public List<Adress> getAddress(String name);
}
And two different implementations depending on the profile.
#Profile("local")
public class DummyAdress implements AdressAdapter{
#Override
public List<Adress> getAddress(String name) {
//Mock here something
return null;
}
}
! means NOT locale profile in this case.
#Profile("!local")
public class RealAdress implements AdressAdapter{
#Override
public List<Adress> getAddress(String name) {
//Make Restcall
return null;
}
}
What you could do is use different application.properties files depending on your profile. That way, you just change the url to a mock server for your local profile.
So what you have to do is :
Create another application.properties in your resources folder named : application-local.properties.
Change the url of the desired service.
Start your application with the VM option -Dspring.profiles.active=local.
Here is a link that describe well what you want to achieve.
For your mock server, you could use Wiremock, Mountebank, Postman,... that can be start separately and mock specific endpoints to return what you want.

How do I find base URL during Spring MVC startup?

fellow programmers who lurks here at Stack Overflow.
Today's question: How can I get the absolute baseUrl in Spring MVC Framework, from startup?
I'm working with Spring MVC Framework for an application, and I'm in this situation: Let's say that I need to make objects of class Foo, inserted into a list. Every object contains an unique self-link (I'm using org.springframework.hateoas.Link for the real application, but that's beside the point).
Example code:
class Foo{
private int ID;
private String absoluteUrl;
public Foo(int ID, String baseUrl){
this.ID = ID;
this.absoluteUrl = baseUrl + ID;
}
public void setAbsoluteUrl(String baseUrl){
this.absoluteUrl = baseUrl + this.ID;
}
}
If I run it through a factory, it could look something like this:
public List<Foo> GenerateFooList(String baseUrlFetchedBySpring){
List<Foo> list = new ArrayList();
for (int i = 0; i<100; i++){
list.add(new Foo(i, baseUrlFetchedBySpring);
return list;
}
Resulting baseadresses I would expect during the test phase would be "http://localhost:8000" (or hypothetically, at production, "http://www.fooExample.com").
My issue: I need to get the baseUrl from Spring MVC Framework, at startup.
The Spring MVC application I'm working with is configured by annotations only. I have found out that one can get an absolute url by using HttpServletRequest.getRequestURL().toString(), but to my understanding the application receives these after startup, while I need the baseUrl from the beginning. After all, the Spring API describes HttpServletRequest as: "Defines an object to provide client request information to a servlet", in other words a request sent from a client after startup.
I could of course add a static baseUrl by writing a private final String in the code:
private final String BASE_URL = "http://www.fooExample.com/"
But in case of changes on the application's base-url over time, it would be better if the base url could be set automaticly by Spring MVC. Let's say that I have a Cache-class, that uses dependency injection:
#Repository
class FooCache{
List<Foo> list;
SpringObject springObject; // = ???????????
#Autowired
public FooCache(SpringObject springObject){
this.springObject = springObject; // = ???????????
initCache();
}
public void initCache(){
for (int i = 0; i<100; i++){
list.add(new Foo(i, springObject.getAbsoluteBaseUrl()); // method = ???????????
}
}
This is more of what I am looking for: The cache is only set once, at the beginning, using an object from Spring that contains the information I need. Most likely, it's a config-class that is part of the framework, but after searching for a while on the Internet, what I mostly find is HttpServletRequest-related solutions.
What Spring class/object and method am I truly looking for? Or what other suggestions do you have to fetch the base_url from the beginning?
I need the absolute base url for this one, not something relative.
There is no single "base URL" for your application. Think about it - you can access your production server:
via different domain names
via IP
SSH to it and access via "localhost"
via HTTP and HTTPS
If you don't want to or cannot reflect the "base URL" of the request, my suggestion would be to have one "canonical base URL" configured per environment eg. in the properties. But that's up to you if it makes sense in your case.
If you're using java config and Servlet 3.0+ style (without web.xml), you could have a WebApplicationInitializer implementation such as:
public class MyWebApplicationInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) {
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet());
registration.setLoadOnStartup(1);
registration.addMapping("/example/*");
}
}
with this arrangement, you could use servletContext.getContextPath() to get base url.
You have no way of knowing that without a request. You could use InetAddress and resolve host information, but I am guessing that is not what you are looking for.
Let's say you have two domains www.foo.com and www.bar.com pointing to the host where your application is running. On startup you have no information about any of that, you would have to do a reverse DNS search.
When you get a request, you have the information where it is coming from.
You should fill the links on request so you can get rid of caching request scope information.

What is the correct way to get a template in FTL/spring when not in a web Servlet

We are currently using a sync. web call to send emails. This was done as a quick fix to prove out some basic functionality, but now we need to send the e-mails async. I have the everything pretty much reworked to queue up jobs and then send the emails, but I've run in to one issue. We use FTL for our email templates and before we were passing the servlet context to FTL to get the template folder. Since we are now doing this in a queued job that get's processed by a Spring #Scheduled job, we no longer have access to the web servlet. I've been researching and playing around for awhile now, but I haven't seem to come up with a way that will actually work.
I have a feeling there is some super simple way to get
The code that did the work before looked similar to this:
#PUT
#Produces(MediaType.APPLICATION_JSON)
#Path("someStuffHere")
#Transactional
public function someWebServiceCall(#Context javax.servlet.http.HttpServletRequest req)
{
SomeStuff foo = gotSomeStuff();
sendEmail(req.getServlet(), foo);
}
public sendEmail(Servlet context, SomeStuff foo) //<-- lives in another class somewhere, just showing how FTL uses the servlet
{
Configuration cfg = new Configuration();
cfg.setServletContextForTemplateLoading(context,"communicationTemplates/email");
}
The new code now looks something like this:
public class someClass
{
#Autowired
private SomeRepo someRepo;
#Scheduled(cron = "* */2 * * * ?")
public void sendAnyOutstandingStuffEmails()
{
SomeStuff foo = someRepo.getStuff();
sendEmail(/*how to get context, or a resource so FTL can get the template folder*/, foo)
}
Even though this post is quite old and the author has given another solution a try, it is not necessary to have a servlet context instance in order to load templates. The freemarker documentation states:
Built-in template loaders
You can set up three template loading methods in the Configuration
using the following convenience methods. (Each method will create a
template loader object internally and set up the Configuration
instance to use that.)
void setDirectoryForTemplateLoading(File dir);
or
void setClassForTemplateLoading(Class cl, String prefix);
or
void setServletContextForTemplateLoading(Object servletContext, String path);
http://freemarker.org/docs/pgui_config_templateloading.html
So in this case it should have been possible to configure freemarker to use the ClassLoader (option 2) by naming a class that is on the same level as the templates or to use this class as root node for navigating to the template using relative paths.

tomcat session replication - not serialized exception

I am currently working on one messy web application. In this application, there is a class that holds all the data sources. And whenever the need to connect to a specific data source, the method in the instance of the class is called with a parameter to select the data source.And class is like follows
public class MyConnection implements Runnable,DbConnection, Serializable
{
private static final long serialVersionUID=2007L;
public static transient DataSource FirstDatasource;
public static transient DataSource SecondDatasource;
BaseDbConnection _bidc;
....
And on each page, this object is to get and set to the session(I don't know why like this). And it works fine with the current setup. (clustering, load balancing etc..)
But my work is to implement fail-over, and when I enable session replication(in memory - simple TCP), writing the session is failing and its throwing the following exception
org.apache.catalina.ha.session.DeltaManager requestCompleted
SEVERE: Unable to serialize delta request for sessionid [FE02AF01C76F41D042FE04692462D983.tomcat1]
java.io.NotSerializableException: org.apache.tomcat.dbcp.dbcp.PoolingDataSource$PoolGuardConnectionWrapper
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1081)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1375)
.....
Since there are more than "I can count" no of pages, it's not easy to replace the code which sets the connection object in session from each and every page. And the complete app is based on this connection object(Datasources also play an important role).
Is there a way I can change this class so that it can be persisted in session?
If I understood correctly, I would say that replicating a DataSource is not correct, it can not work.
What need to be done is, after deserialing, to get a new (local) DataSource that corresponds to the need, and set it in the field. It is possible that this happens already in your code, look for a readResolve method.
If some parameters are needed to know which DataSource, they can be serialized themselves (because they are no DataSource, they could be just Strings for example).

Categories