I'm working on a project where I have the following structure:
(1) An API that holds some case classes, traits, and abstract classes.
(2) Main Jar that uses API(1) as a dependency (API is published and used in the build.sbt as deps).
(3) We have a need sometimes to create jars and load them in the main jar (2) using class utils during runtime, knowing that these jars also use API(1) as a dependency.
Now everything was working fine with the 3rd point which is about loading classes from the jar on runtime and making sure that these extend the API class correctly:
clazz = classLoader.loadClass("name") return clazz.asInstanceOf[MyClass]
But after I made a small change to MyClass in the API which was mainly about adding an implicit parameter to the signature of my abstract class, I can no longer identify the external jars (being loaded during runtime) and I keep getting the following error:
[error] (run-main-0) java.lang.ClassCastException: java.lang.Class cannot be cast to MyClass
[error] java.lang.ClassCastException: java.lang.Class cannot be cast to MyClass
MyClass signature from the API looks something like this:
abstract class MyClass (implicit val something: Type) extends AnotherClass {
Any help to bypass this is appreciated guys. I can give more details if needed as well.
Thanks
Related
I have define scala object(say, MyObject) which extends following
trait GeneratedMessageCompanion[A <: GeneratedMessage with Message[A]]
And when I call parseFrom method on the object, I get following error:
Caused by: java.lang.NoSuchMethodError:....MyObject$.parseFrom([B)Lscalapb/GeneratedMessage;
I tried both scalapb-runtime_2.11 and scalapb-runtime_2.12.
Edit: Issue is solved. It was case of dependency mismatches.
I have written a facade using JSImport, and it works. Unfortunately, I arrived at the solution through trial and error, and I don't fully understand why this particular solution works but others I tried did not.
Background: I'm starting with a working project, built with sbt, which is a single page application that implements the client side code with scala.js and the server side with scala and the Play framework. The javascript libraries were packaged with web jars and bundled into the client js file using the sbt jsDependencies variable. I wanted to implement some new features which required a library up rev, which then required an up rev of some javascript libs which were only available in npm format. So now I am including all the javascript dependencies for the client app using npmDependencies with the scalajs-bundler plugin. This broke some of the scalajs facades leading to my question.
I'll use the facade to log4javascript as an example for this question.
The variable log4javascript is the top level object used to access the rest of the api.
When the js libs were included as web jars, this is how the facade to log4javascript was implemented:
#js.native
#js.annotation.JSGlobalScope
object Log4JavaScript extends js.Object {
val log4javascript:Log4JavaScript = js.native
}
After the change to npm:
import scala.scalajs.js.annotation.JSImport.Namespace
#JSImport("log4javascript", Namespace)
#js.native
object Log4JavaScript extends js.Object {
def resetConfiguration(): Unit = js.native
def getLogger(name:js.UndefOr[String]): JSLogger = js.native
...
}
Following the scala.js docs for writing importing modules I expected the object name (Log4JavaScript in this case) would have to match the exported symbol name in order for the binding to work. However, the top level symbol in log4javascript.js is log4javascript. After experimenting, it seems the scala object name makes no difference for the binding. It binds correctly no matter what I name the scala top level object.
Can someone explain what relationship exists, if any, between the scala object/class/def/val names and the names in the javascript module when using the 'Namespace' arg to JSImport?
According to the scala.js docs, it seems I should be able to provide the actual name of the js object (I also tried "Log4JavaScript")
#JSImport("log4javascript", "log4javascript")
#js.native
object SomeOtherName extends js.Object {
def resetConfiguration(): Unit = js.native
def getLogger(name:js.UndefOr[String]): JSLogger = js.native
...
}
However, this fails to bind. I will get a runtime error when I try to access any of the member functions.
Log4JavaScript.resetConfiguration()
Uncaught TypeError: Cannot read property 'resetConfiguration' of undefined
Can someone explain why this doesn't work?
log4javascript also defines some classes inside the scope of log4javascript. When the lib was included as a web jar the definition looked like:
#js.native
#JSGlobal("log4javascript.AjaxAppender")
class AjaxAppender(url:String) extends Appender {
def addHeader(header:String, value:String):Unit = js.native
}
After switching to npm I had to put the class definition inside the top level object:
#js.native
trait Appender extends js.Object {
...
}
#JSImport("log4javascript", "log4javascript")
#js.native
object Log4JavaScript extends js.Object {
...
class AjaxAppender(url: String) extends Appender {
def addHeader(name: String, value: String): Unit = js.native
}
...
}
This seems sensible, but from the scala.js docs it seems like it should have been possible to define it this way outside of the top level object
#JSImport("log4javascript", "log4javascript.AjaxAppender")
#js.native
class AjaxAppender(url: String) extends Appender {
def addHeader(name: String, value: String): Unit = js.native
}
However, this also fails to bind. Could someone explain the correct way to define the class as above? Or is the definition nested inside the Log4JavaScript object the only correct way to do it?
Can someone explain what relationship exists, if any, between the scala object/class/def/val names and the names in the javascript module when using the 'Namespace' arg to JSImport?
This is explained in this part of the Scala.js documentation. The name of the Scala object defining the facade does not matter. What matter are the parameters of the #JSImport annotation. The first one indicates which module to import from, and the second one indicates what to import.
In your case, the log4javascript module is in the log4javascript.js file, in the log4javascript package directory. So, your first parameter should be:
#JSImport("log4javascript/log4javascript.js", ...)
object Log4JavaScript ...
However, log4javascript is defined as an npm module whose main file refers to the log4javascript.js file. This means that you can just use the package directory name:
#JSImport("log4javascript", ...)
object Log4JavaScript ...
(See this article for more information on how NodeJS does resolve modules)
The second parameter of the #JSImport annotation indicates what to import. In your case, you want to import the whole module, not just a member of it, so you want to use Namespace:
#JSImport("log4javascript", Namespace)
object Log4JavaScript ...
This corresponds to the following EcmaScript import statement:
import * as Log4JavaScript from 'log4javascript'
Note that, although the Scala object name (Log4JavaScript, here) does not matter, the names of its members does matter, as explained in this part of the Scala.js documentation.
According to the scala.js docs, it seems I should be able to provide the actual name of the js object (I also tried "Log4JavaScript")
#JSImport("log4javascript", "log4javascript")
...
However, this fails to bind. I will get a runtime error when I try to access any of the member functions.
When you write that, you try to access to the log4javascript member of the log4javascript module. But that module does not have such a member.
it should have been possible to define it this way outside of the top level object
#JSImport("log4javascript", "log4javascript.AjaxAppender")
...
However, this also fails to bind.
Again, this means “import the log4javascript.AjaxAppender member from the log4javascript module”, but that module does not have such a member. The following should work:
#JSImport("log4javascript", "AjaxAppender")
In Scala, what's the difference between declaring a non-nested trait to be private or package private. I.e., what's the difference between
private MyTrait { ... }
and
private[enclosingpackage] MyTrait { ... }
I know that there is a difference because I can get certain compilation errors to go away if I add the package qualifier. E.g., these two compilation errors will go away if I add the package qualifier to the definition of the Service trait:
[error] /Users/dalan/s/local/eclipse/jeep/runcible/src/main/scala/
org/gaffa/gpp/runcible/httpserver/HttpServer.scala:146:
private trait Service escapes its defining scope as part of type
org.gaffa.gpp.runcible.httpserver.Service
[error] object HttpServer extends App with Service {
[error] ^
[error] /Users/dalan/s/local/eclipse/jeep/runcible/src/main/scala/org/
gaffa/gpp/runcible/httpserver/HttpServer.scala:156: not found: value routes
[error] Http().bindAndHandle(routes, config.getString("http.interface"),
config.getInt("http.port"))
[error] ^
[error] two errors found
The routes val that is being complained about in one of the error messages is in the Service trait, which is being "extended" by the HttpServer singleton object.
Thanks for your help!
P.S. All the code in question is in the same file, in case that makes any difference.
As mentioned at the old discussion article referred by Chris: the qualified private[foo] and the normal private are acting on different levels.
The qualified private[foo] is checked by the Scala compiler and it's giving you access to the resource as long as you belong to the same package. That's why it compiles in the case you are asking here.
Thus, the normal private is a Java regular private instruction that seals the resource to only be used by the resource itself.
None of them is better than the other, of course, it depends on the scope of protection you want to enforce.
There is no difference. It must lead to compile error in both cases, because in both cases "trait Service escapes its defining scope", but with qualified private compiler forgets to do this check.
Seemed that you have met this bug: SI-4323
I'm attempting to un-Spring IoC several Java classes and load them directly in some Scala code. Naturally I'm finding that there are name space conflicts between a package like
com.a.x.SomeClass
and
com.a.y.x.SomeClass
I've tried using the import name space resolvers like
import com.a.y.x.{ SomeClass => YYYSomeClass }
import com.a.x{ SomeClass => XXXSomeClass }
That cleans up the imports, but referring to those classes later in the classes show the error as hovers in the Typesafe ScalaIDE, and after clean compilations.
When I compile from the gradle scala plugin, or via the Typesafe ScalaIDE with scala 2.10.2 or 2.10.3, I get the following type of un-useful error messages:
class SomeClass in package x cannot be accessed in com.a.y.x
The problem occurs if I try to use a class from com.a.y.x that doesn't have a name space conflict. If I try some of the scalac flags, I've also been able to get a Warning out that's slightly different ( during the typer stage ):
class SomeClass in package x cannot be accessed in y.this.x
I'd really like to know if there's a way to expand the first package reference. I'm having trouble setting up an Eclipse project to debug the scalac compiler and I haven't found a scalac flag that adds useful information to the error.
The error occurs when I either try to ctx.getBean("someClass").asInstanceOf[XXXSomeClass] or new XXXSomeClass.
The this reference from the warning makes me think there's some object confusion going on. I don't think it's classpath issue in the larger project I'm working on because removing one jar removes the ability to resolve imports, however I can't reproduce it with simple example classes in a separate project.
A little more information on the java classes - the different, conflicting java packages are in separate jars that were compiled by a 1.6 flavor of Java. They are top level, public classes although two are singletons with private constructors and corresponding public static getInstance() methods.
It really is an access error. I think this is just a poor error message. The java was compiled separately and accessed from jar files. I hadn't paid enough attention to the package private nature of the classes I wanted to instantiate( I had seen "public" in front of some of them and not carefully checked all of them ). The name space collisions were a red-herring in this case and actually don't affect the output of the error message.
In some cases, there are public interfaces I can use instead with a little bit of Spring-glue to instantiate the objects and load them into the Scala class.
The scalac error message would have been more helpful if it had the form:
class SomeClass in package com.a.y.x cannot be accessed in totally.unrelated
where the first package reference is the Java class's package and the trailing package is the package of the Scala class trying to instantiate the Java class.
To recap - the Java class was like:
package com.a.y.x.SomeClass
class SomeClass extends SomeOtherClass implements SomeInterface
and needs to be like:
package com.a.y.x.SomeClass
public class SomeClass extends SomeOtherClass implements SomeInterface
to not get these access errors.
Since I have more control of the Scala here, on a whim I tried changing the package of a Scala class from totally.unrelated to com.a.x.y which then compiles cleanly. As you might guess, that merely defers the error to a run time error -> java.lang.IllegalAccessError. However, the IllegalAccessError from the Java has the description/package order that I think should happen in the compilation error from scalac:
...java.lang.IllegalAccessError: tried to access class com.a.y.x.SomeClass from class s.Trouble$delayedInit$body
I have a simple jar file containing class A:
public class A {}
Then I load it in runtime:
var classLoader = new URLClassLoader(Array(my_jar_file.toURI.toURL))
var clazz = classLoader.loadClass("A")
It is ok, it can load the class A. This command is also ok:
clazz.newInstance
But when I cast it to A:
clazz.newInstance.asInstanceOf[A]
I got this error:
java.lang.ClassCastException: A cannot be cast to A
Could you please help me?
Your code implies that you have "A" available in one classLoader context where you are calling clazz.newInstance.asInstanceOf[A] which is a separate context from where you are getting the clazz object. The problem is that you have two different instances of the class "A" in two different classLoader contexts. An object that is created from one version of class "A" cannot be cast to an instance of the other version in a different classLoader context.
I experienced a very similar problem, in that I observed a ClassCastException when casting a dynamically loaded object to an interface implemented by it.
Thanks to Neil's answer, I was able to determine that the ClassCastException was caused by having different class loader contexts.
To fix this I used the URLClassLoader(URL[] urls, ClassLoader parent) constructor instead of the URLClassLoader(URL[] urls) constructor.