söndag 16 juni 2013

Some sort of success

This is getting annoying, but I do have had some success, just this very minute. Even though it doesn't work.

Now, what you see is my minimal Twitter console client. It works relatively straight forward, you get a link to open in your browser in order to authenticate the Twitter console. When you have logged in to Twitter, and authorized the application (currently named "Jamiga Twotter"), you get a PIN which you should enter into the console application.

An error indicating some sort of success

The result you can the see further down in the picture: Twitter responds with an error, saying that the REST API v1 is no longer active. So, it turns out, that the Twitter4J client I tried out is no longer supported. And this happened recently, in March 2013. I had my Twitter console application running nicely on another platform just the other day, so even though I probably should be a bit mad, I'm actually happy getting this error. Because this is a valid really great error! It means that what I've done, actually works. The JVM can communicate with outside servers, and he whole Twitter API parsing works. So this test actually proves that a lot of stuff is working.

Now, to the avail, the Twitter4J people (who's java implementation I'm using) are saying they've fixed it. So it look like I'm just an update and configuration away from having a working Twitter implementation on JAmiga.

Oh, and by the way, I also fixed the "double trouble" with converting doubles to strings.

lördag 15 juni 2013

Amiga paths

I eventually got some work done this week, but still not quite there. The problems regarding the reported file size was easily fixed, once I saw the error. It was a rather embarassing error. Apparantely the Java type "long" was defined as a 32 bit value, where Java wants a 64 bit type. Easily fixed, and kind of in the areas of what I thought was wrong. Following that fix, my current problem arise around opening file through the Java URL class, i.e. on the "file://SYS:s/startup-sequence"-form. I had the feeling this would come and bite me, sooner or later. GNU Classpath supports Unix and Windows paths, and it decides which system is in use by checking the file separator; if its backslash, its Windows, if it isn't we have Unix. A very pragmatic look, but not terribly allowing for other systems. I'm currently going through the Java class hierarchy to try and find where exactly it doesn't work, because in some cases it works, and in other cases JamVM is trying to reach volumes named "/SYS:".

So, I'm think I'll be able to solve this in the next week, and I can only hope I don't stumble upon some other big issues. I'm counting on minor issues, though. 

måndag 10 juni 2013

Slight delay

The deadline for the bounty is closing in on me. Sadly I won't have much time to work on it this week... so my fear is that the deadline will be delayed. Hopefully only a few days though.

My recent progress has surfaced a few specific issues I have to deal with prior releasing a beta version.

A piece of pie

One of the bounty goals is an SSH-client. The more simple telnet variant works, as can be seen in previous post. However, trying with SSH bubbles up an issue I'm well aware of, that I thought I could wait with. Converting the string "3.1415" to the actual value 3.1415, is actually performed in native code, meaning I need to implement the support for it. Its a rather complex amount of code, but luckily I don't have to do it myself since it has been taken care of in the GNU Classpath implementation. The existing code however doesn't compile out-of-the-box, so I have ignored it for now. I'm hoping this will only be a matter of hours fiddling with the existing source to make it compile in the Amiga environment. And then I'm hoping that the SSH client won't have anymore issues.

The file was how big?

One other issue, that I perhaps could get away without resolving immediately, is a strange issue with how classes are loaded by the JVM. Up until this point, all my classes have been residing in the bootclasspath, as opposed to the ordinary classpath. The bootclasspath should contain all classes needed for the JVM to start, and "user" classes, or rather, external thirdparty jars (for instance the SSH library, or the Twitter4J API) should reside in the ordinary classpath. The ordinary class loader uses Java code to load the classes while the bootclasspath loader uses native code. And there seems to be some issues with how my Amiga implementation reports the size of class files, because every time the JVM want's to allocate memory for the class to load, I get an OutOfMemory exception. Looking at my code and debugging, I really can't see where the size get messed up. The Amiga part correctly reports ordinary sizes, but on the Java side its almost as if using uninitialized variables, or some sort of unsigned/signed error.

Given enough time this week, I believe I could have solved these issues in time. However, my current workload and various after work meetings, minimize my JAmiga time to a value near zero.

måndag 20 maj 2013

A telnet-client in Java on AmigaOS 4.1

I'm happy to announce the first working usable application for Java on Amiga: a telnet-client.

What can be seen is the class "org.jamiga2.jta.JTAConsole" telnetting into my Mac. It's really slow at the moment, but that's due to the extensive logging, and currently my Mac's login credentials are hardcoded in the Java file. And when quitting, one of the Java processes crashes due to some memory I might be freeing too many times. But besides that: it's alive!

Now what?

What's left to do, is everything else. Fix the crash, try out real SSH, fix the client from proof-of-concept status into a real SSH client, and lots of other stuff. But it's really nice to see something usable actually running.

måndag 25 mars 2013

Islands in the streams

This just in!

Update: So, I thought my problems where with the error 209, ACTION_UNKNOWN seen below in a few logs. Not so. Tonight I just realized that I'm playing around with streams that don't support stuff like available() (natively using IDOS->GetFilePosition() and such), i.e. any streaming data with no real end, like STDIN. Like, duh... Anyhow. I've solved this by calling IDOS->IsInteractive() on the file handle prior calling any un-supported functions on it.

Now, this minor "progress" hasn't solved everything, it just renders much of yesterdays ramblings regarding processes interchanging streams invalid. Much is however still valid. I'll try to remove the false parts hastily.

Continuing my Mauve testing, I'm currently occupied in trying to get one process to read data from another process' streams. In the Java world it's really easy to just send an object to any other thread and do stuff with it. Not so much in the AmigaOS 4 world. It's much the same problem I had with the sockets, where each process executes in its own context, i.e. data stream pointers are actually just pointers to internal structures valid only in the current process context.

Problems described in the context of trying to tell something about how Java works

Now, I try to write these blogs with the mixed intent of giving a powerpointy overview in combination with gory tech-details that might spark the attention of Amiga and Java coders, and at the same time getting order to my own thoughts. I sometimes get stuck in complex explanations and an overall eager to combine words in long sentences without seemingly coming to any conclusion. Sometimes I just keep it short. The text today probably requires some degree of technical knowledge. Hopefully, I will have some more real-world usage stuff to talk about in later blogs.

Starting with how Mauve executes tests

The Mauve test suite works by spawning new processes for each test to perform. When you start a Mauve test run, Mauve starts another instance of JamVM, using a simple Runtime.getRuntime().exec("jamvm RunnerProcess test") call, which in Amiga native code is realized using IExec->SystemTags(). While the test JamVM instance is running, there are a few Mauve controller threads keeping track of the test suite processes, aborting any failed or long-running tests. These controller threads talks to the tests using simple "console redirection", i.e. one programs console output decides what will happen next; the controller threads are only loops checking for data on System.in (i.e. STDIN or IDOS->Input()) and performs various tasks depending on input.

A simplified Java code example can be seen below:

01    
02    public static void main(String[] args) {
03        // Exec the other jamvm process and 
04        // set up in/out communications with it.
05        Process process = Runtime.getRuntime().
06                         exec("jamvm RunnerProcess test");
07        // Our controller thread keeping track of
08        // System.in actions
09        InputPiperThread pipe = 
10                 new InputPiperThread(
11                       System.in,
12                       process.getOutputStream());
13        // Start our InputPiperThread
14        pipe.start();
15        
16        // Do other stuff here...
17        ...
18    }
19    private static class InputPiperThread extends Thread {
20        // The controller starts by executing run 
21        public void run() {            
22            do {
23                // Check what's available on 
24                // parent's System.in
25                if (in.available() < 0) {
26                    ch = in.read();
27                    ...
28                } else {
29                    Thread.sleep(200);
30                }
31            } while (ch != -1);
32        }
33        InputStream in;
34        OutputStream out;
35        InputPiperThread(InputStream i, OutputStream o) {
36            in = i;out = o;int ch = 0;
37        }
38        
39    }
40}

The problem -- so far

The problem I had was that call to in.available() on line 25 wasn't interrupted correctly -- it just kept on going on forever. I've simplified the code above, so you can't see that the main Mauve process will eventually try to kill the InputPipeThread, and this should lead to an exception halting the thread, ending the entire process. What I noticed, was that I could not read the data at all -- there was no data to be found, my input stream (in) is actually not even a valid input stream.

Investigation begins

Before I understood what was wrong I had to see how System.in eventually is mapped to IDOS->Input(). This info gives you a good look at what actually goes around in a Java Virtual Machine and its different parts. I have a nice screenshot of it:

  1. In the center of the picture you see the first step in java.lang.System where the actual reference to STDIN is (Java coders will certainly recognize System.in)
  2. You can see it is being statically initialized to VMSystem.makeStandardInputStream(), with VMSystem being the GNU Classpath specific VM native layer.
  3. There we wrap a FileDescriptor.in in a few buffered streams.
  4. FileDescription.in is a statically initialized and wrapped version of FileChannelImpl.in, which in turn is a statically initialized to the value of VMFileChannel.getStdIn().
  5. VMFileChannel.getStdIn() is finally where we get to the actual core of the poodle: stdin_fd().
  6. The stdin_fd() function is native, so in the last step we can see the utterly simple C counterpart (Amiga C coders will recognize this part): the actual call to IDOS->Input().
So, in summary, System.in is the file handle returned by IDOS->Input() wrapped in various Java fluff.

My first thought was that something was awry with the file handle. Looking through logs however showed it wasn't so. Then, remembering my socket problems I realized that I probably can't send one Amiga process's STDIN file handle to any other Amiga process with the same ease I can send data from one Java thread to the other.

Going back to the Mauve threads and processes:

In the picture above we see the first process (marked 1) spawning a new jamvm process (number 2 in the picture). We can also see the Mauve controller threads, 3 and 4, as started by process 1. What to notice here is that to the Amiga, all four boxes represent a unique Amiga DOS Process. However, in the Java virtual machine, process 1, 3 and 4 are regarded as equals all executing in one virtual machine able to share data without much fuss. Process 2 could from the Java viewpoint of process 1 actually be any Amiga native process, like SketchBlock, OWB or whatnot, but in this particular context happens to be another jamvm instance.

Where's the Grim Reaper?

Now, anyone who's been coding on AmigaOS 4 knows that the Grim Reaper will manifest itself if any process tries to mess with privately allocated data by another process. This is also true for sockets and data stream pointers (i.e. our file handle to STDIN as returned by IDOS->Input()), but without the Grim Reaper showing up, since the pointers are only identifiers easily detected by the API as non-valid data. Looking at the logs for one of my experiments clearly show this:

Level PID Message TRACE 260 Returning stdin: 19248530 DEBUG 260 Available for file 19248530 Input: 19248530 Output:19248630 DEBUG 271 Available for file 19248530 Input: 18271650 Output:18271550 ERROR 271 IoErro reading examine data: 209 (ACTION_UNKNOWN)

What you can see is the main jamvm process with ProcessID (PID) 260, returning a file handle to System.in, STDIN (19248530), to be handed over to the controller thread (PID 271). The next line is again process 260 successfully checking for available data on STDIN. And, on the third line you see the controller thread 271 trying the exact same thing, but on the fourth line evidently failing. (The numbers after Input and Output on the second and third line are what IDOS->Input/Output, returns in each process, just to be sure we have the pointer we want.)

And, the solution?

This perhaps doesn't come as a surprise to knowledgeable Amiga programmers (I'm also not surprised), but it does take a while to come to this conclusion given the number of things that can be wrong.

I haven't yet come to a solution, but i think it can involve using a PIPE:. Looking again at the Java example above, around line 12 we actually have a PIPE: in use under the bonnet:

05        Process process = Runtime.getRuntime().
06                         exec("jamvm RunnerProcess test");
07        // Our controller thread keeping track of
08        // System.in actions
09        InputPiperThread pipe = 
10                 new InputPiperThread(
11                       System.in,
12                       process.getOutputStream());
When getting the output and input stream from a Java spawned Amiga process I am already using Amiga pipes, and this works without problems. Now I only need to open a new PIPE: and then pipe the real IDOS->Input() to it. But I don't yet know how to do it.

tisdag 29 januari 2013

The colour Mauve

As briefly mentioned in my previous entry, trying anything more fancy than sending a little data over the network, causes JamVM to wildly crash. I've yet not found the reason for this, it only happens for one particular example (that I've come across) and I can't find any obvious cause for it. It happens right after JamVM has actually ended, and the crash is according to Grim Reaper caused by the process of name "", which milliseconds before was named "jamvm.exe". I'm guessing it has something to do with the C-library trying to close/free something that I've already freed, or something like that. One upside of this problem is that I finally got around to sorting out my issues regarding which C-library I'm using.

JamVM currently uses clib2

So, there, I've said it. Its in black and white. I'm using clib2, and I'm sticking to it. I find that I constantly ask myself about design decisions I've made, and wondering both when, and why I came up with a certain solution. I will not use newlib, for the simple reason that I just wrote that "JamVM currently uses clib2". The JNI libraries (java-net, java-io, etc) should preferably use no C-library, or clib2 when needed. Being libraries, we're not using the C-library startup code. Some libraries however dependes on the reference GNU Classpath implementation, which uses c-library functions. So, in some cases it is more convenient to use a C-library.

I'm not entirely one-hundred percent sure that you can mix different c-libraries in your shared libraries, but I really do not see why not. As long as you don't try to make them talk to each other by interchanging implementation specific handles like sockets or such. But I see no reason why one library calling clib2's printf should interfere with another library calling newlib's printf. Steven Solie has a few good pointers on AmigaOS 4 programming in general, and specifically for networking. He also mentions that clib2 is open-source, and therefore a good choice in terms of debugging options. Which I believe it will prove to be, if I don't stumble upon my problem by accident.

What about that colour?

My previously mentioned doodling with mauve (the test suite for JVM's) continues. Instead of hopelessly banging my head against the keyboard, I thought it wise to continue my mauve runnings. With that I might be able to pinpoint the cause of crash more accurately, instead of wading through hierarchies of Java classes. And that is also one step closer to finishing the bounty.

Note to self

Like I said, my memory fails me. So I thought I'd write down how I run mauve. And it isn't that complicated. I have a mauve installation on my DH1, and it's only a matter of putting it in the classpath to be able to run the "Harness" class. However, putting stuff in the classpath should be as easy as setting an environment variable. But clib2's getenv implementation only has a buffer size of 256 bytes, which means that my scattered long windling classpath doesn't fit. Instead of solving the problem, I hardcoded the classpath. Fugly. But it works. Anyhow, with that in place, starting mauve is as easy as:

jamvm.exe -vm=jamvm.exe Harness java.lang.String

The "java.lang.String" reference is the test to actually run. Not entering this will run all tests. Entering something like java.* will run anything in the "java" package.
Edit: Just remembered that you need to say which VM you want to test. If none is specified, it uses "java", which in my particular case will execute the original JAmiga VM, and not JamVM.

Finding bugs as we speak!

Trying to run mauve, and simultaneously writing this blog entry, I'm actually bumping over a bug of "getenv" in java.lang.VMSystem not being linked correctly. How coincidental. Which concludes my rambling for the night. Off to get that bug!

onsdag 16 januari 2013

Jamiga net fishing

I've had some more progress with the networking. Instead of boring you with text about it, I thought I'd show you a clip of the progress and give an insight in how a day coding JAmiga might look.

Enjoy:

Well, its almost like that.

Anyhow, what the clip tries to illustrate is the Amiga running a Java program opening a server socket on port 7777, with my Mac telnetting to that port, and then exchanging a few strings. You can see a more academic documentary version below:

The current running network solution will most likely only work in single threaded environments, since using bsdsockets.library in multiple processes requires some extra care. My previous described "one-amiga-process-for-each-socket approach is currently disabled. There were other issues with that solution. I haven't yet decided how to do it, but starting an extra process for each socket is really not a satisfying solution -- it can be sufficient to get past the obstacle, but in the long run its really not a good way to do it.

What's next?

Next, I'll focus on getting one of the Milestone 1 possible applications to work, namely the Telnet/SSH client. At the moment, the whole thing crashes wildly, but I'm assuming its a simple mistake I've made.