1

We run a JAVA web application on a Wildfly 8.0.0 server. We have an email template editor form which uses Mustache Fields to put some variable data into the template. The mail also have an enum field which specifies the type of the template. The entity structure is something like this:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE", discriminatorType = DiscriminatorType.STRING, length = 255)
@DiscriminatorOptions(force = true)
@DiscriminatorValue("MailTemplate")
@Table(name = "MailTemplate")
public abstract class MailTemplate extends MyAbstractEntity // AbstractEntity holds the common id field for all entities
{
   private String body;
   private String subject;
}

public class CustomMailTemplate extends MailTemplate implements java.io.Serializable {
    @Enumerated(EnumType.STRING)
    @Column(name = "templateType")
    private CustomTemplateType templateType;
}

public enum CustomTemplateType implements java.io.Serializable {
    OneTemplateType,
    AnotherTemplateType,
    AndAThirdOneTemplateType
}

We use JPA to persist our entities into an Oracle database. This is the controller structure which handles the various type of our templates:

public abstract class AbstractMailTemplateEditController<T extends MailTemplate, TYPE> {
    @Getter @Setter
    protected T entity;

    @Getter @Setter
    protected String subject;

    @Getter @Setter
    protected String body;

    @Getter @Setter
    protected TYPE templateType;    
}

public class CustomMailTemplateEditController extends AbstractMailTemplateEditController<CustomMailTemplate, CustomTemplateType> implements Serializable {
    // this is in a service which injected, it's just for the code example
    @PersistenceContext
    private EntityManager em;

    public void saveTemplate(){
        // let's assume now that our entity property exists
        entity.setBody(body);
        entity.setSubject(subject);
        entity.setTemplateType(templateType); // SOMETHING WRONG HAPPENS HERE!
        em.merge(entity);
    }
}

And here's the very strange part:

after a server restart, we can save our templates without any problem. After a 'T' time we can't save the templates, and we get the following error message:

java.lang.String cannot be cast to CustomTemplateType

I started to debug this and in the entity.setTemplateType(templateType); line I saw that templateType is a String, but this only happens after some time after the server restart. After a restart in the same place the templateType is a CustomTemplateType and I can save the template. How can it happen and what can I do to fix this?

Update: Based on Tobias Liefke's answer I checked how we use this controller in the view and I found the following:

<h:selectOneMenu value="#{bean.templateType}" styleClass="form-control" id="template-type" >
    <f:selectItems value="#{bean.mailTemplateTypes}" var="item" itemLabel="#{msg[item]}" itemValue="#{item}" />
    <f:selectItem itemLabel="Special template" itemValue="#{null}" />
</h:selectOneMenu>

The #{bean.mailTemplateTypes} is a list of enums. So could it be a JSF bug? I mean after a while (days, weeks without server restart) JSF starts to pass the selected mailTemplateType as a String into the #{bean.templateType} value. Is it possible? I checked this Question: How to use enum values in f:selectItem(s) and based on BalusC's answer JSF has a builtin converter for enum. But that's the only part where we set the templateType variable.

Community
  • 1
  • 1
maestro
  • 671
  • 1
  • 13
  • 27
  • Stupid test: what if you get rid of the lombok annotations and just use manually created getter/setters ? – Gimby Oct 07 '15 at 16:19
  • I give it a try but can't see the results immediately because of the fact that the bug depends on server running time... but it would be strange if it worked because I access to the `templateType` field directly, I not use the getter-setter methods. – maestro Oct 07 '15 at 16:48
  • I'm not able to locate in your saveTemplate method where the templateType variable is being declared or initialized. That's why I'm unable to determine the possible cause of why it becomes a String type at a certain time, instead of enum type. – Ish Oct 07 '15 at 20:48
  • *"but it would be strange if it worked because I access to the templateType field directly, I not use the getter-setter methods"* I'm not so much worried about how you access the properties, but about how Hibernate does it. And Hibernate works through proxying, which might not be entirely working correctly when lombok is involved. But that's me making wild guesses. – Gimby Oct 08 '15 at 07:54
  • @Gimby: I eliminated lombok by implementig getter and setter manually, now its working but I need some time because of the server to make sure that it could be a solution. – maestro Oct 08 '15 at 09:10
  • and reading this: I actually have no clue why this would make any difference since lombok apparently works at compile time, not runtime! https://groups.google.com/forum/#!topic/project-lombok/Qaalzr5mKW4 – Gimby Oct 08 '15 at 09:29
  • Agree. I left lombok in another child template but that is working also. So I guess that's not the tricky part. Now I'm waiting for the error to see what happens on a save. – maestro Oct 08 '15 at 09:41
  • I'm curious about that open-ended generic type TYPE - it allows everything through, while your entity seems to dictate it must be some sort of CustomTemplateType. So I'd expect the generic declaration to be – Gimby Oct 08 '15 at 11:20
  • Generic parameter `TYPE` is holding the place for an enum so I can't do any restriction on that parameter. – maestro Oct 08 '15 at 16:01
  • For sure you can, see my answer. – Tobias Liefke Oct 10 '15 at 13:26

1 Answers1

1

The problem is the unbound <TYPE>, which will be compiled to protected Object templateType; and public void setTemplateType(Object templateType)

And someone in your application is calling setTemplateType() with a String. This is possible if he either uses reflection (as Mustache Fields will do) or if he uses an unbound AbstractMailTemplateEditController.

If all subclasses of AbstractMailTemplateEditController use an enum for TYPE, you can bind it to Enum:

public abstract class AbstractMailTemplateEditController<T extends MailTemplate, TYPE extends Enum<TYPE>> {

This will lead to public void setTemplateType(Enum<?> templateType) in the class file - which will throw a ClassCastException as soon as someone is calling it with a String.

Otherwise you could override setTemplateType and CustomMailTemplateEditController and make an explicit type check:

public class CustomMailTemplateEditController ... {
    @Override 
    public void setTemplateType(CustomTemplateType templateType) {
        super.setTemplateType(templateType);
    }
}
Tobias Liefke
  • 8,637
  • 2
  • 41
  • 58