Expand my Community achievements bar.

SOLVED

.cfg.json file not working

Avatar

Level 9

Sorry to those following my saga to get a config file read by a service - it must be painful.

I have now got a config file here:

/aem-guides-wknd.ui.config/src/main/content/jcr_root/apps/wknd/osgiconfig/config/com.adobe.aem.guides.wknd.core.services.MyService.cfg.json

With the following content:

 

{
  "endpoint":"this is a test",
  "timeout":"10",
  "logResponse":true
}

 

But these values are not being picked up. Do I have the file name wrong? or the format of the variables in the file?  I have done a "mvn clean install -PautoInstallSinglePackage" in the project root.

The service parameters are defined here:

/aem-guides-wknd.core/src/main/java/com/adobe/aem/guides/wknd/core/services/MyConfiguration.java

With the following content:

 

@ObjectClassDefinition(
		name = "My Configuration", 
		description = "This configuration reads the values to make ")
public  MyConfiguration {

	@AttributeDefinition(
			name = "HttpVersion", 
			description = "Choose HTTP version used by API", 
			options = {
			@Option(label = "HTTP 1.1", value = "1.1"), (label = "HTTP 2.0", value = "2.0") })
	public String getHttpVersion();

	@AttributeDefinition(
			name = "Endpoint", 
			description = "Enter the endpoint/URL",
			type = AttributeType.STRING)
	public String getEndpoint();

	@AttributeDefinition(
			name = "Timeout", 
			defaultValue = "10000",
			description = "Enter the Timeout in ms",
			type = AttributeType.INTEGER)
	public int getTimeout();
	
	@AttributeDefinition(
			name = "Log Request and Responses", 
			description = "Enter the Timeout in ms",
			type = AttributeType.BOOLEAN)
	public boolean getLogResponse();

 

My service simply prints the config values it finds.

com.adobe.aem.guides.wknd.core.services.MyConfiguration : 
{hashCode=0, getTimeout=10000, equals=false, annotationType=null,
getEndpoint=haha, toString=null, getLogResponse=false}

getEndpoint=haha, it should be "this is a test"

timeout is 10000, it should be 10.

It is ignoring my config file, and is just picking up the defaults and the value of endpoint I set via http://localhost:4502/system/console/configMgr#

under a config under "My Service"

TB3dock_0-1617870649801.png

MyServiceImpl looks like this:

 

package com.adobe.aem.guides.wknd.core.services;

import ...

@Component(service = MyService.class, immediate = true)
@Designate(ocd = MyConfiguration.class)
public class MyServiceImpl implements MyService{

	private MyConfiguration configuration;

	@Override
	public String getMe() {
	  return(configuration.toString());
	}
	
	@Activate
	protected void activate(MyConfiguration configuration) {
		this.configuration = configuration;
	}
	

}

 

==== UPDATE =====

There seems to be some conflict between the accessor method e.g. getTimeout(), and the name of the variable.  I assumed that the variable returned by getTimeout() would be timeout.  As suggested below, I changed the AttributeDefinition methods to be more "fieldy":

 

	@AttributeDefinition(
			name = "Endpoint", 
			description = "Enter the Spine endpoint/URL",
			type = AttributeType.STRING)
	public String endpoint();
	
	@AttributeDefinition(
			name = "Timeout", 
			defaultValue = "10000",
			description = "Enter the Timeout in ms",
			type = AttributeType.INTEGER)
	public int timeout();

 

after doing mvn clean install -PautoInstallSinglePackage now, when I hit the servlet, I get:

 

com.adobe.aem.guides.wknd.core.services.MyConfiguration : {endpoint=null, httpVersion=null, hashCode=0, equals=false, logResponse=false, annotationType=null, toString=null, timeout=0}

 

If I look in http://localhost:4502/system/console/configMgr I now see two entries for my settings:

A new one called com.adobe.aem.guides.wknd.core.services.MyService, with the values set in the config file and a PID of com.adobe.aem.guides.wknd.core.services.MyService

The old one called "Spine Configuration" which was created when I first deployed the servlet/service code and a PID of com.adobe.aem.guides.wknd.core.services.MyServiceImpl

I just edited the "endpoint" value in the "My Configuration" (old) version of the settings in configmgr. Now when I run the servlet, I see these values (not the ones from my config file)

 

com.adobe.aem.guides.wknd.core.services.MyConfiguration : {endpoint=test2, httpVersion=1.1, hashCode=0, equals=false, logResponse=false, annotationType=null, toString=null, timeout=10000}

 

So the config file is writing to a configmgr section called "com.adobe.aem.guides.wknd.core.services.MySerice", but my service is reading from a configmgr section called "My Configuration", and when I dump the configuraiton object from my servlet, its calling it "MyConfiguration"

=== update 2====

Because the service seems to be reading from the wrong configuration, I tried changing the config file name to:  

/aem-guides-wknd.ui.config/src/main/content/jcr_root/apps/wknd/osgiconfig/config/com.adobe.aem.guides.wknd.core.services.MyConfiguration.cfg.json

This didnt help.

 

=== update 3 ===

Having renamed the config file to 

/aem-guides-wknd.ui.config/src/main/content/jcr_root/apps/wknd/osgiconfig/config/com.adobe.aem.guides.wknd.core.services.MyServiceImpl.cfg.json

I deleted both entries from http://localhost:4502/system/console/configMgr then rebuild and deployed.

The result is two entries in the config manager, both with the same Persistent Identity (PID) of com.adobe.aem.guides.wknd.core.services.SpineServiceImpl and nothing under "Configuration Binding" (whatever that is)

The config is read from the wrong one.

1 Accepted Solution

Avatar

Correct answer by
Employee Advisor

Please make sure that

  • Your json file name matches the Service PID (see web console UI below "Configuration Information"; this part is cut off in your screenshot).
  • The keys in your json file match the configuration keys of your service (see web console; key is in brackets after the parameter description). According to your shared code (variable definition is the key) and the screenshot this is currently "getHttpVersion", "getEndpoint", "getTimeout", "getLogResponse" instead of your currently used keys "endpoint", "timeout" and "logResponse".

 

---------- Update ----------

 

Starting from the base project that the AEM Maven archetype generates, I extended the existing example SimpleServlet to be configurable. Here is my resulting source code that works as expected:

 

com.mysite.core.servlets.SimpleServlet

package com.mysite.core.servlets;

import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.servlets.annotations.SlingServletResourceTypes;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.propertytypes.ServiceDescription;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.day.cq.commons.jcr.JcrConstants;


@Designate(ocd=SimpleServlet.Config.class)
@Component(service = { Servlet.class })
@SlingServletResourceTypes(
        resourceTypes="mysite/components/page",
        methods=HttpConstants.METHOD_GET,
        extensions="txt")
@ServiceDescription("Simple Demo Servlet")
public class SimpleServlet extends SlingSafeMethodsServlet {

    private static final long serialVersionUID = 1L;

    @ObjectClassDefinition(name="A simple demo servlet",
            description = "Simple demo servlet with properties")
    public static @interface Config {
    	@AttributeDefinition(name = "Prefix text")
    	String prefix() default "PRE";
    }
    
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private String prefix;
    
    @Override
    protected void doGet(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
        final Resource resource = req.getResource();
        resp.setContentType("text/plain");
        resp.getWriter().write(prefix + " Title = " + resource.getValueMap().get(JcrConstants.JCR_TITLE));
    }
    
    @activate
    protected void activate(final Config config) {
        prefix = config.prefix();
        logger.debug("{} activated with prefix {}", getClass(), prefix);
    }    
}

 

mysite.ui.config/src/main/content/jcr_root/apps/mysite/osgiconfig/config/com.mysite.core.servlets.SimpleServlet.cfg.json

{
  "prefix": "My Prefix"
}

 

View solution in original post

14 Replies

Avatar

Correct answer by
Employee Advisor

Please make sure that

  • Your json file name matches the Service PID (see web console UI below "Configuration Information"; this part is cut off in your screenshot).
  • The keys in your json file match the configuration keys of your service (see web console; key is in brackets after the parameter description). According to your shared code (variable definition is the key) and the screenshot this is currently "getHttpVersion", "getEndpoint", "getTimeout", "getLogResponse" instead of your currently used keys "endpoint", "timeout" and "logResponse".

 

---------- Update ----------

 

Starting from the base project that the AEM Maven archetype generates, I extended the existing example SimpleServlet to be configurable. Here is my resulting source code that works as expected:

 

com.mysite.core.servlets.SimpleServlet

package com.mysite.core.servlets;

import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.servlets.annotations.SlingServletResourceTypes;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.propertytypes.ServiceDescription;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.day.cq.commons.jcr.JcrConstants;


@Designate(ocd=SimpleServlet.Config.class)
@Component(service = { Servlet.class })
@SlingServletResourceTypes(
        resourceTypes="mysite/components/page",
        methods=HttpConstants.METHOD_GET,
        extensions="txt")
@ServiceDescription("Simple Demo Servlet")
public class SimpleServlet extends SlingSafeMethodsServlet {

    private static final long serialVersionUID = 1L;

    @ObjectClassDefinition(name="A simple demo servlet",
            description = "Simple demo servlet with properties")
    public static @interface Config {
    	@AttributeDefinition(name = "Prefix text")
    	String prefix() default "PRE";
    }
    
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private String prefix;
    
    @Override
    protected void doGet(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
        final Resource resource = req.getResource();
        resp.setContentType("text/plain");
        resp.getWriter().write(prefix + " Title = " + resource.getValueMap().get(JcrConstants.JCR_TITLE));
    }
    
    @activate
    protected void activate(final Config config) {
        prefix = config.prefix();
        logger.debug("{} activated with prefix {}", getClass(), prefix);
    }    
}

 

mysite.ui.config/src/main/content/jcr_root/apps/mysite/osgiconfig/config/com.mysite.core.servlets.SimpleServlet.cfg.json

{
  "prefix": "My Prefix"
}

 

Avatar

Employee Advisor
I would recommend to rename the variable identifiers in your Java class. getFoo usually indicates a method but this definition is actually the parameter definition. So "foo" would be a better name compared to "getFoo" here.

Avatar

Level 9

in the MyConfiguration file contains methods such as getEndpoint(). Presumably these are getters for a not-defined variable called endpoint.

In my service code, I can do things like: 

  String endpoint = configuration.getEndpoint();

 

getEndpoint() in this case is the getter defined in the "MyConfiguration.java" file.

Avatar

Employee Advisor
The important part - where the binding happens - is the declaration that is annotated with "@AttributeDefinition". That's what you need to align with the keys in the config file. If you want to keep the "getFoo" naming you need to update your config file to reflect that exact key ("getTimeout" instead of "timeout"). You're right: the declaration is technically a method, but used somewhat differently in this context. You may want to check the example from the Maven archetype that I mentioned earlier (SimpleScheduledTask). The config interface is inlined here but I shows that the common pattern of "String myParameter()" instead of "String getMyParameter()".

Avatar

Level 9
"So "foo" would be a better name compared to "getFoo" here." I tried changing the names from getXxxx to just xxxx, but now it doesnt work at all (get all nulls, see updated question above)

Avatar

Level 9
After various updates (see question), My json file is now called /aem-guides-wknd.ui.config/src/main/content/jcr_root/apps/wknd/osgiconfig/config/com.adobe.aem.guides.wknd.core.services.MySeviceImpl.cfg.json and its PID matches the PID of the "duplicate" config which is generated, and is actually used. I renamed the methods to just int timeout() etc. as suggested. This does not help - its still not getting any config via the json file, its getting it from another config in configmanger (the one create if I dont have any json file), and both of them have the same PID. I even tied wiping the install, and starting from scratch, but same issue.

Avatar

Level 9
Now everything seems to be "correct" is this a bug in AEM?

Avatar

Employee Advisor
I just tried a simple example in the AEM Maven archetype generated project and things worked out pretty straight forward.

Avatar

Employee Advisor
Here is what I did: I made the existing class com.mysite.core.servlets.SimpleServlet configurable through an OSGI config. Steps required: added an inline Config interface with my desired config values; added the @Designate(ocd=SimpleServlet.Config.class) annotation to the class; added a variable to hold my config value to SimpleServlet; added an activate(...) method and assigned the value taken from the config to the previously defined variable. Apart from that I have added a config file as mysite.ui.config/src/main/content/jcr_root/apps/mysite/osgiconfig/config/com.mysite.core.servlets.SimpleServlet.cfg.json. That's it. I'll edit my inital answer and share the source code of the adjusted class and the config file.

Avatar

Level 9
Great if you can share the source of the working version, I can try to work the example service I have backwards.

Avatar

Level 9
Hi why did you add @Designate(ocd=SimpleServlet.Config.class) instead of @Designate(ocd=SimpleServlet.class)? presumably SimpleServlet.Config is not a class?

Avatar

Employee Advisor
SimpleServlet.Config.class refers to the inlined interface definition of my configuration interface: public static @interface Config. In your case with a dedicated config class you would have to point the @Designate annotation to that configuration class. See also https://docs.osgi.org/javadoc/r6/cmpn/org/osgi/service/metatype/annotations/Designate.html and https://felix.apache.org/documentation/subprojects/apache-felix-metatype-service.html

Avatar

Level 9
The crux is that the deployment creates two duplicate OSGi config in the configmanager, both with the same PID. One is created by default, the other is created by my cfg.json file. The service is getting its settings from the wrong one, and there appears to be nothing I can do about it.

Avatar

Level 9

I got the pattern you provided to work thanks.  I gave up trying to do it outside of the service (i.e. having a separate class for the config).  FYI the log statement inside the activate does not work for me, even if I set it to log.error.