Hello, I have a servlet that is modifying an osgi config in the repo, which is working as intended and reflecting correctly in crx/de, but the new config is not being reflected in the console until I do a restart, and it isn't taking precedence over the previous config either when I hit the page that it applies to. Is there a way to tell the console to check for this new config? Or something missing in my code? Any help is appreciated!
Servlet code:
@component(service= Servlet.class,
property={
Constants.SERVICE_DESCRIPTION + "=Changes the home to maintenance page.",
"sling.servlet.methods=" + HttpConstants.METHOD_GET,
"sling.servlet.resourceTypes="+ "sling/servlet/default",
"sling.servlet.paths=/bin/my/portal-maintenance"
})
public class PortalMaintenanceServlet extends SlingAllMethodsServlet {
@Reference
private ResourceResolverFactory resourceResolverFactory;
@Reference
PortalConfigHelper portalConfigHelper;
@Reference
private ConfigurationAdmin configAdmin;
private static Logger logger = LoggerFactory.getLogger(PortalMaintenanceServlet.class);
/**
* Obtains the necessary parameters
*
* @Param request the request
* @Param response the response
* @throws IOException
*/
protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException{
logger.info("Starting servlet");
ResourceResolver resolver = null;
try {
Map<String, Object> param = new HashMap<>();
param.put(ResourceResolverFactory.USER, MyConstants.MY_SYSTEM_USER);
resolver = resourceResolverFactory.getServiceResourceResolver(param);
Map<String, String> reportParams = new HashMap<>();
reportParams.put("publishInstances", StringUtils.join(portalConfigHelper.getPublishDetails(), ';'));
String[] publishInstances = reportParams.get("publishInstances").split(";");
try {
for(String each : publishInstances) {
Jcr2davRepositoryFactory repoFactory = new Jcr2davRepositoryFactory();
String[] publishDetails = each.split("--");
String userName = publishDetails[1].split(":")[0];
String password = publishDetails[1].split(":")[1];
String urlString = String.format("http://%s/crx/server", publishDetails[0]);
Map<String, String> params = new HashMap<String, String>();
params.put("org.apache.jackrabbit.repository.uri", urlString);
logger.info("trying to connect to repo at: " + publishDetails[0]);
Repository repository = repoFactory.getRepository(params);
logger.info("logging in to repo");
Session session = repository.login(new SimpleCredentials(userName,password.toCharArray()),"crx.default");
Node root = null;
Node contentNode = null;
Properties properties = new Properties();
boolean success = false;
try {
root = session.getRootNode();
contentNode = root.getNode("apps/system/config/com.my.aem.dam.core.configs.PortalConfigHelperImpl.config/jcr:content");
if(contentNode == null){
logger.info("unable to get node");
}
logger.info("getting input stream");
InputStream content = contentNode.getProperty("jcr:data").getBinary().getStream();
logger.info("loading content");
properties.load(content);
logger.info("setting homepage");
properties.setProperty("homePageUrl", "/content/my-portal/maintenance-page.html");
logger.info("Set homepage to " + properties.getProperty("homePageUrl"));
success = true;
if(success) {
logger.info("saving settings");
ByteArrayOutputStream fileOut = new ByteArrayOutputStream();
properties.store(fileOut, "Maintenance Page");
ByteArrayInputStream is = new ByteArrayInputStream(fileOut.toByteArray());
ValueFactory valueFactory = resolver.adaptTo(Session.class).getValueFactory();
Binary contentValue = valueFactory.createBinary(is);
contentNode.setProperty("jcr:data", contentValue);
contentValue.dispose();
is.close();
session.save();
fileOut.close();
content.close();
}
}
catch(Exception e) {
System.out.println("Could not set maintenance page" + e);
}
}
}catch (RepositoryException e){
logger.error("Check the permission and the settings of the service user for this service.\n", e);
response.sendError(SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR, MyConstants.MY_SERVER_ERROR
+ "\n" + e.getMessage());
}
} catch (LoginException e) {
logger.error("Error while trying to login.\n", e);
response.sendError(SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR, MyConstants.MY_SERVER_ERROR
+ "\n" + e.getMessage());
if(resolver != null)
resolver.close();
}
}
}
Hi @DNest19, I see you have ConfigurationAdmin object but you are not using it. I think that is the way to go. It will simplify your code a lot. I did quick check on my local instance, you could use code like below:
Configuration config = configAdmin.getConfiguration("com.my.aem.dam.core.configs.PortalConfigHelperImpl"); Dictionary dictionary = config.getProperties(); dictionary.put("homePageUrl", "/content/my-portal/maintenance-page.html"); config.update(dictionary);
In general it will get OSGi configuration for given PID, read current configuration and update it with new value. Configuration is updated and applied immediately - changes can be seen in OSGi console as well. Instance restart is not needed.
If I good understand your code, you are accessing repository of some remote instance from the other instance - I think this approach is over complicated. If you will run your code on each instance separately complexity of your code will decrease significantly, and your servlet could look like that.
@Component(service= Servlet.class, property={ Constants.SERVICE_DESCRIPTION + "=Changes the home to maintenance page.", "sling.servlet.methods=" + HttpConstants.METHOD_GET, "sling.servlet.resourceTypes="+ "sling/servlet/default", "sling.servlet.paths=/bin/my/portal-maintenance" }) public class PortalMaintenanceServlet extends SlingAllMethodsServlet { @Reference private ConfigurationAdmin configAdmin; @Override protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException { Configuration config = configAdmin .getConfiguration("com.my.aem.dam.core.configs.PortalConfigHelperImpl"); Dictionary dictionary = config.getProperties(); dictionary.put("homePageUrl", "/content/my-portal/maintenance-page.html"); config.update(dictionary); } }
Yeah that configadmin was leftover from an older version of the code. The reason I'm accessing the repos remotely is that I'm trying to make a button for a tools page on the author instance that changes these settings on two publisher instances. The goal is to give someone other than myself the ability to do this without having to access the system console or crx/de.
Ok, I got the case, but I still think that even if you would like to control it from author, then you should go in different direction, e.g.
Using OSGi API - for config modification in my opinion is the right approach, of course how this will be triggered is a different story.
I am avoiding replication because some of the settings might differ between the author and publisher, but exposing the endpoint sounds like a good alternative! Can you point me to any resources on this method? If I can't get this code to work soon I'll try that instead, thank you!
Hi @DNest19, do you have the config stored under /apps/<project_structure>/config? Can you try changing the value there as well and see if the OSGi console is updating the value after making the change?
Jineet
I tried that and it doesn't reflect in the console either.
Check this for precedence https://experienceleague.adobe.com/docs/experience-manager-65/deploying/configuring/configuring-osgi...
According to that article, my implementation should take effect immediately:
Configuration changes made while the system is running trigger a reload with the modified configuration.
Then the following order of precedence applies:
i am modifying the config in /apps
I found one issue and it was really simple and dumb lol. For some reason this:
properties.setProperty("homePageUrl", "/content/my-portal/maintenance-page.html");
wasn't actually getting picked up as a string in the datastream. I added a pair of escaped quotes around the url and now it picks up the new string, but only after I manually hit "save all" in crx/de. Is there a way to programmatically perform that action? I thought session.save() should, but that's not working out.
properties.setProperty("homePageUrl", "\"/content/my-portal/maintenance-page.html\"");
Edited my response. I though this had solved the issue, but I still have to hit save all in the repo to get the changes to pick up. Is there a way to perform that action programmatically? I thought session.save() is more or less equivalent, but it doesn't seem to be.
@DNest19, this line could be the root cause of your current problem:
ValueFactory valueFactory = resolver.adaptTo(Session.class).getValueFactory();
You are using local resolver and local session instead of remote session that you have created here:
Session session = repository.login(new SimpleCredentials(userName,password.toCharArray()),"crx.default");
So you could try something like this:
ValueFactory valueFactory = resolver.adaptTo(Session.class).getValueFactory();
// replace with ValueFactory valueFactory = session.getValueFactory();
If this will work then you will not need the resolver and resolver related code at all.
I tried this fix and it's still not getting picked up until I hit save all manually.
Hi @DNest19, so I think I've found out what is the problem. Below are the details and modified version of your code.
I've identified following issues:
ValueFactory valueFactory = resolver.adaptTo(Session.class).getValueFactory(); // replace with ValueFactory valueFactory = session.getValueFactory();
contentNode.setProperty("jcr:data", contentValue);
// missing jcr:lastModified update contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
contentValue.dispose(); is.close(); session.save(); ileOut.close(); content.close(); // replaced with session.save(); contentValue.dispose(); is.close(); ileOut.close(); content.close();
I have applied above changes and I was able to run your code successfully, and get OSGi configuration update without doing additional save on crx/de level. Changes are also visible directly from OSGi console.
Below you can find 2 versions of code:
@Component(service= Servlet.class, property={ Constants.SERVICE_DESCRIPTION + "=Changes the home to maintenance page.", "sling.servlet.methods=" + HttpConstants.METHOD_POST, "sling.servlet.resourceTypes="+ "sling/servlet/default", "sling.servlet.paths=/bin/my/portal-maintenance" }) public class PortalMaintenanceServlet extends SlingAllMethodsServlet { @Reference private ResourceResolverFactory resourceResolverFactory; @Reference PortalConfigHelper portalConfigHelper; @Reference private ConfigurationAdmin configAdmin; private static Logger logger = LoggerFactory.getLogger(PortalMaintenanceServlet.class); /** * Obtains the necessary parameters * * @Param request the request * @Param response the response * @throws IOException */ protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException{ logger.info("Starting servlet"); ResourceResolver resolver = null; try { Map<String, Object> param = new HashMap<>(); param.put(ResourceResolverFactory.USER, MyConstants.MY_SYSTEM_USER); resolver = resourceResolverFactory.getServiceResourceResolver(param); Map<String, String> reportParams = new HashMap<>(); reportParams.put("publishInstances", StringUtils.join(portalConfigHelper.getPublishDetails(), ';')); String[] publishInstances = reportParams.get("publishInstances").split(";"); try { for(String each : publishInstances) { Jcr2davRepositoryFactory repoFactory = new Jcr2davRepositoryFactory(); String[] publishDetails = each.split("--"); String userName = publishDetails[1].split(":")[0]; String password = publishDetails[1].split(":")[1]; String urlString = String.format("http://%s/crx/server", publishDetails[0]); Map<String, String> params = new HashMap<String, String>(); params.put("org.apache.jackrabbit.repository.uri", urlString); logger.info("trying to connect to repo at: " + publishDetails[0]); Repository repository = repoFactory.getRepository(params); logger.info("logging in to repo"); Session session = repository.login(new SimpleCredentials(userName,password.toCharArray()),"crx.default"); Node root = null; Node contentNode = null; Properties properties = new Properties(); boolean success = false; try { root = session.getRootNode(); contentNode = root.getNode("apps/system/config/com.my.aem.dam.core.configs.PortalConfigHelperImpl.config/jcr:content"); if(contentNode == null){ logger.info("unable to get node"); } logger.info("getting input stream"); InputStream content = contentNode.getProperty("jcr:data").getBinary().getStream(); logger.info("loading content"); properties.load(content); logger.info("setting homepage"); properties.setProperty("homePageUrl", "\"/content/my-portal/maintenance-page.html\""); logger.info("Set homepage to " + properties.getProperty("homePageUrl")); success = true; if(success) { logger.info("saving settings"); ByteArrayOutputStream fileOut = new ByteArrayOutputStream(); properties.store(fileOut, "Maintenance Page"); ByteArrayInputStream is = new ByteArrayInputStream(fileOut.toByteArray()); ValueFactory valueFactory = session.getValueFactory(); Binary contentValue = valueFactory.createBinary(is); contentNode.setProperty("jcr:data", contentValue); contentNode.setProperty("jcr:lastModified", Calendar.getInstance()); session.save(); contentValue.dispose(); is.close(); fileOut.close(); content.close(); } } catch(Exception e) { System.out.println("Could not set maintenance page" + e); } } } catch (RepositoryException e){ logger.error("Check the permission and the settings of the service user for this service.\n", e); response.sendError(SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR, MyConstants.MY_SERVER_ERROR + "\n" + e.getMessage()); } } catch (LoginException e) { logger.error("Error while trying to login.\n", e); response.sendError(SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR, MyConstants.MY_SERVER_ERROR + "\n" + e.getMessage()); if(resolver != null) resolver.close(); } } }
@Component(service= Servlet.class, property={ Constants.SERVICE_DESCRIPTION + "=Changes the home to maintenance page.", "sling.servlet.methods=" + HttpConstants.METHOD_POST, "sling.servlet.resourceTypes="+ "sling/servlet/default", "sling.servlet.paths=/bin/my/portal-maintenance"}) public class PortalMaintenanceServlet extends SlingAllMethodsServlet { @Reference PortalConfigHelper portalConfigHelper; private static Logger logger = LoggerFactory.getLogger(PortalMaintenanceServlet.class); /** * Obtains the necessary parameters * * @Param request the request * @Param response the response * @throws IOException */ protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException{ logger.info("Starting servlet"); try { Map<String, String> reportParams = new HashMap<>(); reportParams.put("publishInstances", StringUtils.join(portalConfigHelper.getPublishDetails(), ';')); String[] publishInstances = reportParams.get("publishInstances").split(";"); try { for(String each : publishInstances) { Jcr2davRepositoryFactory repoFactory = new Jcr2davRepositoryFactory(); String[] publishDetails = each.split("--"); String userName = publishDetails[1].split(":")[0]; String password = publishDetails[1].split(":")[1]; String urlString = String.format("http://%s/crx/server", publishDetails[0]); Map<String, String> params = new HashMap<String, String>(); params.put("org.apache.jackrabbit.repository.uri", urlString); logger.info("trying to connect to repo at: " + publishDetails[0]); Repository repository = repoFactory.getRepository(params); logger.info("logging in to repo"); Session session = repository.login(new SimpleCredentials(userName,password.toCharArray()),"crx.default"); Node root = null; Node contentNode = null; Properties properties = new Properties(); boolean success = false; try { root = session.getRootNode(); contentNode = root.getNode("apps/system/config/com.my.aem.dam.core.configs.PortalConfigHelperImpl.config/jcr:content"); if (contentNode == null){ logger.info("unable to get node"); } logger.info("getting input stream"); InputStream content = contentNode.getProperty("jcr:data").getBinary().getStream(); logger.info("loading content"); properties.load(content); logger.info("setting homepage"); properties.setProperty("homePageUrl", "\"/content/my-portal/maintenance-page.html\""); logger.info("Set homepage to " + properties.getProperty("homePageUrl")); success = true; if (success) { logger.info("saving settings"); ByteArrayOutputStream fileOut = new ByteArrayOutputStream(); properties.store(fileOut, "Maintenance Page"); ByteArrayInputStream is = new ByteArrayInputStream(fileOut.toByteArray()); ValueFactory valueFactory = session.getValueFactory(); Binary contentValue = valueFactory.createBinary(is); contentNode.setProperty("jcr:data", contentValue); contentNode.setProperty("jcr:lastModified", Calendar.getInstance()); session.save(); contentValue.dispose(); is.close(); fileOut.close(); content.close(); } } catch (Exception e) { System.out.println("Could not set maintenance page" + e); } } } catch (RepositoryException e){ logger.error("Check the permission and the settings of the service user for this service.\n", e); response.sendError(SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR, MyConstants.MY_SERVER_ERROR + "\n" + e.getMessage()); } } catch (LoginException e) { logger.error("Error while trying to login.\n", e); response.sendError(SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR, MyConstants.MY_SERVER_ERROR + "\n" + e.getMessage()); } } }
Thank you!