OSGI config edited in repo not applying during runtime | Community
Skip to main content
Level 3
March 2, 2022

OSGI config edited in repo not applying during runtime

  • March 2, 2022
  • 3 replies
  • 3851 views

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:

 

@8220494(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 * * @90521 request the request * @90521 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(); } } }
This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.

3 replies

lukasz-m
Community Advisor
Community Advisor
March 2, 2022

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);
    }
} 

 

DNest19Author
Level 3
March 2, 2022

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.

lukasz-m
Community Advisor
Community Advisor
March 2, 2022

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.

  • you can expose an endpoint (servlet on each publish server, and by clicking on the button in UI simple sent a request - this will run the logic that will modify OSGi config - of course you can add some additional security layer related to that servlet)
  • or you could use replication, and trigger some action on publish that will be related to that.

Using OSGi API - for config modification in my opinion is the right approach, of course how this will be triggered is a different story.

Jineet_Vora
Community Advisor and Adobe Champion
Community Advisor and Adobe Champion
March 2, 2022

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

DNest19Author
Level 3
March 2, 2022

I tried that and it doesn't reflect in the console either.

Himanshu_Jain
Community Advisor
Community Advisor
March 3, 2022
DNest19Author
Level 3
March 3, 2022

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\"");

 

DNest19Author
Level 3
March 4, 2022

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. 

DNest19Author
Level 3
March 14, 2022

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:

  • you are using incorrect session, local instead of remote
    ValueFactory valueFactory = resolver.adaptTo(Session.class).getValueFactory();
    
    // replace with
    ValueFactory valueFactory = session.getValueFactory();
  • after modifying jcr:content node by setting new binary value via jcr:data property, you did not updated jcr:lastModified property
    contentNode.setProperty("jcr:data", contentValue);
    // missing jcr:lastModified update contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
  • you are saving changes after dispose
    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:

  1. Original version with above changes:
    @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();
    			
    		}
    	}
    }
  2. Optimized version with above changes, and without resource resolver related code - as it is not needed:
    @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!