1) I am trying to inject ResourceResolverFactory in SlingModel class like this:
package com.aem.sites.models;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aem.sites.services.WeatherService;
@Model(adaptables=Resource.class)
public class Banner {
final static Logger logger = LoggerFactory.getLogger(Banner.class);
@Inject
private WeatherService weatherService;
private String serviceEndpoint;
private String apiKey;
private String temperature;
@Inject
@Optional
@Named("bannerText")
private String bannerText;
@Inject
@Optional
@Named("button1Text")
private String button1Text;
@Inject
@Optional
@Named("button2Text")
private String button2Text;
@Inject
private ResourceResolverFactory resourceResolverFactory;
@PostConstruct
public void init() {
serviceEndpoint = weatherService.getServiceEndpoint();
apiKey = weatherService.getApiKey();
temperature = weatherService.getTemperatureValue()+" "+weatherService.getUnit();
checkTemperatureProperty();
}
public String getServiceEndpoint() {
return serviceEndpoint;
}
public String getApiKey() {
return apiKey;
}
public String getTemperature() {
return temperature;
}
public String getBannerText() {
return bannerText;
}
public String getButton1Text() {
return button1Text;
}
public String getButton2Text() {
return button2Text;
}
private void checkTemperatureProperty() {
logger.info("**********************checkTemperatureProperty****************");
Map<String, Object> param = new HashMap<String, Object>();
param.put(ResourceResolverFactory.SUBSERVICE,"getResourceResolver");
ResourceResolver resourceResolver = null;
try {
if(resourceResolverFactory == null) {
logger.info("***********************resourceResolverFactory is null******************************");
}
else {
resourceResolver = resourceResolverFactory.getServiceResourceResolver(param);
logger.info("**********************resource resolver id****************"+resourceResolver.getUserID());
//Resource resource = resourceResolver.getResource("/content/aemsite/en/jcr:content/root/responsivegrid/banner");
//ValueMap readMap = resource.getValueMap();
//logger.info(readMap.get("temperature", ""));
}
}
catch (LoginException e) {
logger.error("LoginException",e);
} finally{
if(resourceResolver != null && resourceResolver.isLive()){
resourceResolver.close();
}
}
}
}
The method in question here is checkTemperatureProperty(). I have created system user that looks like this :
I have also configured the Mapper Service Amendment like this:
The symbolic name of the bundle looks like this :
The ResourceResolverFactor injection is throwing LoginException in the Sling Model class and here is the error:
com.aem.sites.models.Banner LoginException
org.apache.sling.api.resource.LoginException: Cannot derive user name for bundle org.apache.sling.models.impl [489] and sub service getResourceResolver
at org.apache.sling.resourceresolver.impl.ResourceResolverFactoryImpl.getServiceResourceResolver(ResourceResolverFactoryImpl.java:86)
While when I am trying to use the ResourceResolverFactory in Service using @Reference, it is throwing a null pointer exception.
The class in question is given below, method checkTemperatureProperty() line number 195:
package com.aem.sites.services.impl;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Reference;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aem.sites.interfaces.Configuration;
import com.aem.sites.pojos.CurrentConditions;
import com.aem.sites.services.WeatherService;
import com.google.gson.Gson;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.resource.LoginException;
// TODO: Auto-generated Javadoc
/**
* The Class WeatherServiceImpl.
*/
@Component(service = WeatherService.class,
immediate = true,
configurationPid = "com.aem.sites.services.impl.WeatherServiceImpl")
@Designate(ocd = Configuration.class)
public class WeatherServiceImpl implements WeatherService {
/** The Constant logger. */
final static Logger logger = LoggerFactory.getLogger(WeatherServiceImpl.class);
/** The service endpoint. */
private String serviceEndpoint;
/** The api key. */
private String apiKey;
/** The response body. */
private String responseBody;
/** The temperature value. */
private String temperatureValue;
/** The unit. */
private String unit;
/** The resource resolver factory. */
@Reference
private ResourceResolverFactory resourceResolverFactory;
/* (non-Javadoc)
* @see com.aem.sites.services.WeatherService#getServiceEndpoint()
*/
@Override
public String getServiceEndpoint() {
return serviceEndpoint;
}
/* (non-Javadoc)
* @see com.aem.sites.services.WeatherService#getApiKey()
*/
@Override
public String getApiKey() {
return apiKey;
}
/**
* Activate.
*
* @param config the config
*/
@Activate
@Modified
protected final void activate(Configuration config) {
logger.info("**********************************************activate********************************************");
serviceEndpoint = config.getServiceURL();
apiKey = config.getApiKey();
String serviceURL = createRequestURL();
logger.info("**************************The serviceURL is*******************************************"+serviceURL);
callWebService(serviceURL);
String responseString = getResponseBody();
logger.info("**************************The response body is*******************************************"+responseString);
convertStringToPojos(responseString);
checkTemperatureProperty();
//saveTemperatureInTheNode();
}
/**
* Creates the request URL.
*
* @return the string
*/
private String createRequestURL() {
StringBuilder sb = new StringBuilder(); ;
if(!StringUtils.isEmpty(serviceEndpoint)) {
sb.append(serviceEndpoint);
sb.append("/currentconditions/v1/33785");
sb.append("?apikey=");
sb.append(apiKey);
}
return sb.toString();
}
/**
* Call web service.
*
* @param serviceURL the service URL
*/
private void callWebService(String serviceURL) {
if(!StringUtils.isEmpty(serviceURL)) {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet(serviceURL);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
if (entity != null) {
String responseString = EntityUtils.toString(entity);
setResponseBody(responseString);
}
}
else {
logger.info("*********************Unexpected response status: " + status);
}
} catch (IOException | UnsupportedOperationException e) {
// TODO Auto-generated catch block
logger.info("***********************************inside catch block****************IOException"+e.getMessage());
}
finally {
if(null != response){
try {
response.close();
httpclient.close();
} catch (IOException e) {
logger.info("***********************************inside finally block****************IOException"+e.getMessage());
}
}
}
}
}
/**
* Convert string to pojos.
*
* @param jsonString the json string
*/
private void convertStringToPojos(String jsonString) {
if(!StringUtils.isEmpty(jsonString)) {
jsonString = jsonString.substring(1);
if(!StringUtils.isEmpty(jsonString)) {
int ind = jsonString.lastIndexOf("]");
if(ind >= 0) {
jsonString = jsonString.substring(0,jsonString.length()-1);
}
Gson gson = new Gson();
if(!StringUtils.isEmpty(jsonString)) {
CurrentConditions conditions = gson.fromJson(jsonString, CurrentConditions.class);
logger.info("***************current temperature is*****************"+conditions.getTemperature().getImperial().getValue());
temperatureValue = conditions.getTemperature().getImperial().getValue();
unit = conditions.getTemperature().getImperial().getUnit();
}
}
}
}
/**
* Save temperature in the node.
*/
private void checkTemperatureProperty() {
logger.info("**********************checkTemperatureProperty****************");
Map<String, Object> param = new HashMap<String, Object>();
param.put(ResourceResolverFactory.SUBSERVICE,"getResourceResolver");
ResourceResolver resourceResolver = null;
try {
resourceResolver = resourceResolverFactory.getServiceResourceResolver(param);
logger.info("**********************resource resolver id****************"+resourceResolver.getUserID());
Resource resource = resourceResolver.getResource("/content/aemsite/en/jcr:content/root/responsivegrid/banner");
ValueMap readMap = resource.getValueMap();
logger.info(readMap.get("temperature", ""));
}
catch (LoginException e) {
logger.error("LoginException",e);
} finally{
if(resourceResolver != null && resourceResolver.isLive()){
resourceResolver.close();
}
}
}
private void saveTemperatureProperty() {
}
/**
* Gets the response body.
*
* @return the response body
*/
public String getResponseBody() {
return responseBody;
}
/**
* Sets the response body.
*
* @param responseBody the new response body
*/
public void setResponseBody(String responseBody) {
this.responseBody = responseBody;
}
/* (non-Javadoc)
* @see com.aem.sites.services.WeatherService#getTemperatureValue()
*/
@Override
public String getTemperatureValue() {
// TODO Auto-generated method stub
return temperatureValue;
}
/* (non-Javadoc)
* @see com.aem.sites.services.WeatherService#getUnit()
*/
@Override
public String getUnit() {
// TODO Auto-generated method stub
return unit;
}
/**
* Deactivate.
*/
@Deactivate
protected void deactivate() {
}
}
I first tried using the ResourceResolverFactory in the service class(second class) and then in the Banner.java(first class). But it worked in neither.
I have gone through various tutorials and also similar questions raised here on stackoverflow. Some of them are here:
ResourceResolverFactory getServiceResourceResolver throws Exception in AEM 6.1
ResourceResolverFactory and SlingRepository null in Service class
ResourceResolverFactory is NULL (Adobe Experience Manager AEM)
and followed it completely but couldn't find the resolution. Any help is appreciated.
Thanks in advance.
I see You are using OSGI declarative services (DS)
A few things:
maven-bundle-plugin
dependency has to be version 3.2.0
or greaterorg.osgi.service.component.annotations
and not the SCR annotations in package org.apache.felix.scr.annotations.Reference
in your weather service, I see you are using SCR's @Reference
and not OSGI's @Reference
In your sling model:
You should use OSGI service injector for services: @OSGiService
Though using @Inject
works just fine, a specific injector is better for readability and performance, albeit insignificant, since we only use a specific injector
Here is an example of a service that uses some of your code:
Weather service interface:
package com.aem.sites.services;
public interface WeatherService
{
public String checkApps();
}
Weather service impl:
package com.aem.sites.services.impl;
import java.util.HashMap;
import java.util.Map;
import org.apache.felix.scr.annotations.Reference;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aem.sites.services.Configuration;
import com.aem.sites.services.WeatherService;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.LoginException;
/**
* The Class WeatherServiceImpl.
*/
@Component(service = WeatherService.class,
immediate = true,
configurationPid = "com.aem.sites.services.impl.WeatherServiceImpl")
@Designate(ocd = Configuration.class)
public class WeatherServiceImpl implements WeatherService {
/** The Constant logger. */
final static Logger logger = LoggerFactory.getLogger(WeatherServiceImpl.class);
/** The resource resolver factory. */
@Reference
private ResourceResolverFactory resourceResolverFactory;
private ResourceResolver resolver;
/**
* Activate.
*
* @param config the config
*/
@Activate
@Modified
protected final void activate(Configuration config) {}
/**
* check if we can get /apps resource, print weather it's null or not
*/
public String checkApps() {
logger.info("*************** checkApps");
Map<String, Object> param = new HashMap<String, Object>();
param.put(ResourceResolverFactory.SUBSERVICE,"getResourceResolver");
ResourceResolver resourceResolver = null;
try
{
resourceResolver = resourceResolverFactory.getServiceResourceResolver(param);
logger.info("*************** resource resolver user id: "+resourceResolver.getUserID());
Resource appsResource = resourceResolver.getResource("/apps");
// return appropriate msg
return appsResource == null
? "apps resource is null"
: "apps resource is NOT null";
}
catch (LoginException e)
{
logger.error("LoginException",e);
return e.toString();
}
finally
{
if(resourceResolver != null && resourceResolver.isLive()){
resourceResolver.close();
}
}
}
/**
* Deactivate.
*/
@Deactivate
protected void deactivate() {
}
}
banner model:
package com.aem.sites.models;
import javax.annotation.PostConstruct;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aem.sites.services.WeatherService;
@Model(adaptables=Resource.class)
public class Banner {
final static Logger logger = LoggerFactory.getLogger(Banner.class);
@OSGiService
private WeatherService weatherService;
private String msg;
@PostConstruct
public void init() {
msg = weatherService.checkApps();
}
public String getMsg() {
return msg;
}
}
Simple component that displays the msg:
<h1 data-sly-use.banner="com.aem.sites.models.Banner">${banner.msg}</h1>
UPDATE: regarding the use of resourceResolver in a sling model:
It's better to keep logic, that does no use the model's resource/resourceResolver, in a separate service. In your code,
Banner#checkTemperatureProperty
should be in a service and not in the model itself.
This my personal opinion and there is no right or wrong here:
You can always get a resourceResolver in a sling model by injecting it OR by injecting a resource or a request then getting the resolver from the resource/request. In either case, you'll get a resourceResolver that has the same permissions as the requesting user, which is preferred for components. for example, if you need to get a resource from a different component on the same page or in /content in general, this is the way to go.
If you have a use case where you need perform actions the requesting user or an anonymous user does not have sufficient permissions to do nor do you want to give them permissions for, you can get a resourceResolver via system user that has sufficient permissions. for example, you want to read some property from /apps or you want to kick off a workflow... etc. In this case, it's a bit more organized to keep this code in a service rather than the model itself, and maybe generalize it so that you can reuse it in other models.