0

We're having problems with duplicate @PostConstruct calls on a base class hierarchy.

Here's the base class first:

public abstract class AbstractManager<T> implements Serializable
{
    private List<T> entities;

    @PostConstruct // when annotated with @PostConstruct this method is called even if overridden in sub class
    public void init()
    {
        System.out.println( AbstractManager.class.getSimpleName() + " @PostConstruct on " + this.getClass().getSimpleName() + "!" );
    }

    protected abstract List<T> getDbEntities();

    public List<T> getEntities()
    {
        if ( this.entities == null )
        {
            this.entities = this.getDbEntities();
        }

        return this.entities;
    }

    public void setEntities( List<T> entities )
    {
        this.entities = entities;
    }

    public void clearEntities()
    {
        this.entities = null;
    }
}

Here's the concrete sub class (notice how init() is overridden to call super.init()):

@Named
@ViewScoped
public class PseudoEntityManager extends AbstractManager<PseudoEntity>
{
    private static final long serialVersionUID = 1L;

    @Override
    @PostConstruct
    public void init()
    {
        super.init();
    }

    ...
}

When some (unshown) page is rendered, the pseudoEntityManager bean is instantiated, however @PostConstruct is called twice. This is the output:

INFO: AbstractManager @PostConstruct on PseudoEntityManager!
INFO: AbstractManager @PostConstruct on PseudoEntityManager!
INFO: New list of pseudo DB entities!

When commenting the overriding init() method in the concrete sub class so that there's only one @PostConstruct method from the super class, the following output is generated:

INFO: AbstractManager @PostConstruct on PseudoEntityManager!
INFO: New list of pseudo DB entities!

Q:

What's the correct behavior according to CDI specification now? (references anybody?)

Notes:

I also found this mailing list conversation while researching:

http://list-archives.org/2012/10/11/cdi-dev-lists-jboss-org/postconstruct-on-inherited-class/f/4426568582

In the conversation, some gurus tend to say "only the @PostConstruct method on the sub class should be called". If you read closely, there's a link to a Weld bug that's said to be resolved since Weld 1.1.5:

https://issues.jboss.org/browse/WELD-1225

Has this really been fixed? According to the output I get, it's not.

Environment: Weld 1.1.8 along with Seam 3 to get the CDI @ViewScoped working correctly (on GlassFish 3.1.2).

Kawu
  • 13,647
  • 34
  • 123
  • 195
  • IMO there's no need to use `@PostConstruct` in a non managed bean. Since your `AbstractManager` class is not a managed bean, then it must not have this `@PostConstruct` method to begin with. Also, there's no `@ViewScoped` for CDI until the new JSF 2.2. If you're using JSF 2.2 then tag the question as such instead of JSF 2 only. – Luiggi Mendoza Jul 16 '13 at 16:50
  • It must initialize something as soon as some concrete sub class bean is instantiated. If the init method isn't overridden, the super class' `@PostConstruct` must be called, because otherwise the integrity of the bean cannot be guaranteed. – Kawu Jul 16 '13 at 16:53
  • About the JSF 2.2 notes: the question was tagged as Seam 3, which replaces the JSF with a CDI `@ViewScoped`. But it is ultimately about CDI/Weld here and why the overridden `@PostConstruct` gets called, even though the Weld bug at https://issues.jboss.org/browse/WELD-1225 is resolved (see first example there - it's just the same). – Kawu Jul 16 '13 at 16:54
  • You can override the `init` method in your subclasses and use `super.init()` as you're currently doing but only decorete the `init` with `@PostConstruct` in your subclasses, not in the super class. – Luiggi Mendoza Jul 16 '13 at 16:55
  • See the comment above. – Kawu Jul 16 '13 at 16:57
  • See exactly what? You already know the problem, and also you should not decorate it in super class (regardless if is a bug or not). – Luiggi Mendoza Jul 16 '13 at 16:58
  • As I already explained in my first comment, the sub classes dont' **have** to override `init()`, that is when they don't, the super class' `@PostConstruct` should (must) be called, because otherwise the super class will stop to function if `init()` isn't called. – Kawu Jul 16 '13 at 17:00

2 Answers2

1

Yes, it's been fixed. Unfortunately, it's only fixed in the Weld 2.0 release line. Sometimes these bugs get back ported, but unfortunately I doubt this one will end up being resolved in the maintenance releases.

John Ament
  • 11,595
  • 1
  • 36
  • 45
  • Note that https://issues.jboss.org/browse/WELD-1225 has been backported to Weld 1.1.14 as discussed in https://community.jboss.org/message/828272 ... – Kawu Apr 11 '14 at 08:52
1

This is a Workaround answer.

With the Bug mentioned both @PostConstruct annotation are considered, and their logic code is executed.

  1. using @Override will execute the post on the superclass which is overriden by the subclass
  2. then the post on the subclass will be executed as well.

to workaround this is possible to do as following:

  • define a new signature and mark it @PostConstruct
  • override the superclass method marked as @PostConstruct, but dont put @PostConstruct Annotation, just the @Override, and implement it empty.

As result you will have only executed the code marked @PostConstruct on your subclass:

@Specializes
@ViewAccessScoped
public class BaseBean extends SubBean {

@PostConstruct
public void postConstructExtension() {
    LOGGER.info("POST CALLED ON SPECIALIZED CLASS" + this.getClass().getSimpleName());
}

@Override
public void postConstruct() {
    LOGGER.info("OVERRIDDEN POST CALLED ON SPECIALIZED CLASS" + this.getClass().getSimpleName());
}

}