This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Scala EJB without interface doesn't seem to work

15 replies
Ant Kutschera
Joined: 2012-01-28,
User offline. Last seen 42 years 45 weeks ago.

I'm attempting to deploy an EJB written in Scala to an app server.

If I create a stateless session bean and give it a (remote) interface,
then it works fine (the interface is a trait).

The problem is when I want to create a stateless session bean with NO
interface. On both Glassfish and JBoss I get an error. If I convert
the code to Java, then I have no problem.

Glassfish 3.1.1: IllegalArgumentException: Can not set
ejb.MyScalaEJB2Bean field
ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
$Proxy290

JBoss 7.0.2.Final: IllegalArgumentException: Can not set
ejb.MyScalaEJB2Bean field
ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
scala.ScalaObject$$$view4

The EJB source is:

package ch.maxant.produkte.ejb.scala

@Stateless(name = "ScalaTestBean2", mappedName = "ScalaTestBean2")
class MyScalaEJB2Bean {

def doStuff(x: String): String = {
"hello " + x + ", this is a scala EJB"
}

}

The Servlet which needs the EJB injecting has this source:

package ch.maxant.produkte.web.scala;

@WebServlet(urlPatterns = Array("/BhfSuche2"))
@SerialVersionUID(1L)
class BhfSuche2 extends HttpServlet {

@EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
var scalaEjb2NoInterface: MyScalaEJB2Bean = null

@throws(classOf[ServletException])
@throws(classOf[IOException])
override def doGet(request: HttpServletRequest, response:
HttpServletResponse):Unit = {

val msg2 = scalaEjb2NoInterface.doStuff("ScalaWebServlet")
response.getWriter.write(msg2 + "\r")
}
}

Why am I getting these errors and is there anything I can do about it?

Cheers,
Ant

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: Scala EJB without interface doesn't seem to work

I think you need to annotate the ejb property with @BeanProperty as
well.

@BeanProperty
@EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
var scalaEjb2NoInterface: MyScalaEJB2Bean = _

to generate to right EJB getter/setter format getScalaEjb2NoInterface
and setScalaEjb2NoInterface

On 2 feb, 13:45, Ant Kutschera wrote:
> I'm attempting to deploy an EJB written in Scala to an app server.
>
> If I create a stateless session bean and give it a (remote) interface,
> then it works fine (the interface is a trait).
>
> The problem is when I want to create a stateless session bean with NO
> interface. On both Glassfish and JBoss I get an error. If I convert
> the code to Java, then I have no problem.
>
> Glassfish 3.1.1: IllegalArgumentException: Can not set
> ejb.MyScalaEJB2Bean field
> ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> $Proxy290
>
> JBoss 7.0.2.Final: IllegalArgumentException: Can not set
> ejb.MyScalaEJB2Bean field
> ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> scala.ScalaObject$$$view4
>
> The EJB source is:
>
> package ch.maxant.produkte.ejb.scala
>
> @Stateless(name = "ScalaTestBean2", mappedName = "ScalaTestBean2")
> class MyScalaEJB2Bean {
>
>   def doStuff(x: String): String = {
>     "hello " + x + ", this is a scala EJB"
>   }
>
> }
>
> The Servlet which needs the EJB injecting has this source:
>
> package ch.maxant.produkte.web.scala;
>
> @WebServlet(urlPatterns = Array("/BhfSuche2"))
> @SerialVersionUID(1L)
> class BhfSuche2 extends HttpServlet {
>
>   @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
>   var scalaEjb2NoInterface: MyScalaEJB2Bean = null
>
>   @throws(classOf[ServletException])
>   @throws(classOf[IOException])
>   override def doGet(request: HttpServletRequest, response:
> HttpServletResponse):Unit = {
>
>     val msg2 = scalaEjb2NoInterface.doStuff("ScalaWebServlet")
>     response.getWriter.write(msg2 + "\r")
>   }
>
> }
>
> Why am I getting these errors and is there anything I can do about it?
>
> Cheers,
> Ant

Ant Kutschera
Joined: 2012-01-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Scala EJB without interface doesn't seem to work

Perhaps, I'll give it a whirl. Although the container should be
setting the field, rather than using an accessor, since the EJB
annotation is on the field.

On Feb 2, 3:21 pm, Dave wrote:
> I think you need to annotate the ejb property with @BeanProperty as
> well.
>
> @BeanProperty
> @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> var scalaEjb2NoInterface: MyScalaEJB2Bean = _
>
> to generate to right EJB getter/setter format getScalaEjb2NoInterface
> and setScalaEjb2NoInterface
>
> On 2 feb, 13:45, Ant Kutschera wrote:
>
>
>
>
>
>
>
> > I'm attempting to deploy an EJB written in Scala to an app server.
>
> > If I create a stateless session bean and give it a (remote) interface,
> > then it works fine (the interface is a trait).
>
> > The problem is when I want to create a stateless session bean with NO
> > interface. On both Glassfish and JBoss I get an error. If I convert
> > the code to Java, then I have no problem.
>
> > Glassfish 3.1.1: IllegalArgumentException: Can not set
> > ejb.MyScalaEJB2Bean field
> > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > $Proxy290
>
> > JBoss 7.0.2.Final: IllegalArgumentException: Can not set
> > ejb.MyScalaEJB2Bean field
> > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > scala.ScalaObject$$$view4
>
> > The EJB source is:
>
> > package ch.maxant.produkte.ejb.scala
>
> > @Stateless(name = "ScalaTestBean2", mappedName = "ScalaTestBean2")
> > class MyScalaEJB2Bean {
>
> >   def doStuff(x: String): String = {
> >     "hello " + x + ", this is a scala EJB"
> >   }
>
> > }
>
> > The Servlet which needs the EJB injecting has this source:
>
> > package ch.maxant.produkte.web.scala;
>
> > @WebServlet(urlPatterns = Array("/BhfSuche2"))
> > @SerialVersionUID(1L)
> > class BhfSuche2 extends HttpServlet {
>
> >   @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> >   var scalaEjb2NoInterface: MyScalaEJB2Bean = null
>
> >   @throws(classOf[ServletException])
> >   @throws(classOf[IOException])
> >   override def doGet(request: HttpServletRequest, response:
> > HttpServletResponse):Unit = {
>
> >     val msg2 = scalaEjb2NoInterface.doStuff("ScalaWebServlet")
> >     response.getWriter.write(msg2 + "\r")
> >   }
>
> > }
>
> > Why am I getting these errors and is there anything I can do about it?
>
> > Cheers,
> > Ant

Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
Re: Re: Scala EJB without interface doesn't seem to work

Scala does not have fields.  Members are all methods.   You can annotate your annotations with @annotation.target.field and it will be placed on the (inaccessible from scala) field that the compiler generates.

On Feb 2, 2012 10:00 AM, "Ant Kutschera" <ant.kutschera@gmail.com> wrote:
Perhaps, I'll give it a whirl.  Although the container should be
setting the field, rather than using an accessor, since the EJB
annotation is on the field.


On Feb 2, 3:21 pm, Dave <dave.mahabiers...@hotmail.com> wrote:
> I think you need to annotate the ejb property with @BeanProperty as
> well.
>
> @BeanProperty
> @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> var scalaEjb2NoInterface: MyScalaEJB2Bean = _
>
> to generate to right EJB getter/setter format getScalaEjb2NoInterface
> and setScalaEjb2NoInterface
>
> On 2 feb, 13:45, Ant Kutschera <ant.kutsch...@gmail.com> wrote:
>
>
>
>
>
>
>
> > I'm attempting to deploy an EJB written in Scala to an app server.
>
> > If I create a stateless session bean and give it a (remote) interface,
> > then it works fine (the interface is a trait).
>
> > The problem is when I want to create a stateless session bean with NO
> > interface. On both Glassfish and JBoss I get an error. If I convert
> > the code to Java, then I have no problem.
>
> > Glassfish 3.1.1: IllegalArgumentException: Can not set
> > ejb.MyScalaEJB2Bean field
> > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > $Proxy290
>
> > JBoss 7.0.2.Final: IllegalArgumentException: Can not set
> > ejb.MyScalaEJB2Bean field
> > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > scala.ScalaObject$$$view4
>
> > The EJB source is:
>
> > package ch.maxant.produkte.ejb.scala
>
> > @Stateless(name = "ScalaTestBean2", mappedName = "ScalaTestBean2")
> > class MyScalaEJB2Bean {
>
> >   def doStuff(x: String): String = {
> >     "hello " + x + ", this is a scala EJB"
> >   }
>
> > }
>
> > The Servlet which needs the EJB injecting has this source:
>
> > package ch.maxant.produkte.web.scala;
>
> > @WebServlet(urlPatterns = Array("/BhfSuche2"))
> > @SerialVersionUID(1L)
> > class BhfSuche2 extends HttpServlet {
>
> >   @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> >   var scalaEjb2NoInterface: MyScalaEJB2Bean = null
>
> >   @throws(classOf[ServletException])
> >   @throws(classOf[IOException])
> >   override def doGet(request: HttpServletRequest, response:
> > HttpServletResponse):Unit = {
>
> >     val msg2 = scalaEjb2NoInterface.doStuff("ScalaWebServlet")
> >     response.getWriter.write(msg2 + "\r")
> >   }
>
> > }
>
> > Why am I getting these errors and is there anything I can do about it?
>
> > Cheers,
> > Ant
Ant Kutschera
Joined: 2012-01-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Scala EJB without interface doesn't seem to work

Hi,

That isn't correct. Here is the *decompiled* Scala (ie, from the
class file which the Scala compiler generated):

@EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
private MyScalaEJB2Bean scalaEjb2NoInterface;

I got that out of: http://java.decompiler.free.fr/?q=jdgui

So there is indeed a field.

Also, don't forget, this works when the the EJB has a local or remote
interface, and I inject the interface. It doesn't work, when I remove
the interface. According to the EJB 3.1 specs, and my own tests in
Java, this should work fine without an interface.

The problem is the bean which the container is instantiating. It
seems to have the wrong type. I assume that if there is an interface,
the container creates a standard Java proxy using that interface, and
passes calls to the interface through to the implementation. When
there is no interface, it cannot create a normal proxy and has to
manipulate the byte code. Something in the bytecode generated by
Scala is causing the container to have problems.

I just can't figure out what the problem is, nor how to solve it.

Cheers,
Ant

On Feb 2, 8:53 pm, Josh Suereth wrote:
> Scala does not have fields.  Members are all methods.   You can annotate
> your annotations with @annotation.target.field and it will be placed on the
> (inaccessible from scala) field that the compiler generates.
> On Feb 2, 2012 10:00 AM, "Ant Kutschera" wrote:
>
>
>
>
>
>
>
> > Perhaps, I'll give it a whirl.  Although the container should be
> > setting the field, rather than using an accessor, since the EJB
> > annotation is on the field.
>
> > On Feb 2, 3:21 pm, Dave wrote:
> > > I think you need to annotate the ejb property with @BeanProperty as
> > > well.
>
> > > @BeanProperty
> > > @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> > > var scalaEjb2NoInterface: MyScalaEJB2Bean = _
>
> > > to generate to right EJB getter/setter format getScalaEjb2NoInterface
> > > and setScalaEjb2NoInterface
>
> > > On 2 feb, 13:45, Ant Kutschera wrote:
>
> > > > I'm attempting to deploy an EJB written in Scala to an app server.
>
> > > > If I create a stateless session bean and give it a (remote) interface,
> > > > then it works fine (the interface is a trait).
>
> > > > The problem is when I want to create a stateless session bean with NO
> > > > interface. On both Glassfish and JBoss I get an error. If I convert
> > > > the code to Java, then I have no problem.
>
> > > > Glassfish 3.1.1: IllegalArgumentException: Can not set
> > > > ejb.MyScalaEJB2Bean field
> > > > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > > > $Proxy290
>
> > > > JBoss 7.0.2.Final: IllegalArgumentException: Can not set
> > > > ejb.MyScalaEJB2Bean field
> > > > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > > > scala.ScalaObject$$$view4
>
> > > > The EJB source is:
>
> > > > package ch.maxant.produkte.ejb.scala
>
> > > > @Stateless(name = "ScalaTestBean2", mappedName = "ScalaTestBean2")
> > > > class MyScalaEJB2Bean {
>
> > > >   def doStuff(x: String): String = {
> > > >     "hello " + x + ", this is a scala EJB"
> > > >   }
>
> > > > }
>
> > > > The Servlet which needs the EJB injecting has this source:
>
> > > > package ch.maxant.produkte.web.scala;
>
> > > > @WebServlet(urlPatterns = Array("/BhfSuche2"))
> > > > @SerialVersionUID(1L)
> > > > class BhfSuche2 extends HttpServlet {
>
> > > >   @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> > > >   var scalaEjb2NoInterface: MyScalaEJB2Bean = null
>
> > > >   @throws(classOf[ServletException])
> > > >   @throws(classOf[IOException])
> > > >   override def doGet(request: HttpServletRequest, response:
> > > > HttpServletResponse):Unit = {
>
> > > >     val msg2 = scalaEjb2NoInterface.doStuff("ScalaWebServlet")
> > > >     response.getWriter.write(msg2 + "\r")
> > > >   }
>
> > > > }
>
> > > > Why am I getting these errors and is there anything I can do about it?
>
> > > > Cheers,
> > > > Ant

Ant Kutschera
Joined: 2012-01-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Scala EJB without interface doesn't seem to work

Hi Dave,

It didn't make a difference, and that makes sense, since the
decompiled code shows that its the field which has the annotation. It
should be safe to assume that because the annotation is on the field,
any generated methods are entirely ignored by the container.

On Feb 2, 3:21 pm, Dave wrote:
> I think you need to annotate the ejb property with @BeanProperty as
> well.
>
> @BeanProperty
> @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> var scalaEjb2NoInterface: MyScalaEJB2Bean = _
>
> to generate to right EJB getter/setter format getScalaEjb2NoInterface
> and setScalaEjb2NoInterface
>
> On 2 feb, 13:45, Ant Kutschera wrote:
>
>
>
>
>
>
>
> > I'm attempting to deploy an EJB written in Scala to an app server.
>
> > If I create a stateless session bean and give it a (remote) interface,
> > then it works fine (the interface is a trait).
>
> > The problem is when I want to create a stateless session bean with NO
> > interface. On both Glassfish and JBoss I get an error. If I convert
> > the code to Java, then I have no problem.
>
> > Glassfish 3.1.1: IllegalArgumentException: Can not set
> > ejb.MyScalaEJB2Bean field
> > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > $Proxy290
>
> > JBoss 7.0.2.Final: IllegalArgumentException: Can not set
> > ejb.MyScalaEJB2Bean field
> > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > scala.ScalaObject$$$view4
>
> > The EJB source is:
>
> > package ch.maxant.produkte.ejb.scala
>
> > @Stateless(name = "ScalaTestBean2", mappedName = "ScalaTestBean2")
> > class MyScalaEJB2Bean {
>
> >   def doStuff(x: String): String = {
> >     "hello " + x + ", this is a scala EJB"
> >   }
>
> > }
>
> > The Servlet which needs the EJB injecting has this source:
>
> > package ch.maxant.produkte.web.scala;
>
> > @WebServlet(urlPatterns = Array("/BhfSuche2"))
> > @SerialVersionUID(1L)
> > class BhfSuche2 extends HttpServlet {
>
> >   @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> >   var scalaEjb2NoInterface: MyScalaEJB2Bean = null
>
> >   @throws(classOf[ServletException])
> >   @throws(classOf[IOException])
> >   override def doGet(request: HttpServletRequest, response:
> > HttpServletResponse):Unit = {
>
> >     val msg2 = scalaEjb2NoInterface.doStuff("ScalaWebServlet")
> >     response.getWriter.write(msg2 + "\r")
> >   }
>
> > }
>
> > Why am I getting these errors and is there anything I can do about it?
>
> > Cheers,
> > Ant

Ant Kutschera
Joined: 2012-01-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Scala EJB without interface doesn't seem to work

Fixed it!

The bean without an interface needs this annotation too:

@LocalBean

After adding that to the bean implementation class, just after
@Stateless, everything works fine.

:-)

Thanks for the help,
Ant

On Feb 2, 9:58 pm, Ant Kutschera wrote:
> Hi Dave,
>
> It didn't make a difference, and that makes sense, since the
> decompiled code shows that its the field which has the annotation.  It
> should be safe to assume that because the annotation is on the field,
> any generated methods are entirely ignored by the container.
>
> On Feb 2, 3:21 pm, Dave wrote:
>
>
>
>
>
>
>
> > I think you need to annotate the ejb property with @BeanProperty as
> > well.
>
> > @BeanProperty
> > @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> > var scalaEjb2NoInterface: MyScalaEJB2Bean = _
>
> > to generate to right EJB getter/setter format getScalaEjb2NoInterface
> > and setScalaEjb2NoInterface
>
> > On 2 feb, 13:45, Ant Kutschera wrote:
>
> > > I'm attempting to deploy an EJB written in Scala to an app server.
>
> > > If I create a stateless session bean and give it a (remote) interface,
> > > then it works fine (the interface is a trait).
>
> > > The problem is when I want to create a stateless session bean with NO
> > > interface. On both Glassfish and JBoss I get an error. If I convert
> > > the code to Java, then I have no problem.
>
> > > Glassfish 3.1.1: IllegalArgumentException: Can not set
> > > ejb.MyScalaEJB2Bean field
> > > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > > $Proxy290
>
> > > JBoss 7.0.2.Final: IllegalArgumentException: Can not set
> > > ejb.MyScalaEJB2Bean field
> > > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > > scala.ScalaObject$$$view4
>
> > > The EJB source is:
>
> > > package ch.maxant.produkte.ejb.scala
>
> > > @Stateless(name = "ScalaTestBean2", mappedName = "ScalaTestBean2")
> > > class MyScalaEJB2Bean {
>
> > >   def doStuff(x: String): String = {
> > >     "hello " + x + ", this is a scala EJB"
> > >   }
>
> > > }
>
> > > The Servlet which needs the EJB injecting has this source:
>
> > > package ch.maxant.produkte.web.scala;
>
> > > @WebServlet(urlPatterns = Array("/BhfSuche2"))
> > > @SerialVersionUID(1L)
> > > class BhfSuche2 extends HttpServlet {
>
> > >   @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> > >   var scalaEjb2NoInterface: MyScalaEJB2Bean = null
>
> > >   @throws(classOf[ServletException])
> > >   @throws(classOf[IOException])
> > >   override def doGet(request: HttpServletRequest, response:
> > > HttpServletResponse):Unit = {
>
> > >     val msg2 = scalaEjb2NoInterface.doStuff("ScalaWebServlet")
> > >     response.getWriter.write(msg2 + "\r")
> > >   }
>
> > > }
>
> > > Why am I getting these errors and is there anything I can do about it?
>
> > > Cheers,
> > > Ant

Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
Re: Re: Scala EJB without interface doesn't seem to work
The "field" and field name are considered "private implementation details of the compiler". The "var" keyword creates a field (the name of which is an implementation detail), and a pair of methods:  "name" and "name_$eq" which are the getter/setter.  
 IF you place the annotation on a var, it will end up on both the field + the methods for convenience. This could be hurting your bytecode rewriting since the annotation shows up two places.  I know from experience using EJB3 and hibernate.   You need to ensure that your annotation only lands in the desired location, using one of annotation.target.{field,setter,getter,beanGetter,beanSetter} 
What you probably want to do is:
type EjbOnField = (EJB @annotation.target.field)@EjbOnField(...)private var scalaEjb2NoInterface: MyScalaEJB2Bean = null
There's also the potential that the EJB bytecode rewriters are getting hung up on some scala classfiles.   That's a very real possibility, and something we try to prevent from happening as often as possible, but some tools are not as flexible as the JVM itself, especially with rewriting.
I've done a decent amount of Scala with EJB.  As with most Scala-JavaFramework integrations, you need to limit the amount of advanced features you use at that boundary and things are good.   This may be such a case.
If you can isolate what bytecode may be causing the problem, in a reproducible way, it's possible we can fix it.  I'd try the annotation fix first.

On Thu, Feb 2, 2012 at 3:56 PM, Ant Kutschera <ant.kutschera@gmail.com> wrote:
Hi,

That isn't correct.  Here is the *decompiled* Scala (ie, from the
class file which the Scala compiler generated):

 @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
 private MyScalaEJB2Bean scalaEjb2NoInterface;

I got that out of: http://java.decompiler.free.fr/?q=jdgui

So there is indeed a field.

Also, don't forget, this works when the the EJB has a local or remote
interface, and I inject the interface.  It doesn't work, when I remove
the interface.  According to the EJB 3.1 specs, and my own tests in
Java, this should work fine without an interface.

The problem is the bean which the container is instantiating.  It
seems to have the wrong type.  I assume that if there is an interface,
the container creates a standard Java proxy using that interface, and
passes calls to the interface through to the implementation.  When
there is no interface, it cannot create a normal proxy and has to
manipulate the byte code.  Something in the bytecode generated by
Scala is causing the container to have problems.

I just can't figure out what the problem is, nor how to solve it.

Cheers,
Ant


On Feb 2, 8:53 pm, Josh Suereth <joshua.suer...@gmail.com> wrote:
> Scala does not have fields.  Members are all methods.   You can annotate
> your annotations with @annotation.target.field and it will be placed on the
> (inaccessible from scala) field that the compiler generates.
> On Feb 2, 2012 10:00 AM, "Ant Kutschera" <ant.kutsch...@gmail.com> wrote:
>
>
>
>
>
>
>
> > Perhaps, I'll give it a whirl.  Although the container should be
> > setting the field, rather than using an accessor, since the EJB
> > annotation is on the field.
>
> > On Feb 2, 3:21 pm, Dave <dave.mahabiers...@hotmail.com> wrote:
> > > I think you need to annotate the ejb property with @BeanProperty as
> > > well.
>
> > > @BeanProperty
> > > @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> > > var scalaEjb2NoInterface: MyScalaEJB2Bean = _
>
> > > to generate to right EJB getter/setter format getScalaEjb2NoInterface
> > > and setScalaEjb2NoInterface
>
> > > On 2 feb, 13:45, Ant Kutschera <ant.kutsch...@gmail.com> wrote:
>
> > > > I'm attempting to deploy an EJB written in Scala to an app server.
>
> > > > If I create a stateless session bean and give it a (remote) interface,
> > > > then it works fine (the interface is a trait).
>
> > > > The problem is when I want to create a stateless session bean with NO
> > > > interface. On both Glassfish and JBoss I get an error. If I convert
> > > > the code to Java, then I have no problem.
>
> > > > Glassfish 3.1.1: IllegalArgumentException: Can not set
> > > > ejb.MyScalaEJB2Bean field
> > > > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > > > $Proxy290
>
> > > > JBoss 7.0.2.Final: IllegalArgumentException: Can not set
> > > > ejb.MyScalaEJB2Bean field
> > > > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > > > scala.ScalaObject$$$view4
>
> > > > The EJB source is:
>
> > > > package ch.maxant.produkte.ejb.scala
>
> > > > @Stateless(name = "ScalaTestBean2", mappedName = "ScalaTestBean2")
> > > > class MyScalaEJB2Bean {
>
> > > >   def doStuff(x: String): String = {
> > > >     "hello " + x + ", this is a scala EJB"
> > > >   }
>
> > > > }
>
> > > > The Servlet which needs the EJB injecting has this source:
>
> > > > package ch.maxant.produkte.web.scala;
>
> > > > @WebServlet(urlPatterns = Array("/BhfSuche2"))
> > > > @SerialVersionUID(1L)
> > > > class BhfSuche2 extends HttpServlet {
>
> > > >   @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> > > >   var scalaEjb2NoInterface: MyScalaEJB2Bean = null
>
> > > >   @throws(classOf[ServletException])
> > > >   @throws(classOf[IOException])
> > > >   override def doGet(request: HttpServletRequest, response:
> > > > HttpServletResponse):Unit = {
>
> > > >     val msg2 = scalaEjb2NoInterface.doStuff("ScalaWebServlet")
> > > >     response.getWriter.write(msg2 + "\r")
> > > >   }
>
> > > > }
>
> > > > Why am I getting these errors and is there anything I can do about it?
>
> > > > Cheers,
> > > > Ant

Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
Re: Re: Scala EJB without interface doesn't seem to work
This wasn't guaranteed, at least in the containers I was using a few years back.  It's better to be safe and ensure the annotation is *only* on the field.
- Josh

On Thu, Feb 2, 2012 at 3:58 PM, Ant Kutschera <ant.kutschera@gmail.com> wrote:
Hi Dave,

It didn't make a difference, and that makes sense, since the
decompiled code shows that its the field which has the annotation.  It
should be safe to assume that because the annotation is on the field,
any generated methods are entirely ignored by the container.

On Feb 2, 3:21 pm, Dave <dave.mahabiers...@hotmail.com> wrote:
> I think you need to annotate the ejb property with @BeanProperty as
> well.
>
> @BeanProperty
> @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> var scalaEjb2NoInterface: MyScalaEJB2Bean = _
>
> to generate to right EJB getter/setter format getScalaEjb2NoInterface
> and setScalaEjb2NoInterface
>
> On 2 feb, 13:45, Ant Kutschera <ant.kutsch...@gmail.com> wrote:
>
>
>
>
>
>
>
> > I'm attempting to deploy an EJB written in Scala to an app server.
>
> > If I create a stateless session bean and give it a (remote) interface,
> > then it works fine (the interface is a trait).
>
> > The problem is when I want to create a stateless session bean with NO
> > interface. On both Glassfish and JBoss I get an error. If I convert
> > the code to Java, then I have no problem.
>
> > Glassfish 3.1.1: IllegalArgumentException: Can not set
> > ejb.MyScalaEJB2Bean field
> > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > $Proxy290
>
> > JBoss 7.0.2.Final: IllegalArgumentException: Can not set
> > ejb.MyScalaEJB2Bean field
> > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > scala.ScalaObject$$$view4
>
> > The EJB source is:
>
> > package ch.maxant.produkte.ejb.scala
>
> > @Stateless(name = "ScalaTestBean2", mappedName = "ScalaTestBean2")
> > class MyScalaEJB2Bean {
>
> >   def doStuff(x: String): String = {
> >     "hello " + x + ", this is a scala EJB"
> >   }
>
> > }
>
> > The Servlet which needs the EJB injecting has this source:
>
> > package ch.maxant.produkte.web.scala;
>
> > @WebServlet(urlPatterns = Array("/BhfSuche2"))
> > @SerialVersionUID(1L)
> > class BhfSuche2 extends HttpServlet {
>
> >   @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> >   var scalaEjb2NoInterface: MyScalaEJB2Bean = null
>
> >   @throws(classOf[ServletException])
> >   @throws(classOf[IOException])
> >   override def doGet(request: HttpServletRequest, response:
> > HttpServletResponse):Unit = {
>
> >     val msg2 = scalaEjb2NoInterface.doStuff("ScalaWebServlet")
> >     response.getWriter.write(msg2 + "\r")
> >   }
>
> > }
>
> > Why am I getting these errors and is there anything I can do about it?
>
> > Cheers,
> > Ant

Ant Kutschera
Joined: 2012-01-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Scala EJB without interface doesn't seem to work

Hi Josh,

Those are good tips.

According to the API docs: http://www.scala-lang.org/api/current/scala/annotation/target/package.html
- "Annotations on fields by default only end up on the field."
So, if my current solution stops working, I'd be in the right to
report it as a bug.

But another good reason to put it on a setter method is that it makes
the class using the EJB easier to test - you can stick a mock/stub
into an instance of the class by calling the setter rather than using
reflection to setup the field.

So, to annotate the setter, I have two options.

a) Write my own setter and annotate that. This works fine, but means
I have to write the setter, when Scala can generate it for me.

b) Get Scala to generate the setter. To make that work, I have this
code:

type EjbOnMethod=(EJB @scala.annotation.target.beanSetter)

@EjbOnMethod
@BeanProperty
@EJB
var scalaEjb2NoInterface: ejb.MyScalaEJB2Bean = null

Assuming the type goes into say a library or helper, I still end up
with four lines of code compared to option a) which also has
approximately 4 lines of code.

And for some reason, the above doesn't quite work, since the generated
code ends up with the EJB annotation on BOTH the field AND the setter,
which like you said, could confuse some containers.

Is there a way to tell scala to *not* annotate the field?

Cheers,
Ant

On Feb 3, 3:40 am, Josh Suereth wrote:
> The "field" and field name are considered "private implementation details
> of the compiler". The "var" keyword creates a field (the name of which is
> an implementation detail), and a pair of methods:  "name" and "name_$eq"
> which are the getter/setter.
>
>  IF you place the annotation on a var, it will end up on both the field +
> the methods for convenience. This could be hurting your bytecode rewriting
> since the annotation shows up two places.  I know from experience using
> EJB3 and hibernate.   You need to ensure that your annotation only lands in
> the desired location, using one of
> annotation.target.{field,setter,getter,beanGetter,beanSetter}
>
> What you probably want to do is:
>
> type EjbOnField = (EJB @annotation.target.field)
> @EjbOnField(...)
> private var scalaEjb2NoInterface: MyScalaEJB2Bean = null
>
> There's also the potential that the EJB bytecode rewriters are getting hung
> up on some scala classfiles.   That's a very real possibility, and
> something we try to prevent from happening as often as possible, but some
> tools are not as flexible as the JVM itself, especially with rewriting.
>
> I've done a decent amount of Scala with EJB.  As with most
> Scala-JavaFramework integrations, you need to limit the amount of advanced
> features you use at that boundary and things are good.   This may be such a
> case.
>
> If you can isolate what bytecode may be causing the problem, in
> a reproducible way, it's possible we can fix it.  I'd try the annotation
> fix first.
>
> On Thu, Feb 2, 2012 at 3:56 PM, Ant Kutschera wrote:
>
>
>
>
>
>
>
> > Hi,
>
> > That isn't correct.  Here is the *decompiled* Scala (ie, from the
> > class file which the Scala compiler generated):
>
> >  @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> >  private MyScalaEJB2Bean scalaEjb2NoInterface;
>
> > I got that out of:http://java.decompiler.free.fr/?q=jdgui
>
> > So there is indeed a field.
>
> > Also, don't forget, this works when the the EJB has a local or remote
> > interface, and I inject the interface.  It doesn't work, when I remove
> > the interface.  According to the EJB 3.1 specs, and my own tests in
> > Java, this should work fine without an interface.
>
> > The problem is the bean which the container is instantiating.  It
> > seems to have the wrong type.  I assume that if there is an interface,
> > the container creates a standard Java proxy using that interface, and
> > passes calls to the interface through to the implementation.  When
> > there is no interface, it cannot create a normal proxy and has to
> > manipulate the byte code.  Something in the bytecode generated by
> > Scala is causing the container to have problems.
>
> > I just can't figure out what the problem is, nor how to solve it.
>
> > Cheers,
> > Ant
>
> > On Feb 2, 8:53 pm, Josh Suereth wrote:
> > > Scala does not have fields.  Members are all methods.   You can annotate
> > > your annotations with @annotation.target.field and it will be placed on
> > the
> > > (inaccessible from scala) field that the compiler generates.
> > > On Feb 2, 2012 10:00 AM, "Ant Kutschera"
> > wrote:
>
> > > > Perhaps, I'll give it a whirl.  Although the container should be
> > > > setting the field, rather than using an accessor, since the EJB
> > > > annotation is on the field.
>
> > > > On Feb 2, 3:21 pm, Dave wrote:
> > > > > I think you need to annotate the ejb property with @BeanProperty as
> > > > > well.
>
> > > > > @BeanProperty
> > > > > @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> > > > > var scalaEjb2NoInterface: MyScalaEJB2Bean = _
>
> > > > > to generate to right EJB getter/setter format getScalaEjb2NoInterface
> > > > > and setScalaEjb2NoInterface
>
> > > > > On 2 feb, 13:45, Ant Kutschera wrote:
>
> > > > > > I'm attempting to deploy an EJB written in Scala to an app server.
>
> > > > > > If I create a stateless session bean and give it a (remote)
> > interface,
> > > > > > then it works fine (the interface is a trait).
>
> > > > > > The problem is when I want to create a stateless session bean with
> > NO
> > > > > > interface. On both Glassfish and JBoss I get an error. If I convert
> > > > > > the code to Java, then I have no problem.
>
> > > > > > Glassfish 3.1.1: IllegalArgumentException: Can not set
> > > > > > ejb.MyScalaEJB2Bean field
> > > > > > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > > > > > $Proxy290
>
> > > > > > JBoss 7.0.2.Final: IllegalArgumentException: Can not set
> > > > > > ejb.MyScalaEJB2Bean field
> > > > > > ch.maxant.produkte.web.scala.BhfSuche2.scalaEjb2NoInterface to
> > > > > > scala.ScalaObject$$$view4
>
> > > > > > The EJB source is:
>
> > > > > > package ch.maxant.produkte.ejb.scala
>
> > > > > > @Stateless(name = "ScalaTestBean2", mappedName = "ScalaTestBean2")
> > > > > > class MyScalaEJB2Bean {
>
> > > > > >   def doStuff(x: String): String = {
> > > > > >     "hello " + x + ", this is a scala EJB"
> > > > > >   }
>
> > > > > > }
>
> > > > > > The Servlet which needs the EJB injecting has this source:
>
> > > > > > package ch.maxant.produkte.web.scala;
>
> > > > > > @WebServlet(urlPatterns = Array("/BhfSuche2"))
> > > > > > @SerialVersionUID(1L)
> > > > > > class BhfSuche2 extends HttpServlet {
>
> > @EJB(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> > > > > >   var scalaEjb2NoInterface: MyScalaEJB2Bean = null
>
> > > > > >   @throws(classOf[ServletException])
> > > > > >   @throws(classOf[IOException])
> > > > > >   override def doGet(request: HttpServletRequest, response:
> > > > > > HttpServletResponse):Unit = {
>
> > > > > >     val msg2 = scalaEjb2NoInterface.doStuff("ScalaWebServlet")
> > > > > >     response.getWriter.write(msg2 + "\r")
> > > > > >   }
>
> > > > > > }
>
> > > > > > Why am I getting these errors and is there anything I can do about
> > it?
>
> > > > > > Cheers,
> > > > > > Ant

Ant Kutschera
Joined: 2012-01-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Scala EJB without interface doesn't seem to work

On Feb 3, 3:40 am, Josh Suereth wrote:
> The "field" and field name are considered "private implementation details
> of the compiler". The "var" keyword creates a field (the name of which is
> an implementation detail), and a pair of methods:  "name" and "name_$eq"
> which are the getter/setter.

Hi Josh,

Let's say I had:

@EJB myEJB : SomeEJBClass

What you are saying is that the compiler might implement that as:

@EJB someNameTheCompilerChose : SomeEJBClass

public SomeEJBClass myEJB(){return someNameTheCompilerChose;}
public void myEJB_$eq(SomeEJBClass a){someNameTheCompilerChose=a;}

Even if it did do this, it's not a problem. Anywhere in my Scala code
where I refer to "myEJB" the compiler would generate using
"someNameTheCompilerChose". So the container would be correct to
inject the field.

I don't see the problem here. And like I posted a few minutes ago,
the API docs clearly state that the annotation goes on the field by
default.

Cheers,
Ant

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: Scala EJB without interface doesn't seem to work

On 2 feb, 22:13, Ant Kutschera wrote:
> Fixed it!
>
> The bean without an interface needs this annotation too:
>
> @LocalBean
>
> After adding that to the bean implementation class, just after
> @Stateless, everything works fine.

But the Java version didn't need @LocalBean?

Ant Kutschera
Joined: 2012-01-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Scala EJB without interface doesn't seem to work

On Feb 3, 2:14 pm, Dave wrote:
> But the Java version didn't need @LocalBean?

No.

Here is what the EJB 3.1 spec has to say:

4.9.8 Session Bean’s No-Interface View
The following are the requirements for a session bean that exposes a
no-interface view :
• The bean class must designate that it exposes a no-interface view
via its bean class definition
or in the deployment descriptor. The following rules apply :
• If the bean does not expose any other client views (Local, Remote,
No-Interface, 2.x
Remote Home, 2.x Local Home, Web Service) and its implements clause is
empty,
the bean defines a no-interface view.
• If the bean exposes at least one other client view, the bean
designates that it exposes a
no-interface view by means of the @LocalBean annotation on the bean
class or in
the deployment descriptor.
• The following interfaces are excluded when determining whether the
bean exposes a
no-interface view : java.io.Serializable; java.io.Externalizable;
any of the interfaces defined by the javax.ejb package.
• All public methods of the bean class and any superclasses are
exposed as business methods
through the no-interface view. [ Note : This includes callback
methods. The Bean Developer
should exercise caution when choosing to expose callback
methods as business methods
through the no-interface view. The runtime context(e.g. transaction
context, caller principal,
operations allowed, etc.) for a method invoked as a callback can
differ significantly from the
context for the same method invoked via a client invocation.
In general, callback methods
should not be exposed as business methods. Therefore, it is
recommended that all non-business
methods be assigned an access type other than public.]
• The throws clause of a bean class method exposed through the no-
interface view must not
include the java.rmi.RemoteException.
• All methods of the bean class and any superclasses must not be
declared final.

This part is interesting: "If the bean exposes at least one other
client view, the bean designates that it exposes a
no-interface view by means of the @LocalBean annotation on the bean
class or in the deployment descriptor."

It could be, that because Scala generates code which implements the
interface "ScalaObject", that the container thinks that is a business
interface. And hence the injection fails, because the injected object
does not have the right methods which the servlet reference is
expecting.

It surprises me a little, since I would have thought that the
container would only consider interfaces annotated with @Local or
@Remote to be business interfaces.

All that said, I can live with annotating Scala EJBs with @LocalBean.
If I forget, my tests will fail fast and hard, so that's not a big
problem.

Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
Re: Re: Scala EJB without interface doesn't seem to work
So, I was doing EJB with Scala 2.8 where the default was *not* the Field, but the getter/setter and field.  I had forgotten that 2.9 "fixed" that, by defaulting to field.
Next, scala will *always* generate getters/setters for you, unless your field is private[this] (and even then, you may still get a method).
So all you need to annotate the setter is:
type EjbOnMethod=(EJB @scala.annotation.target.setter)
@EjbOnMethod(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2") var scalaEjb2NoInterface: ejb.MyScalaEJB2Bean = null
NOTE, we're annotating the EJB annotation itself here.  You don't need to add @EJB again.  Also the @BeanProperty annotation is meaninless, since it just adds *extra* getters and setters (called getXxx and setXxx rather than xxx() and xxx_$eq()) for us.
The explanation of the interfaces makes a whole lot of sense, sorry to send you down a sidetrack.  However, especially when using EJB technology, it's important to understand what Scala code is compiling down to.  Every member of a class compiles to a method.  The exception to that rule are things marked private[this].  If your Java framework is sufficiently well-coded, there's no need for the @BeanProperty annotation at all, helping bytecode size  a teeny-tiny amount.
- Josh


On Fri, Feb 3, 2012 at 3:12 AM, Ant Kutschera <ant.kutschera@gmail.com> wrote:
On Feb 3, 3:40 am, Josh Suereth <joshua.suer...@gmail.com> wrote:
> The "field" and field name are considered "private implementation details
> of the compiler". The "var" keyword creates a field (the name of which is
> an implementation detail), and a pair of methods:  "name" and "name_$eq"
> which are the getter/setter.

Hi Josh,

Let's say I had:

    @EJB myEJB : SomeEJBClass

What you are saying is that the compiler might implement that as:

   @EJB someNameTheCompilerChose : SomeEJBClass

   public SomeEJBClass myEJB(){return someNameTheCompilerChose;}
   public void myEJB_$eq(SomeEJBClass a){someNameTheCompilerChose=a;}

Even if it did do this, it's not a problem.  Anywhere in my Scala code
where I refer to "myEJB" the compiler would generate using
"someNameTheCompilerChose".  So the container would be correct to
inject the field.

I don't see the problem here.  And like I posted a few minutes ago,
the API docs clearly state that the annotation goes on the field by
default.

Cheers,
Ant

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: Scala EJB without interface doesn't seem to work

> type EjbOnMethod=(EJB @scala.annotation.target.setter)
> @EjbOnMethod(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> var scalaEjb2NoInterface: ejb.MyScalaEJB2Bean = null

Why doesn't the scala compiler do that internally?

Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
Re: Re: Scala EJB without interface doesn't seem to work
The Scala compiler will annotate the field in 2.9.  It used to annotate everything.  Which default is correct?  Any default is wrong in some circumstance, hence the target annotations.   I talk about these and setting up helper annotation libs for your own code in Chapter 10 (Java interop) of Scala In Depth.
- Josh

On Fri, Feb 3, 2012 at 1:46 PM, Dave <dave.mahabiersing@hotmail.com> wrote:
> type EjbOnMethod=(EJB @scala.annotation.target.setter)
> @EjbOnMethod(lookup="java:global/ProdukteEAR/ProdukteScala/ScalaTestBean2")
> var scalaEjb2NoInterface: ejb.MyScalaEJB2Bean = null

Why doesn't the scala compiler do that internally?

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland