4

In order to do custom actions on committing a deploy action, I would like to override the com.tridion.persistence.JPADAOFactory class, which is the default storage class when using a database to publish stuff into and is configurable in cd_storage_conf.xml.

However, I found that this is not so easy to do, as an overridden class does not get loaded through the Spring bean mechanism, cause the JPADAOFactory.configureBundle(Configuration storageBundleConfiguration) method to fail as an overridden class has no access to Spring's application context - the method gives a NullPointerException on this variable.

I could solve this by overriding this method completely and load the Storage DAOs in my own way, but before I do that, I have a couple of questions:

  1. Is it possible or recommended at all to override the JPADAOFactory class?
  2. If so, what is the best way to do it?
  3. Does anyone see any issues with me loading the DAO bundles in the same way as for instance the FSDAOFactory class does?

2 Answers 2

5

It is not possible to achieve what you wish to do in a supported way because that class was not build to be public extension point. However, if you really want to extend it and keep all the functionality inherited from the parent class then you need to do quite a few things:

  1. Create a class that extends JPADAOFactory in a package under com.tridion.storage;
  2. Annotate it with @Component(...) and @Scope("singleton");
  3. Make sure it implements ApplicationContextAware;
  4. Make sure you force the setting of the private field (applicationContext) which you inherit from JPADAOFactory;
  5. Override the methods you're interested in;
  6. Configure it in cd_storage_conf.xml.

Keep in mind that this extension is not supported by Tridion, is a bit hackish and it's not the only (hackish) way to do it.

Next, I will include some sample code to exemplify what I just described. Again, this is just an example (not beautiful code, not thread safe, no errors handled):

@Component("JPADAOExtensionFactory")
@Scope("singleton")
public class JPADAOExtensionFactory extends JPADAOFactory 
                                    implements ApplicationContextAware {

    /**
     * Static variable which will be set by the Singleton instance managed by Spring 
     * and used by the instance created through Reflection by Tridion code during 
     * initialization.
     */
    private static ApplicationContext APPLICATION_CONTEXT;

    /**
     * Constructor called by Spring when init is done.
     */
    public JPADAOExtensionFactory() {
        //not important what we send. This instance is never going to be used
        super(null, "MSSQL");
    }

    /**
     * Constructor called by Tridion at app start-up (after the Spring 
     * init has happened).
     */
    public JPADAOExtensionFactory(String storageId, String dialect) {
        super(storageId, dialect);
    }

    /**
     * The method we actually want to override.
     */
    public void commitTransaction(String transactionId) throws StorageException {
        super.commitTransaction(transactionId);
        // TODO: do my stuff
    }

    /** 
     * Override this method so we can set the application context on the 
     * private field we inherit from JPADAOFactory. 
     */
    public void configureBundle(Configuration storageDAOBundleConfiguration) 
                                              throws ConfigurationException {
        // first set the right value for the private field 
        // called 'applicationContext'
        try {
            setPrivateField(this, "applicationContext", APPLICATION_CONTEXT);
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
        }

        // configure the bundle like we normally do
        super.configureBundle(storageDAOBundleConfiguration);
    }

    /**
     * Method called by Spring.
     * @see ApplicationContextAware#setApplicationContext(ApplicationContext) 
     */
    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) 
                                                           throws BeansException {
        APPLICATION_CONTEXT = applicationContext;
    }

    /**
     * Utility method using Reflection to be able to set the private field 
     * we inherit from our parent class.
     */
    private static void setPrivateField(final Object fieldOwner, 
                                        final String fieldName, final Object value) 
                              throws NoSuchFieldException, IllegalAccessException {
        final Field privateField = getPrivateFieldRec(fieldOwner.getClass(), 
                                                      fieldName);

        if (privateField != null) {
            final boolean accesible = privateField.isAccessible();
            privateField.setAccessible(true);

            privateField.set(fieldOwner, value);

            privateField.setAccessible(accesible);
        }
    }

    private static Field getPrivateFieldRec(final Class<?> clazz, 
                                            final String fieldName) {
        for (Field field : clazz.getDeclaredFields()) {
            if (fieldName.equals(field.getName())) {
                return field;
            }
        }
        final Class<?> superClazz = clazz.getSuperclass();

        if (superClazz != null) {
            return getPrivateFieldRec(superClazz, fieldName);
        }

        return null;
    }
}

Hope this helps.

1
  • This works indeed. :)
    – Raimond
    Commented Apr 17, 2013 at 0:09
1

Below are the answers to your questions:

  1. Yes you can override the JPADAOFactory class and is recommended whenever you want to override the default storage of DCP or likes
  2. The best way to do is the use of a storage extension
  3. Yes, I have seen issue while overriding JPADAOFactory class in Storage Extension and this is related to way you are creating your Storage Extension JAR file

Below are few details about how you can do that and specific errors like "No Beans Loaded" and their possible resolutions:

Tridion Forum (Need Login) - Defines step-by-step process to achieve it for Dynamic Component Presentation

StackOverflow

2
  • Thanks Pankaj, but I actually want to override the Factory class itself, which is slightly different than just overriding a DAO.
    – Raimond
    Commented Apr 16, 2013 at 9:00
  • In that case I am not sure if it is possible but I would not recommend it and instead I would recommend overriding the individual specific instances of the factory, as I am slightly not in favour changing the default behaviour and instead in favour of overriding the specific instances. Commented Apr 16, 2013 at 9:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.