map-server: Implement altos-map in C
authorKeith Packard <keithp@keithp.com>
Sun, 7 Oct 2018 16:22:44 +0000 (09:22 -0700)
committerKeith Packard <keithp@keithp.com>
Sun, 7 Oct 2018 16:38:43 +0000 (09:38 -0700)
This app is all about startup time, and java starts
slowly. Re-implement in C to improve server performance.

Signed-off-by: Keith Packard <keithp@keithp.com>
14 files changed:
configure.ac
map-server/Makefile.am
map-server/altos-map/.gitignore
map-server/altos-map/AltosMap.java [deleted file]
map-server/altos-map/Makefile.am
map-server/altos-map/Manifest.txt [deleted file]
map-server/altos-map/altos-map-fake [deleted file]
map-server/altos-map/altos-map.c [new file with mode: 0644]
map-server/altos-map/altos-mapc-fake [new file with mode: 0755]
map-server/altos-mapj/.gitignore [new file with mode: 0644]
map-server/altos-mapj/AltosMap.java [new file with mode: 0644]
map-server/altos-mapj/Makefile.am [new file with mode: 0644]
map-server/altos-mapj/Manifest.txt [new file with mode: 0644]
map-server/altos-mapj/altos-mapj-fake [new file with mode: 0755]

index dae2499d7ea4b23058e65af2a2fad0d288376b85..6e02f968e320ccc40a32293f5df2b6636e7ffe11 100644 (file)
@@ -417,6 +417,8 @@ if test "x$HAVE_NICKLE" = "xno"; then
        AC_MSG_ERROR([Please install nickle to build AltOs])
 fi
 
+PKG_CHECK_MODULES([JANSSON], [jansson])
+
 AC_ARG_WITH([readline],
            [AS_HELP_STRING([--with-readline],
                              [enable readline functionality in ao-dbg @<:@default=auto@:>@])],
@@ -574,6 +576,7 @@ ao-utils/Makefile
 map-server/Makefile
 map-server/altos-mapd/Makefile
 map-server/altos-map/Makefile
+map-server/altos-mapj/Makefile
 src/Version
 ])
 
index f9b8a727b098b33ed6373f84b1de5afb9c021e82..39ff80142a4faf4c5423a02c8eb9850a5df888e6 100644 (file)
@@ -1 +1 @@
-SUBDIRS=altos-mapd altos-map
+SUBDIRS=altos-mapd altos-map altos-mapj
index ea012eef05ef3940f377bc6c770d3b059c9d0375..1158b56afcc3dc6903c473740ec622db35347458 100644 (file)
@@ -1,6 +1 @@
 altos-map
-altos-map-jdb
-altos-map-test
-*.jar
-*.stamp
-classes
diff --git a/map-server/altos-map/AltosMap.java b/map-server/altos-map/AltosMap.java
deleted file mode 100644 (file)
index ad0a4f5..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright © 2018 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- */
-
-package altosmap;
-
-import java.net.*;
-import java.io.*;
-import java.util.*;
-import java.text.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-public class AltosMap {
-
-       public final static int port = 16717;
-
-       public final static String protocol_version = "1.0.0";
-
-       String  query_string;
-       String  remote_addr;
-
-       public String reason_string(int code) {
-               switch (code) {
-               case 200:
-                       return "OK";
-               case 400:
-                       return "Bad Request";
-               case 403:
-                       return "Forbidden";
-               case 404:
-                       return "Not Found";
-               case 408:
-                       return "Request Timeout";
-               default:
-                       return "Failure";
-               }
-       }
-
-       public void write_status(int status) {
-               System.out.printf("Status: %d %s\n", status, reason_string(status));
-       }
-
-       public void write_type(String type) {
-               System.out.printf("Content-Type: %s\n", type);
-       }
-
-       public void fail(int status, String reason) {
-               write_status(status);
-               write_type("text/html");
-               System.out.printf("\n");
-               System.out.printf("<html>\n");
-               System.out.printf("<head><title>Map Fetch Failure</title></head>\n");
-               System.out.printf("<body>%s</body>\n", reason);
-               System.out.printf("</html>\n");
-               System.exit(1);
-       }
-
-       public void process() {
-               query_string = System.getenv("QUERY_STRING");
-
-               if (query_string == null)
-                       fail(400, "Missing query string");
-
-               remote_addr = System.getenv("REMOTE_ADDR");
-
-               if (remote_addr == null)
-                       fail(400, "Missing remote address");
-
-               String[] queries = query_string.split("&");
-
-               double  lon = AltosLib.MISSING;
-               double  lat = AltosLib.MISSING;
-               int     zoom = AltosLib.MISSING;
-               String  version = null;
-
-               try {
-                       for (String query : queries) {
-                               String[] q = query.split("=");
-                               if (q.length >= 2) {
-                                       String name = q[0];
-                                       String value = q[1];
-                                       if (name.equals("lon"))
-                                               lon = AltosParse.parse_double_net(value);
-                                       else if (name.equals("lat"))
-                                               lat = AltosParse.parse_double_net(value);
-                                       else if (name.equals("zoom"))
-                                               zoom = AltosParse.parse_int(value);
-                                       else if (name.equals("version"))
-                                               version = value;
-                                       else
-                                               fail(400, String.format("Extra query param \"%s\"", query));
-                               }
-                       }
-               } catch (ParseException pe) {
-                       fail(400, String.format("Invalid query: %s", pe.toString()));
-               }
-
-               if (version != null) {
-                       System.out.printf("Content-Type: text/plain\n");
-                       System.out.printf("\n");
-                       System.out.printf("%s\n", protocol_version);
-               } else {
-                       if (lon == AltosLib.MISSING)
-                               fail(400, "Missing longitude");
-                       if (lat == AltosLib.MISSING)
-                               fail(400, "Missing latitude");
-                       if (zoom == AltosLib.MISSING)
-                               fail(400, "Missing zoom");
-
-                       try {
-                               Socket  socket = null;
-                               int tries = 0;
-
-                               while (tries < 10 && socket == null) {
-                                       try {
-                                               socket = new Socket(InetAddress.getLoopbackAddress(), port);
-                                       } catch (IOException ie) {
-                                               Thread.sleep(100);
-                                               tries++;
-                                       }
-                               }
-
-                               AltosJson       request = new AltosJson();
-
-                               request.put("lat", lat);
-                               request.put("lon", lon);
-                               request.put("zoom", zoom);
-                               request.put("remote_addr", remote_addr);
-
-                               Writer writer = new PrintWriter(socket.getOutputStream());
-                               request.write(writer);
-                               writer.flush();
-
-                               AltosJson       reply = AltosJson.fromInputStream(socket.getInputStream());
-
-                               int status = reply.get_int("status", 400);
-
-                               if (status != 200)
-                                       fail(status, "Bad cache status");
-
-                               String filename = reply.get_string("filename", null);
-                               try {
-                                       File file = new File(filename);
-                                       long length = file.length();
-                                       FileInputStream in = new FileInputStream(file);
-                                       String content_type = reply.get_string("content_type", null);
-                                       System.out.printf("Content-Type: %s\n", content_type);
-                                       System.out.printf("Content-Length: %d\n", file.length());
-                                       System.out.printf("\n");
-                                       byte[] buf = new byte[4096];
-                                       int bytes_read;
-                                       while ((bytes_read = in.read(buf)) > 0)
-                                               System.out.write(buf);
-                               } catch (IOException ie) {
-                                       fail(404, String.format("IO Exception: %s", ie.toString()));
-                               }
-                       } catch (Exception e) {
-                               fail(404, String.format("Exception %s", e.toString()));
-                       }
-               }
-       }
-
-       public AltosMap() {
-       }
-
-       public static void main(final String[] args) {
-
-               new AltosMap().process();
-
-       }
-}
index f7c9bdc8523d40fccd731365c776cb06e2cb2185..6925957b537113982e539f431401ef72eb429e88 100644 (file)
@@ -1,62 +1,6 @@
-JAVAROOT=classes
-AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6
+bin_PROGRAMS = altos-map
 
-altoslibdir=$(libdir)/altos
-
-CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="$(JAVAROOT):../../altoslib/*"
-
-bin_SCRIPTS=altos-map
-
-altosmap_JAVA = \
-       AltosMap.java
-
-ALTOSLIB_CLASS=\
-       altoslib_$(ALTOSLIB_VERSION).jar
-
-JAR=altosmap.jar
-
-FATJAR=altosmap-fat.jar
-
-all-local: classes/altosmap $(JAR) altos-map altos-map-test altos-map-jdb
-
-install-altosmapJAVA: altosmap.jar
-       @$(NORMAL_INSTALL)
-       test -z "$(altosmapdir)" || $(MKDIR_P) "$(DESTDIR)$(altosmapdir)"
-       echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(altosmapdir)/altosmap.jar'"; \
-       $(INSTALL_DATA) "$<" "$(DESTDIR)$(altosmapdir)"
-
-classes/altosmap:
-       mkdir -p classes/altosmap
-
-$(JAR): classaltosmap.stamp Manifest.txt $(ALTOSLIB_CLASS)
-       jar cfm $@ Manifest.txt \
-               -C classes altosmap
-
-altosmapdir=$(datadir)/java
-
-$(FATJAR): classaltosmap.stamp Manifest-fat.txt $(ALTOSLIB_CLASS)
-       jar cfm $@ Manifest-fat.txt \
-               -C classes altosmap
-
-altos-map: Makefile
-       echo "#!/bin/sh" > $@
-       echo 'exec java -Djava.library.path="$(altoslibdir)" -jar "$(altosmapdir)/altosmap.jar" "$$@"' >> $@
-       chmod +x $@
-
-altos-map-test: Makefile
-       echo '#!/bin/sh' > $@
-       echo 'dir="$$(dirname $$0)"' >> $@
-       echo 'cd "$$dir"' >> $@
-       echo 'altosmap="$$(pwd -P)"' >> $@
-       echo 'exec java -jar "$$altosmap/altosmap.jar" "$$@"' >> $@
-       chmod +x $@
-
-altos-map-jdb: Makefile
-       echo "#!/bin/sh" > $@
-       echo 'exec jdb altosmap/AltosMap "$$@"' >> $@
-       chmod +x $@
-
-$(ALTOSLIB_CLASS):
-       -rm -f "$@"
-       $(LN_S) ../../altoslib/"$@" .
+altos_map_SOURCES = altos-map.c
 
+altos_map_LDADD = $(JANSSON_LIBS)
+altos_map_CFLAGS = $(JANSSON_CFLAGS) $(WARN_CFLAGS)
diff --git a/map-server/altos-map/Manifest.txt b/map-server/altos-map/Manifest.txt
deleted file mode 100644 (file)
index 1a285b4..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-Main-Class: altosmap.AltosMap
-Class-Path: altoslib_13.jar
diff --git a/map-server/altos-map/altos-map-fake b/map-server/altos-map/altos-map-fake
deleted file mode 100755 (executable)
index a78bbd6..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-# map-N43.799102,W120.586281-hybrid-20.jpg
-export QUERY_STRING="lat=43.799102&lon=-120.586281&zoom=20"
-export REMOTE_ADDR="127.0.0.1"
-./altos-map-test
-
diff --git a/map-server/altos-map/altos-map.c b/map-server/altos-map/altos-map.c
new file mode 100644 (file)
index 0000000..690ec72
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <jansson.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+
+#define ALTOS_MAP_PORT 16717
+
+#define MISSING                0x7fffffff
+
+#define ALTOS_MAP_PROTOCOL_VERSION     "1.0.0"
+
+static char *
+reason_string(int code)
+{
+       switch (code) {
+       case 200:
+               return "OK";
+       case 400:
+               return "Bad Request";
+       case 403:
+               return "Forbidden";
+       case 404:
+               return "Not Found";
+       case 408:
+               return "Request Timeout";
+       default:
+               return "Failure";
+       }
+}
+
+static void
+write_status(int status)
+{
+       printf("Status: %d %s\n", status, reason_string(status));
+}
+
+static void
+write_type(char * type)
+{
+       printf("Content-Type: %s\n", type);
+}
+
+static void
+fail(int status, char *format, ...)
+{
+       va_list ap;
+
+       write_status(status);
+       write_type("text/html");
+       printf("\n");
+       printf("<html>\n");
+       printf("<head><title>Map Fetch Failure</title></head>\n");
+       printf("<body>\n");
+       va_start(ap, format);
+       vprintf(format, ap);
+       va_end(ap);
+       printf("</body>\n");
+       printf("</html>\n");
+       exit(1);
+}
+
+static char *
+getenv_copy(const char *name)
+{
+       const char *value = getenv(name);
+
+       if (!value)
+               return NULL;
+
+       return strdup(value);
+}
+
+static double
+parse_double(char *string)
+{
+       char *end;
+       double value;
+
+       value = strtod(string, &end);
+       if (*end)
+               fail(400, "Invalid double %s", string);
+       return value;
+}
+
+static int
+parse_int(char *string)
+{
+       char *end;
+       long int value;
+
+       value = strtol(string, &end, 10);
+       if (*end)
+               fail(400, "Invalid int %s", string);
+       if (value < INT_MIN || INT_MAX < value)
+               fail(400, "Int value out of range %ld", value);
+       return (int) value;
+}
+
+
+static int
+connect_service(void)
+{
+       struct sockaddr_in      altos_map_addr = {
+               .sin_family = AF_INET,
+               .sin_port = htons(ALTOS_MAP_PORT),
+               .sin_addr = {
+                       .s_addr = htonl(INADDR_LOOPBACK),
+               },
+       };
+
+       int     s = socket(AF_INET, SOCK_STREAM, 0);
+
+       if (s < 0)
+               return -1;
+
+       if (connect (s, (const struct sockaddr *) &altos_map_addr, sizeof (altos_map_addr)) < 0) {
+               close (s);
+               return -1;
+       }
+
+       return s;
+}
+
+int main(int argc, char **argv)
+{
+
+       char *query_string = getenv_copy("QUERY_STRING");
+
+       if (query_string == NULL)
+               fail(400, "%s", "Missing query string");
+
+       char *remote_addr = getenv_copy("REMOTE_ADDR");
+
+       if (remote_addr == NULL)
+               fail(400, "%s", "Missing remote address");
+
+       double  lon = MISSING;
+       double  lat = MISSING;
+       int     zoom = MISSING;
+       char    *version = NULL;
+
+       char    *query, *query_save = NULL;
+       char    *query_start = query_string;
+
+       while ((query = strtok_r(query_start, "&", &query_save)) != NULL) {
+               query_start = NULL;
+
+               char    *token, *token_save = NULL;
+               char    *token_start = query;
+
+               char    *name = NULL;
+               char    *value = NULL;
+
+               while ((token = strtok_r(token_start, "=", &token_save)) != NULL) {
+                       token_start = NULL;
+                       if (name == NULL)
+                               name = token;
+                       else if (value == NULL)
+                               value = token;
+                       else
+                               break;
+               }
+
+               if (name && value) {
+                       if (!strcmp(name, "lon"))
+                               lon = parse_double(value);
+                       else if (!strcmp(name, "lat"))
+                               lat = parse_double(value);
+                       else if (!strcmp(name, "zoom"))
+                               zoom = parse_int(value);
+                       else if (!strcmp(name, "version"))
+                               version = value;
+                       else
+                               fail(400, "Extra query param \"%s\"", query);
+               }
+       }
+
+       if (version != NULL) {
+               printf("Content-Type: text/plain\n");
+               printf("\n");
+               printf("%s\n", ALTOS_MAP_PROTOCOL_VERSION);
+               return 0;
+       }
+       if (lon == MISSING)
+               fail(400, "Missing longitude");
+       if (lat == MISSING)
+               fail(400, "Missing latitude");
+       if (zoom == MISSING)
+               fail(400, "Missing zoom");
+
+       int     s = -1;
+       int     tries = 0;
+
+       while (tries < 10 && s < 0) {
+               s = connect_service();
+               if (s < 0) {
+                       usleep(100 * 1000);
+                       tries++;
+               }
+       }
+
+       if (s < 0)
+               fail(408, "Cannot connect AltOS map daemon");
+
+       json_t *request = json_pack("{s:f s:f s:i s:s}", "lat", lat, "lon", lon, "zoom", zoom, "remote_addr", remote_addr);
+
+       if (request == NULL)
+               fail(400, "Cannot create JSON request");
+
+       if (json_dumpfd(request, s, 0) < 0)
+               fail(400, "Cannot write JSON request");
+
+       json_error_t    error;
+       json_t          *reply = json_loadfd(s, 0, &error);
+
+       if (!reply)
+               fail(400, "Cannot read JSON reply");
+
+       int     status;
+
+       if (json_unpack(reply, "{s:i}", "status", &status) < 0)
+               fail(400, "No status returned");
+
+       if (status != 200)
+               fail(status, "Bad cache status");
+
+       char    *filename, *content_type;
+
+       if (json_unpack(reply, "{s:s s:s}", "filename", &filename, "content_type", &content_type) < 0)
+               fail(400, "JSON reply parse failure");
+
+       int     fd = open(filename, O_RDONLY);
+
+       if (fd < 0)
+               fail(400, "%s: %s", filename, strerror(errno));
+
+       struct stat     statb;
+
+       if (fstat(fd, &statb) < 0)
+               fail(400, "%s: %s", filename, strerror(errno));
+
+       printf("Content-Type: %s\n", content_type);
+       printf("Content-Length: %lu\n", (unsigned long) statb.st_size);
+       printf("\n");
+       fflush(stdout);
+
+       char    buf[4096];
+       ssize_t bytes_read;
+
+       while ((bytes_read = read(fd, buf, sizeof (buf))) > 0) {
+               ssize_t total_write = 0;
+               while (total_write < bytes_read) {
+                       ssize_t bytes_write = write(1, buf + total_write, bytes_read - total_write);
+                       if (bytes_write <= 0)
+                               return 1;
+                       total_write += bytes_write;
+               }
+       }
+       if (bytes_read < 0)
+               return 1;
+       return 0;
+}
diff --git a/map-server/altos-map/altos-mapc-fake b/map-server/altos-map/altos-mapc-fake
new file mode 100755 (executable)
index 0000000..9e22f76
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+# map-N43.799102,W120.586281-hybrid-20.jpg
+export QUERY_STRING="lat=43.799102&lon=-120.586281&zoom=20"
+export REMOTE_ADDR="127.0.0.1"
+./altos-mapc
+
diff --git a/map-server/altos-mapj/.gitignore b/map-server/altos-mapj/.gitignore
new file mode 100644 (file)
index 0000000..c5d593e
--- /dev/null
@@ -0,0 +1,6 @@
+altos-mapj
+altos-mapj-jdb
+altos-mapj-test
+*.jar
+*.stamp
+classes
diff --git a/map-server/altos-mapj/AltosMap.java b/map-server/altos-mapj/AltosMap.java
new file mode 100644 (file)
index 0000000..ad0a4f5
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+package altosmap;
+
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+public class AltosMap {
+
+       public final static int port = 16717;
+
+       public final static String protocol_version = "1.0.0";
+
+       String  query_string;
+       String  remote_addr;
+
+       public String reason_string(int code) {
+               switch (code) {
+               case 200:
+                       return "OK";
+               case 400:
+                       return "Bad Request";
+               case 403:
+                       return "Forbidden";
+               case 404:
+                       return "Not Found";
+               case 408:
+                       return "Request Timeout";
+               default:
+                       return "Failure";
+               }
+       }
+
+       public void write_status(int status) {
+               System.out.printf("Status: %d %s\n", status, reason_string(status));
+       }
+
+       public void write_type(String type) {
+               System.out.printf("Content-Type: %s\n", type);
+       }
+
+       public void fail(int status, String reason) {
+               write_status(status);
+               write_type("text/html");
+               System.out.printf("\n");
+               System.out.printf("<html>\n");
+               System.out.printf("<head><title>Map Fetch Failure</title></head>\n");
+               System.out.printf("<body>%s</body>\n", reason);
+               System.out.printf("</html>\n");
+               System.exit(1);
+       }
+
+       public void process() {
+               query_string = System.getenv("QUERY_STRING");
+
+               if (query_string == null)
+                       fail(400, "Missing query string");
+
+               remote_addr = System.getenv("REMOTE_ADDR");
+
+               if (remote_addr == null)
+                       fail(400, "Missing remote address");
+
+               String[] queries = query_string.split("&");
+
+               double  lon = AltosLib.MISSING;
+               double  lat = AltosLib.MISSING;
+               int     zoom = AltosLib.MISSING;
+               String  version = null;
+
+               try {
+                       for (String query : queries) {
+                               String[] q = query.split("=");
+                               if (q.length >= 2) {
+                                       String name = q[0];
+                                       String value = q[1];
+                                       if (name.equals("lon"))
+                                               lon = AltosParse.parse_double_net(value);
+                                       else if (name.equals("lat"))
+                                               lat = AltosParse.parse_double_net(value);
+                                       else if (name.equals("zoom"))
+                                               zoom = AltosParse.parse_int(value);
+                                       else if (name.equals("version"))
+                                               version = value;
+                                       else
+                                               fail(400, String.format("Extra query param \"%s\"", query));
+                               }
+                       }
+               } catch (ParseException pe) {
+                       fail(400, String.format("Invalid query: %s", pe.toString()));
+               }
+
+               if (version != null) {
+                       System.out.printf("Content-Type: text/plain\n");
+                       System.out.printf("\n");
+                       System.out.printf("%s\n", protocol_version);
+               } else {
+                       if (lon == AltosLib.MISSING)
+                               fail(400, "Missing longitude");
+                       if (lat == AltosLib.MISSING)
+                               fail(400, "Missing latitude");
+                       if (zoom == AltosLib.MISSING)
+                               fail(400, "Missing zoom");
+
+                       try {
+                               Socket  socket = null;
+                               int tries = 0;
+
+                               while (tries < 10 && socket == null) {
+                                       try {
+                                               socket = new Socket(InetAddress.getLoopbackAddress(), port);
+                                       } catch (IOException ie) {
+                                               Thread.sleep(100);
+                                               tries++;
+                                       }
+                               }
+
+                               AltosJson       request = new AltosJson();
+
+                               request.put("lat", lat);
+                               request.put("lon", lon);
+                               request.put("zoom", zoom);
+                               request.put("remote_addr", remote_addr);
+
+                               Writer writer = new PrintWriter(socket.getOutputStream());
+                               request.write(writer);
+                               writer.flush();
+
+                               AltosJson       reply = AltosJson.fromInputStream(socket.getInputStream());
+
+                               int status = reply.get_int("status", 400);
+
+                               if (status != 200)
+                                       fail(status, "Bad cache status");
+
+                               String filename = reply.get_string("filename", null);
+                               try {
+                                       File file = new File(filename);
+                                       long length = file.length();
+                                       FileInputStream in = new FileInputStream(file);
+                                       String content_type = reply.get_string("content_type", null);
+                                       System.out.printf("Content-Type: %s\n", content_type);
+                                       System.out.printf("Content-Length: %d\n", file.length());
+                                       System.out.printf("\n");
+                                       byte[] buf = new byte[4096];
+                                       int bytes_read;
+                                       while ((bytes_read = in.read(buf)) > 0)
+                                               System.out.write(buf);
+                               } catch (IOException ie) {
+                                       fail(404, String.format("IO Exception: %s", ie.toString()));
+                               }
+                       } catch (Exception e) {
+                               fail(404, String.format("Exception %s", e.toString()));
+                       }
+               }
+       }
+
+       public AltosMap() {
+       }
+
+       public static void main(final String[] args) {
+
+               new AltosMap().process();
+
+       }
+}
diff --git a/map-server/altos-mapj/Makefile.am b/map-server/altos-mapj/Makefile.am
new file mode 100644 (file)
index 0000000..f447adb
--- /dev/null
@@ -0,0 +1,62 @@
+JAVAROOT=classes
+AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6
+
+altoslibdir=$(libdir)/altos
+
+CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="$(JAVAROOT):../../altoslib/*"
+
+bin_SCRIPTS=altos-mapj
+
+altosmap_JAVA = \
+       AltosMap.java
+
+ALTOSLIB_CLASS=\
+       altoslib_$(ALTOSLIB_VERSION).jar
+
+JAR=altosmap.jar
+
+FATJAR=altosmap-fat.jar
+
+all-local: classes/altosmap $(JAR) altos-mapj altos-mapj-test altos-mapj-jdb
+
+install-altosmapJAVA: altosmap.jar
+       @$(NORMAL_INSTALL)
+       test -z "$(altosmapdir)" || $(MKDIR_P) "$(DESTDIR)$(altosmapdir)"
+       echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(altosmapdir)/altosmap.jar'"; \
+       $(INSTALL_DATA) "$<" "$(DESTDIR)$(altosmapdir)"
+
+classes/altosmap:
+       mkdir -p classes/altosmap
+
+$(JAR): classaltosmap.stamp Manifest.txt $(ALTOSLIB_CLASS)
+       jar cfm $@ Manifest.txt \
+               -C classes altosmap
+
+altosmapdir=$(datadir)/java
+
+$(FATJAR): classaltosmap.stamp Manifest-fat.txt $(ALTOSLIB_CLASS)
+       jar cfm $@ Manifest-fat.txt \
+               -C classes altosmap
+
+altos-mapj: Makefile
+       echo "#!/bin/sh" > $@
+       echo 'exec java -Djava.library.path="$(altoslibdir)" -jar "$(altosmapdir)/altosmap.jar" "$$@"' >> $@
+       chmod +x $@
+
+altos-mapj-test: Makefile
+       echo '#!/bin/sh' > $@
+       echo 'dir="$$(dirname $$0)"' >> $@
+       echo 'cd "$$dir"' >> $@
+       echo 'altosmap="$$(pwd -P)"' >> $@
+       echo 'exec java -jar "$$altosmap/altosmap.jar" "$$@"' >> $@
+       chmod +x $@
+
+altos-mapj-jdb: Makefile
+       echo "#!/bin/sh" > $@
+       echo 'exec jdb altosmap/AltosMap "$$@"' >> $@
+       chmod +x $@
+
+$(ALTOSLIB_CLASS):
+       -rm -f "$@"
+       $(LN_S) ../../altoslib/"$@" .
+
diff --git a/map-server/altos-mapj/Manifest.txt b/map-server/altos-mapj/Manifest.txt
new file mode 100644 (file)
index 0000000..1a285b4
--- /dev/null
@@ -0,0 +1,2 @@
+Main-Class: altosmap.AltosMap
+Class-Path: altoslib_13.jar
diff --git a/map-server/altos-mapj/altos-mapj-fake b/map-server/altos-mapj/altos-mapj-fake
new file mode 100755 (executable)
index 0000000..f600fec
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+# map-N43.799102,W120.586281-hybrid-20.jpg
+export QUERY_STRING="lat=43.799102&lon=-120.586281&zoom=20"
+export REMOTE_ADDR="127.0.0.1"
+./altos-mapj-test
+