Expand my Community achievements bar.

Guidelines for the Responsible Use of Generative AI in the Experience Cloud Community.
SOLVED

@Reference annotation not working

Avatar

Level 4

I am missing something here. I am creating a new class and waht to have the resource resolver factory loaded using the @Reference annotation.

But it is not working and I am always getting an NPE when I use it.

Would someone help me to understand why? What am I missing?

Thanks.

@Component(metatype = false, immediate = true)

@Service(value = TestComponent.class)

@Properties({ @org.apache.felix.scr.annotations.Property(name = Constants.SERVICE_DESCRIPTION, value = "TestComponent"),

        @org.apache.felix.scr.annotations.Property(name = Constants.SERVICE_VENDOR, value = GeneralConstants.VENDOR),

        @org.apache.felix.scr.annotations.Property(name = "process.label", value = "TestComponent") })

public class TestComponentImpl implements TestComponent {

    protected static final Logger LOG = LoggerFactory.getLogger( TestComponent.class);

    @Reference( policy = ReferencePolicy.STATIC )

    private ResourceResolverFactory resolverFactory;

1 Accepted Solution

Avatar

Correct answer by
Employee Advisor

Hi,

resourceResolver = new ReadServiceImpl().getResourceResolver(); 

That's the problem. You are constructing the ReadServiceImpl via Constructor, and in that way SCR cannot inject any references. You should make the ReadServiceImpl a service and let SCR manage its lifecycle.

Jörg

View solution in original post

22 Replies

Avatar

Level 10

@Reference works - we have a lot of HELPX articles that show its use. See this one where we use it with Declarative Services annotations:

Adobe Experience Manager Help | Creating an Experience Manager 6.3 Page using the Page Manager API

For info on DS annotations - see: Official OSGi Declarative Services Annotations in AEM - Adobe Experience Manager | AEM/CQ | Apache S...

Avatar

Level 10

I have personally never used the extra syntax you have:

( policy = ReferencePolicy.STATIC )

Avatar

Level 4

I know it works. We have many instances where it is working. I just don't understand why it isn't in this case.

Avatar

Level 10

Try same code on a fresh instance that will tell us if issue is with code or AEM instance.

Avatar

Employee Advisor

Does the reference show up at the OSGI console (/system/console/components) for this component?

Jörg

Avatar

Level 4

Yes. The component status is active and the resource resolver factory is satisfied.

Reference resolverFactory["Satisfied","Service Name: org.apache.sling.api.resource.ResourceResolverFactory","Cardinality: 1..1","Policy: static","Policy Option: reluctant","Bound Service ID 981 (Apache Sling Resource Resolver Factory)"]

Avatar

Employee Advisor

Ok, how does the code look you are using?

Jörg

Avatar

Level 3

ReferencePolicy.STATIC is the default policy for any referenced service

Avatar

Level 4

Can you give me an idea of what you are looking for in the logs?

Avatar

Level 10

Did you try running this code on a Fresh AEM DEV instance so we can rule out if its your code or the instance. Please do so and let us know the results.

Avatar

Level 4

I started to, but there are dependencies that are not satisfied for our primary bundle. So at the moment I have suspended that approach.

Avatar

Level 4

One curiosity I discovered, I added an activate method and logged the resource resolver in the activate and it appeared non-null.

In the same test, the value for the resource resolver in the main method was null. Not sure what to make of that.

Avatar

Level 3

Can you hook a debugger to your application? Just to make sure that you're actually in the same object. It could be that once you start using that factory that for some reason, you're not in an osgi managed service anymore, but a new created object.

Avatar

Level 10

In your example - you are using it within a method that belongs to TestComponentImpl -- correct?

Avatar

Level 4

I have logging setup in all related classes. I know I am accessing the my class. No need for debugger in that regard.

Yes. I am utilizing the class from within a TestComponentImpl method.

Avatar

Employee Advisor

Hi,

can you please share the code of your component? "main method" sounds a bit strange.

Jörg

Avatar

Level 4

I have taken the time to work the ReadServiceImpl code directly into the event listener for additional testing. Adding the ResourceResolverFactory injection successfully into the listener with that test.

FormSubmitEventListener Class which calls ReadServiceImpl

=====================================================================================

package org.oclc.cq.listeners;

import org.apache.felix.scr.annotations.*;

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.commons.json.JSONObject;

import org.apache.sling.commons.osgi.PropertiesUtil;

import org.apache.sling.jcr.api.SlingRepository;

import org.oclc.cq.config.EnvironmentConfig;

import org.oclc.cq.database.daoimpl.FormSubmittedDataDaoImpl;

import org.oclc.cq.database.util.DataSourceLookup;

import org.oclc.cq.database.valueobject.FormSubmittedData;

import org.oclc.cq.model.GeneralConstants;

import org.oclc.cq.service.ReadServiceImpl;

import org.oclc.cq.utils.ConvertResourceToJSON;

import org.osgi.framework.Constants;

import org.osgi.service.component.ComponentContext;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import javax.jcr.RepositoryException;

import javax.jcr.Session;

import javax.jcr.observation.Event;

import javax.jcr.observation.EventIterator;

import javax.jcr.observation.EventListener;

import javax.sql.DataSource;

import java.util.Calendar;

import java.util.Dictionary;

@Component( metatype = true, immediate = true, label = "Form Submit Event Listener", description = "Save form data to database" )

@Service( value = EventListener.class )

@Properties( {

   @org.apache.felix.scr.annotations.Property( name = Constants.SERVICE_DESCRIPTION, value = "Form Submit Event Listener" ),

   @org.apache.felix.scr.annotations.Property( name = Constants.SERVICE_VENDOR, value = GeneralConstants.VENDOR ) } )

public class FormSubmitEventListener implements EventListener {

   protected static final Logger LOG = LoggerFactory.getLogger( FormSubmitEventListener.class);

   private static final String FORMSUBMITTEDDATA_DATASOURCE_NAME = "MySQL_FormSubmittedData_DataSource";

   @org.apache.felix.scr.annotations.Property( description = "Property to enable the event listener", boolValue = false )

   public static final String IS_ENABLED = "org.oclc.cq.listeners.FormSubmitEventListener.isEnabled";

   boolean isEnabled = false;

   @Reference
   private DataSourceLookup dataSourceLookup;

   private Session readSession;

   @Activate
   public void activate( ComponentContext context ) throws Exception {

   @SuppressWarnings( "unchecked" ) Dictionary<String, ?> props = context.getProperties();

   isEnabled = PropertiesUtil.toBoolean( props.get( IS_ENABLED ), false );

   // must be enabled and only allowed to run on author
   if ( ! isEnabled || ( isEnabled && ! EnvironmentConfig.isAuthor()) ) {

   LOG.info( "FormSubmitEventListener not enabled {} {}", isEnabled, EnvironmentConfig.isAuthor() );

   return;

  }

   LOG.info( "FormSubmitEventListener activate" );

  ResourceResolver resourceResolver = null;

   try {

  resourceResolver = new ReadServiceImpl().getResourceResolver();  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  Session readSession = resourceResolver.adaptTo( Session.class );

  readSession.getWorkspace().getObservationManager().addEventListener( this, // handler
   (Event.NODE_ADDED | Event.PROPERTY_CHANGED), // binary combination of event types
   "/content/usergenerated/forms", // path
   true, // is Deep?
   null, // uuids filter
   null, // nodetypes filter, allow all nodetypes
   false );

  } catch ( RepositoryException e ) {

   LOG.error( "FormSubmitEventListener did not get registered", e );

   throw new Exception( e );

  }

   finally {

   try {

  resourceResolver.close();

  }

   catch ( Exception e ) {

   LOG.error( "****OCLC**** Error in finally attempting closes "+e.getMessage(), e );

  }

  }

  }

   @Deactivate
   public void deactivate() {

   if ( readSession != null ) {

   readSession.logout();

  }

  }

   @Override
   public void onEvent( EventIterator events ) {

  ResourceResolver resourceResolver = null;

  FormSubmittedDataDaoImpl formSubmittedDataDaoImpl = null;

  FormSubmittedData formSubmittedData = new FormSubmittedData();

   try {

   // THIS WORKS
   DataSource dataSource = (DataSource) dataSourceLookup.getDataSource( FORMSUBMITTEDDATA_DATASOURCE_NAME );

   LOG.info( "dataSourceLookup dataSource in FormSubmitEventListener {}", dataSource);

  resourceResolver = new ReadServiceImpl().getResourceResolver();

  ConvertResourceToJSON jsonConverter = new ConvertResourceToJSON();

  formSubmittedDataDaoImpl = new FormSubmittedDataDaoImpl( dataSource );

  formSubmittedDataDaoImpl.connect();

   while ( events.hasNext() ) {

  Event event = events.nextEvent();

   LOG.info( "FormSubmitEventListener event.getPath : {}", event.getPath() );

   LOG.info( "FormSubmitEventListener event.getType : {}", event.getType() );

  String contentPath = event.getPath();

   // this is temporary during testing, will only use NODE_ADDED
   if ( event.getType() == Event.PROPERTY_CHANGED ) {

   // need to clean up path, remove everything after nodename; example
  // /content/usergenerated/forms/worldwide/en/oclc-rsc/1507296389824_195/jcr:content/participant.name becomes
  // content/usergenerated/forms/worldwide/en/oclc-rsc/1507296389824_195
   int x = contentPath.indexOf( "/jcr:content");

  contentPath = contentPath.substring( 0, x );

  }

  Resource resource = resourceResolver.resolve(contentPath+"/jcr:content");

  JSONObject jsonObject = jsonConverter.resourceToJSON(resource);

  ValueMap properties = resource.adaptTo( ValueMap.class );

  Calendar calendar = properties.get("jcr:created", null) ;

  formSubmittedData.setNodeName( jsonObject.getString("jcr:title") );

  formSubmittedData.setContentPath( contentPath );

  formSubmittedData.setFormId( jsonObject.getString("formIdentifier") );

  formSubmittedData.setFormTitle( jsonObject.getString("formTitle") );

  formSubmittedData.setCreatedDate( new java.sql.Timestamp( calendar.getTimeInMillis() ) );

  formSubmittedData.setJsonContent( jsonObject.toString() );

  formSubmittedDataDaoImpl.addFormSubmittedData( formSubmittedData );

  }

  formSubmittedDataDaoImpl.close();

  }

   catch ( Exception e ) {

   LOG.error( "Error while processing events "+e.getMessage(), e );

  }

   finally {

   try {

  formSubmittedDataDaoImpl.close();

  resourceResolver.close();

  }

   catch ( Exception e ) {

   LOG.error( "Error in finally attempting closes "+e.getMessage(), e );

  }

  }

  }

}

=====================================================================================

ReadServiceImpl seems to never inject the ResourceResolverFactory

NPE on resolverFactory.getServiceResourceResolver(serviceParams)

=====================================================================================

package org.oclc.cq.service;

import org.apache.felix.scr.annotations.Component;

import org.apache.felix.scr.annotations.Reference;

import org.apache.felix.scr.annotations.Service;

import org.apache.sling.api.resource.LoginException;

import org.apache.sling.api.resource.ResourceResolver;

import org.apache.sling.api.resource.ResourceResolverFactory;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import java.util.HashMap;

import java.util.Map;

@Component(metatype = false, immediate = true)

@Service(value = ReadService.class )

public class ReadServiceImpl implements ReadService {

   protected static final Logger LOG = LoggerFactory.getLogger( ReadServiceImpl.class);

   @Reference
   private ResourceResolverFactory resolverFactory;

   /**
  *
  * @return
   */
   public ResourceResolver getResourceResolver() {

  Map<String, Object> serviceParams = new HashMap<>();

  serviceParams.put( ResourceResolverFactory.SUBSERVICE, "readService" );

  ResourceResolver resolver = null;

   try {

   LOG.info("resolverFactory {}", resolverFactory);

  resolver = resolverFactory.getServiceResourceResolver(serviceParams);

   return resolver;

  } catch (LoginException e) {

   LOG.error("getServiceResoureResolver exception", e);

  }

   return null;

  }

}

Avatar

Correct answer by
Employee Advisor

Hi,

resourceResolver = new ReadServiceImpl().getResourceResolver(); 

That's the problem. You are constructing the ReadServiceImpl via Constructor, and in that way SCR cannot inject any references. You should make the ReadServiceImpl a service and let SCR manage its lifecycle.

Jörg