I have built a jar from my Scala project.
I have the following structure for what I want to use from this jar
package aaa.bbb.ccc
case class FooResult(...)
trait Foo(...) {
def bar(): FooResult
}
object Foo {
private class FooImpl(...) extends Foo {
...
}
def apply(...): Foo
}
First question: Maybe I have misunderstood something in what Py4J offers,
but do I have to write a java/scala class to start the Py4J gateway if I want to use my own classes? Or is it enough to add it to the gateway's jvm's classpath?
Second question (which I guess doesn't apply depending on the answer to above): How do I add my jar when starting the java gateway in order to make it available? To solve this temporarily, I just started the jvm manually with my jar along with the Py4J jar with this command
java -classpath "path/to/py4j.jar:path/to/my.jar" py4j.GatewayServer 0
and then connected to it manually from the Python code. Then I tried to import my classes via
java_import(gateway.jvm, "aaa.bbb.ccc.*")
which didn't throw any error but I'm not sure it worked because it doesn't throw any error if I input some fake classpath.
Third question (which applies if the answer to the first is that I have to write the entry point to access my classes): How does this work when using scala?
object Main extends App {
val gw = new GatewayServer(// TODO: how to expose my classes here?
}
Related
I need to import custom classes defined outside of my jenkinsfile. These classes have constructors which require parameters. I've already tried using load() for each of the class files, but it seems this only works for script files that just define static methods. When I tried using this to load my class files, it threw an error that it could not find an <init> method.
I currently have a way of importing these classes that works, but it requires re-cloning the repository as a library (code below). I'd like to move away from this since it's inefficient and seems like it should be unnecessary.
current working (but not great) implementation:
Jenkinsfile:
lib = library(identifier: "<libraryName>#${env.currentBranch}",
retriever: modernSCM([$class: 'GitSCMSource',
credentialsId: <credentialsId>,
id: '<id>',
remote: env.projectRemote,
traits: [[$class: 'jenkins.plugins.git.traits.BranchDiscoveryTrait']]])).com.company.jenkins.sdk
def git = lib.Git.new(this, currentStage, currentStep)
...
src/com/company/jenkins/sdk/Git.groovy:
package com.company.jenkins.sdk.Git
class Git implements Serializable {
def script
def stage
def step
Git(script, stage, step){
...
}
}
I'd like to get rid of that library command and replace it with a simpler way of importing these classes.
I was working on injecting of groovy scripts dynamically in Java. So before executing those scripts, I want to get sure of that they do not have potential bugs using SpotBugs (static code analyzer).
Here is the Psuedo-Code:
Here it should return the infinite loop bug.
String script = "class Hello { static void main(String []args) { def i = 0; while ( i <= 0) { i = i - 1; } } } ";
List<Bugs> bugs = SpotBugs.getBugs(script);
if (bugs == null) {
execute(script);
}
So how to do the SpotBugs.getBugs(script) using java, the input script will not be hard-coded as in above example, but will be dynamically fetched.
Easiest API
The easiest way is to write the compiled code to class files (in a temp directory if needed). By having compiled class as file, you will be able to use the FindBugs class which provide an API to configure the scope and rules without playing with internal classes that are subject to changes.
Groovy dynamic (default) vs static compiling
However, the main obstacle you'll face is that groovy bytecode is too obfuscated for SpotBugs. For the call to function abc(), you will not see an invoke to method abc in the bytecode. It will be a reference to a global functions map that is created at runtime. Groovy has a mode to compile to a less dynamic format. This mode does not allow functions to be created at runtime. You can check the configuration to instruct the compiler for the static mode in this test repo: https://github.com/find-sec-bugs/find-sec-bugs-demos/tree/master/groovy-simple. This is, however, a Gradle compilation not a programmatic API that received a String as code.
seems like SpotBugs should run using maven, which means it will package and include only the groovy scripts that are valid.
hence, you will not need to check before execution.
I encountered a compilation issue under Linux.
I'm compiling java programs on Linux; the target use is both Linux and Windows.
The code check if in there are platform specific classes (as shown in the code below).
So if the code is running under Linux, the specific Windows code will not be executed.
The issue arise on the use of a platform specific class Win32MediaTray
The compile error reported is
PrinterScanner.java:9: error: cannot find symbol
import sun.print.Win32MediaTray;
^
Is it possible to compile it under Linux? Or is it just impossible?
I can use some workaround (reflection?)
Needless to say that the compilation under Windows gives no errors.
Thankyou for your help.
For reference, the code behind this issue is the following:
private String getTrayName(Media media) {
String result = "id:" + media.getValue();
boolean isWin32 = media.getClass().getName().equals("sun.print.Win32MediaTray");
if (isWin32) {
Win32MediaTray w32 = (Win32MediaTray) media;
result = result + ",winId:" + w32.winID;
}
return result;
}
I believe that the class you are trying to use is sun.print.Win32MediaTray.
And the answer is that you cannot use it ... or compile a class that uses it ... on a Linux release of Java. That class is not included in the rt.jar file on a Linux release of Java.
Furthermore, you shouldn't be using it. The Java documentation makes it very clear that application code should not make use of classes in the sun.* package hierarchy.
If you have no choice but to do this, then your best bet is to use reflection to fetch the value of that w32Id field. You'll also need to deal with the case where the media object is not an instance of the Win32MediaTray class. Beware that you are relying on implementation details that Oracle says specifically that you shouldn't. There is a risk that they will change (without notice!) in some future Windows release.
The other alternatives are:
Implement your own platform adapter classes with a different one for each platform. These have to be compiled separately on each platform, and then dynamically loaded.
Implement separate codebases for each platform.
To make the compiler happy you could implement a dummy class named sun.print.Win32MediaTray and make it available both on the compile and runtime classpath. The class doesn't need to work, it only has to be API compatible (same signatures and return types, but in this case you only really need to extend Media and have a public int winID), so that you can satisfy both the compiler and the verifier.
At runtime, the version included in rt.jar should be loaded on Windows thanks to loading delegation. On Linux, the dummy version is the only one available, but you stated that the program checks for the platform and executes another branch of code, so it shouldn't cause your program to fail.
For example, with the following class on the classpath:
package sun.print;
import javax.print.attribute.standard.Media;
public class Win32MediaTray extends Media {
public int winID = 0xBADC0DE;
protected Win32MediaTray(int value) {
super(value);
}
static {
System.out.println("Won't see me on Windows");
}
}
I managed to run this program on Windows:
public class Main {
public static void main(String[] args) {
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
for (PrintService svc : services ) {
DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PAGEABLE;
Object o = svc.getSupportedAttributeValues(Media.class, flavor, null);
if (o != null && o.getClass().isArray()) {
for (Media media : (Media[]) o) {
if ( media instanceof Win32MediaTray )
System.out.println( ((Win32MediaTray) media).winID );
}
}
}
}
}
The message in the static initializer is not printed on Windows, because the definition that is actually loaded is the one from rt.jar. Obviously, the code can be compiled on any platform.
How about putting the code that uses windows-specific stuff into a separate jar; then you can compile and include that jar on windows, and leave it off systems otherwise.
One standard way to do this is to have one or more interfaces used by your application code; you can have a factory provide the implementing classes or inject them with Spring or whatever. But I think rather than "how can I compile this on Linux" your question should be "I have this Windows dependency in an app targeted at multiple operating systems, how do I handle it?"
By running System.loadLibrary("myAPI"), I verified that the DLL file "myAPI.dll" can be successfully loaded into my Eclipse Java project. Now I need to call methods specified inside this DLL file from my Java code. To do this, I added JNA to my Java project. Then I wrote the below-given code snippet that should be able to get instances of classes IProject and ProjectFactory (specified in the DLL file).
I still don't understand how to properly implement this with JNA. I checked different threads, e.g. this one, but the ones I checked don't provide an answer. Any help is highly appreciated. Thanks.
import com.sun.jna.Library;
import com.sun.jna.Native;
public class MyClass {
public interface myAPI extends Library {
//...
}
void LoadProj() {
myAPI api = (myAPI) Native.loadLibrary("myAPI",myAPI.class);
String fileName = "xxx.sp";
IProject project; // this is wrong but shows what I am trying to do
try {
project = ProjectFactory.LoadProject(fileName);
}
catch (Exception ex) {
MessageBox.Show(this, ex.Message, "Load failure");
}
}
}
Not sure what problem you are facing but as a practice your myAPI interface should declare all the methods verbatim with appropriate parameter mapping. I don't see any methods inside your interface.
Please checkout the this link as well as the link mentioned above by #Perception
If there are no Java classes or Java source hidden inside this DLL (which would be ... strange), then it will never work this way. You can't instantiate C# classes or use C# interfaces. MessageBox.Show( isn't Java either, it is Windows Forms code.
So, I have something written in Java, and I want to extend it in Scala... The issue I'm running into is that Scala isn't seeing methods I need.
Here is how it's set up:
Player extends Mob, and Mob extends Entity.
I need to access a method in Player that isn't defined in Mob or Entity, but Scala doesn't think it exists even though Java does.
It can see methods defined by Mob and Entity just fine. Also, all the methods I'm talking about are non-static.
So, am I doing something wrong, or is this a limitation imposed by Scala?
Edit --
Here is the relevant code:
package test
import rsca.gs.model.Player
object Test {
def handle(p:Player): Unit = {
p.getActionSender().sendTeleBubble(0, 0, false);
}
}
Player class:
package rsca.gs.model;
// imports
public final class Player extends Mob {
// Implemented methods (not going to post them, as there are quite a few)
// Relevant code
private MiscPacketBuilder actionSender;
public MiscPacketBuilder getActionSender() {
return actionSender;
}
}
Error:
value getActionSender is not a member of rsca.gs.model.Player
I never encountered such problems, and you probably checked your configuration and everything else twice, so I would guess this is some Eclipse related build issue. You should try to build from the command line in order to see whether Scala or Eclipse is the problem.
Is it possible for you to run a test against the class just to see if you got the right one?
p.getClass.getMethods
... and if possible (may run into NPE) in order to find the source:
p.getClass.getProtectionDomain.getCodeSource.getLocation.getPath
When compiling the Scala class, do something like this:
scalac *.scala *.java
This way, Scala will look a the Java code to see what is available. If, however, the Java code is already compiled and provided as a jar file, just add it to the classpath used when compiling the Scala code.