Showing posts with label Web. Show all posts
Showing posts with label Web. Show all posts

Wednesday, 15 January 2014

Getting Started with Clojure

I'm starting to enjoy using Clojure - the Lisp built on top of Java if you like. Great set of libraries, available and relevant documentation, a good base of users and plenty of examples.

I admit I'm already familiar with the functional programming style and have been for many years since discovering ML, Hope and Gofer many, many years ago, so I guess moving back to the one true language - Lisp - isn't so difficult, and anyway I don't want to talk about functional programming.

As everything seems to be about web based services, it tends to always be an quick experiment for me to build a simple web based service to get to grips with a language and its support. I must admit I loved Opa, but was let down by the support; Ruby on Rails I never really liked and let's not discuss Java and all its frameworks. Anyway, Clojure has been getting a lot of attention and a couple of colleagues of mine have had some very favourable things to say about it (one is a Lisp-fanatic however...) So here goes.

First go get Leiningen, the rather cool Clojure project automator. Dump that in a place where everyone can see it. I'm using Debian 7, so it goes in /usr/local/bin for me. I assume you're already familiar with things like this. Create a new Clojure project, which creates a whole bunch of directories under ./test
ian@deb1:~$ lein new test
Generating a project called test based on the 'default' template.
To see other templates (app, lein plugin, etc), try `lein help new`.
ian@
ian@deb1:~$ ls -l test
total 36
drwxr-xr-x 2 ian ian  4096 Jan 15 10:52 doc
-rw-r--r-- 1 ian ian 11220 Jan 15 10:52 LICENSE
-rw-r--r-- 1 ian ian   264 Jan 15 10:52 project.clj
-rw-r--r-- 1 ian ian   230 Jan 15 10:52 README.md
drwxr-xr-x 2 ian ian  4096 Jan 15 10:52 resources
drwxr-xr-x 3 ian ian  4096 Jan 15 10:52 src
drwxr-xr-x 3 ian ian  4096 Jan 15 10:52 test
So what we're going to do is write a small service that queries a database from a call over HTTP or via a browser.

First however, we need to make sure we've a database somewhere. I'm using MariaDB, you could use MySQL or any other RBMS system, but you'll need to check some specifics on how to connect. I've created a database called testdb, a single table and populated it with some data. You'll need to use whatever tools your database provides to do this, but for MariaDB (and MySQL I guess) it is the following:
ian@ian@deb1:~/dbtest$ mysql -u root -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 28
Server version: 5.5.34-MariaDB-1~wheezy-log mariadb.org binary distribution
Copyright (c) 2000, 2013, Oracle, Monty Program Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> create database fred;
Query OK, 1 row affected (0.00 sec)
MariaDB [(none)]> use "fred";
Database changed
MariaDB [fred]> create table ids (id integer, name varchar(50) );
Query OK, 0 rows affected (0.17 sec)
MariaDB [fred]> insert into ids values (1,"Alice");
Query OK, 1 row affected (0.02 sec)
MariaDB [fred]> insert into ids values (2,"Bob");
Query OK, 1 row affected (0.01 sec)
MariaDB [fred]> insert into ids values (3,"Eve");
Query OK, 1 row affected (0.01 sec)
MariaDB [fred]> commit;
Query OK, 0 rows affected (0.00 sec)
Aside: yes, I should be shot for using the database root user and not bothering about security...agreed! Don't do it and go read up on such things.

Now the two important files are project.clj and core.clj ... use your favourite text editor to open these:
ian@ian@deb1:~$ cd dbtest
ian@
ian@deb1:~/dbtest$ gedit project.clj src/dbtest/core.clj &
Edit the file project.clj so it looks like this:
(defproject dbtest "v0.1 DBTest"
  :description "Simple Test Application"
  :url "http://http://ijosblog.blogspot.fi/"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/java.jdbc "0.2.3"]
                 [compojure "1.1.6"]
                 [mysql/mysql-connector-java "5.1.18"]]
  :plugins [[lein-ring "0.8.10"]]
  :ring {:handler dbtest.core/app }
  :profiles {:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
                        [ring-mock "0.1.5"]]}}
)
Don't worry about the description, url and license properties or the name of the project. The important things here are the dependencies, plugins, profiles and ring.

These describe the packages that we'll be including in our project, think of them as #includes in C/C++. What we're stating here is that we require Clojure version 1.5.1, Java JDBC , the MySQL connector (works with MariaDB) and a package called Compojure which will handle requests. Additionally we have a plugin ca lled lein-ring which handles the low-level details of HTTP calls plus provides a server to receive the calls. The line stating with :ring states the function that will be called on the start up of the application. The specifics aren't too important at the moment, we can learn about that later.

The actual code that we will run is held in the core.clj file by default. There are clues to Clojure's structuring and packaging mechanisms in the above code and in the directory structure. Firstly the we define the namespace and the packages we wish to include:
(ns dbtest.core  (:use [compojure.core]        [clojure.test] )      (:require [compojure.handler :as handler]            [compojure.route :as route] ))(use 'clojure.java.jdbc)
Then we define the function helloworld:
(defn helloworld []
  "Hello World")
and the functions that handle requests from the client
(defroutes app-routes
  (GET "/" [] ( helloworld ))
  (route/resources "/")
  (route/not-found "Not Found"))
(def app
  (handler/site app-routes))
And that is effectively everything we need to do to get something running. But before you do run, it is necessary to make sure we have all the libraries installed and lein does this for us by consulting the project.clj file, so run:
ian@ian@deb1:~/dbtest$ lein deps
and let it download everything you need. You generally only have to do ths when you add libraries to the system. Once that has completed (there'll be lots of messages about downloads, or nothing if you've already got everything) start the server:
ian@ian@deb1:~/dbtest$ lein ring server
2014-01-15 11:12:17.221:INFO:oejs.Server:jetty-7.6.8.v20121106
2014-01-15 11:12:17.345:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:3000
Started server on port 3000
A browser window should pop up, but if not start your favourity browser, eg: firefox and point it at http://127.0.0.1:3000 and you should get a page starting Hello World. NB: 127.0.0.1 always resolves to the local machine, so if you have the browser running on a seperate machine (virtual or not) then make sure you use the correct IP address. For the brave you can always use curl to get the body and headers by running the following in a seperate terminal window:
ian@ian@deb1:~/dbtest$ curl http://127.0.0.1:3000
Hello World
ian@
ian@deb1:~/dbtest$ curl -I  http://127.0.0.1:3000
HTTP/1.1 200 OK
Date: Wed, 15 Jan 2014 09:19:09 GMT
Content-Type: text/html;charset=UTF-8
Content-Length: 0
Server: Jetty(7.6.8.v20121106)
After you're happy, kill the server with ctrl-c.

Let's now connect to the database by adding the following code between the (ns...) and (use...) and our hello world function:
(let [db-host "localhost"
      db-port 3306
      db-name "testdb"]

  (def db {:classname "com.mysql.jdbc.Driver" ; must be in classpath
           :subprotocol "mysql"
           :subname (str "//" db-host ":" db-port "/" db-name)
           ; Any additional keys are passed to the driver
           ; as driver-specific properties.
           :user "root"
           :password "badpassword"}))
Firstly localhost is the local machine again. I'm running everything on a single machine, but if the database were elsewhere then obviously I'd need to the use the IP address of that machine. And if I were being clever then I'd pull all this in from some configuration file...again, later...

The user and password are used to log in to that specific database (testdb), which if you remember I created using the MariaDB root user (bad!) and also note the horrificly insecure password I was using. Oh and it is an even worse idea to hard code it in plain text in your source file. Don't say you weren't warned. DO NOT DO THIS IN ANYTHNG OTHER THAN A TRIVIAL EXAMPLE, and even then it is probably a bad idea.

After the above code which connects to our database, we add a simple function:
(defn list-all []  (with-connection db    (with-query-results rs ["select * from ids"]      (doall rs))))
This states to use the connection details stored in the db variable and make a query on the database and store the results in as a list called rs and then process it. In this case we're forcing the list to be evaluated using the doall function over the list rs. Clojure is a lazy functional programming language like Haskell, or at least has lazy features.

Now modify the app-routes function so:
(defroutes app-routes
  (GET "/" [] ( helloworld ))
  (GET "/listall" [] ( list-all ))
  (route/resources "/")
  (route/not-found "Not Found"))
Don't forget to restart the server:
ian@ian@deb1:~/dbtest$ lein ring server
2014-01-15 11:12:17.221:INFO:oejs.Server:jetty-7.6.8.v20121106
2014-01-15 11:12:17.345:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:3000
Started server on port 3000
and in a seperate terminal (or put the URL below into your browser)
ian@ian@deb1:~/dbtest$ curl http://127.0.0.1:3000/listall
{:name "Alice", :id 1}{:name "Bob", :id 2}{:name "Eve", :id 3}
And there you have Clojure returning the contents of the database. After this, kill the server again (note: there is a way to get lein to automatically rebuild and restart the server...exercise left to reader ;-)

Now add the function:
(defn get-by-id [the_id]
  (with-connection db
   (with-query-results rs ["select * from ids where id=?" the_id ]
     (doall rs))))
Aside: the above probably could do with some validation to protect against SQL injection and other bad data (cf: obxkcd)

and modify app-routes so it contains an additional line:
  (GET "/id/:the_id" [the_id] ( get-by-id the_id )) 
and restart the server as before and test with curl (or your browser):
ian@ian@deb1:~/dbtest$ curl http://127.0.0.1:3000/id/1
{:name "Alice", :id 1}
ian@
ian@deb1:~/dbtest$ curl http://127.0.0.1:3000/id/2
{:name "Bob", :id 2}
ian@
ian@deb1:~/dbtest$ curl http://127.0.0.1:3000/id/3
{:name "Eve", :id 3}
ian@
ian@deb1:~/dbtest$ curl http://127.0.0.1:3000/id/4
Not Found
And that's it, a "fully" functioning web service of sorts. 0/10 probably for style and -100000s for security but quite cool for so few lines of code.

--

Here's the final code for the core.clj file:

(ns dbtest.core  (:use [compojure.core]        [clojure.test])      (:require [compojure.handler :as handler]            [compojure.route :as route]          ))
(use 'clojure.java.jdbc) (let [db-host "localhost"    ;; change this as necessary      db-port 3306           ;; change this as necessary      db-name "testdb"]      ;; change this as necessary   (def db {:classname "com.mysql.jdbc.Driver" ; must be in classpath           :subprotocol "mysql"           :subname (str "//" db-host ":" db-port "/" db-name)           ; Any additional keys are passed to the driver           ; as driver-specific properties.           :user "root"           :password "badpassword"}))     ;; change this as necessary!

(defn list-all []  (with-connection db    (with-query-results rs ["select * from ids"]      (doall rs))))

(defn get-by-id [the_id]  (with-connection db    (with-query-results rs ["select * from ids where id=?" the_id ]      (doall rs))))
(defn helloworld []  "Hello World")
(defroutes app-routes  (GET "/" [] ( helloworld ))  (GET "/listall" [] ( list-all ))  (GET "/id/:the_id" [the_id] ( get-by-id the_id ))   (route/resources "/")  (route/not-found "Not Found"))
(def app  (handler/site app-routes))









Tuesday, 17 July 2012

Compiling node.js on the Raspberry Pi

I see a few people have already complied node.js on the Raspberry Pi already and so these instructions are somewhat of a repeat but here goes anyway. I've included various links below, but as you'll see we've all the same experiences more or less but it is good to see commonality.

Disclaimer: you're on your own from here...I take no responsibility for data loss, your Pi exploding or anything else.

Here's my configuration:

$uname -a
Linux raspberrypi 3.1.9+ #90 Wed Apr 18 18:23:05 BST 2012 armv6l GNU/Linux
$ cat /etc/debian_version
6.0.4
$ gcc -v
Using built-in specs.
Target: arm-linux-gnueabi
Configured with: ../src/configure -v --with-pkgversion='Debian 4.4.5-8' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --disable-sjlj-exceptions --enable-checking=release --build=arm-linux-gnueabi --host=arm-linux-gnueabi --target=arm-linux-gnueabi
Thread model: posix
gcc version 4.4.5 (Debian 4.4.5-8)


There are reports of the Pi running out of memory, so you can change the distribution between the CPU and GPU memory allocation by using a different kernel.

NB: the $ symbol is the command prompt I'm using under bash!

$sudo cp /boot/arm224_start.elf /boot/start.elf

This is explained more here: Changing RaspberryPi RAM CPU:GPU Ratio

Then I downloaded libssl-dev, after running apt-get update a couple(!!) of times.

$sudo apt-get update
$sudo apt-get update
$sudo apt-get install libssl-dev

Download and extract node.js:

$wget http://nodejs.org/dist/v0.8.2/node-v0.8.2.tar.gz
$tar xvf node-v0.8.2.tar.gz
$cd node-v0.8.2

Set the compilation flags:

$export CCFLAGS='-march=armv6'
$export CXXFLAGS='-march=armv6'
$export GYP_DEFINES='armv7=0'

Edit one of the node.js configuration files with vi

$vi deps/v8/SConstruct

Aside: use vi as emacs is now too big for any known Earth based computer - rumour has it that the cloud was only invented to gather together enough resources just to even start emacs, let alone do anything useful...(note to emacs users, this is humour!! ;-)

The lines you're looking for are found around lines 82-90 and look like this:

'all': {
    'CCFLAGS': ['$DIALECTFLAGS', '$WARNINGFLAGS'],
    'CXXFLAGS': ['-fno-rtti', '-fno-exceptions'],
},


change these by adding the -march=armv6 options so they look like this:

'all': {
   'CCFLAGS': ['$DIALECTFLAGS', '$WARNINGFLAGS', '-march=armv6'],
   'CXXFLAGS': ['-fno-rtti', '-fno-exceptions', '-march=armv6'],
},


Then somewhere around lines 170-175 you'll find:

'armeabi:softfp' : {
   'CPPDEFINES' : ['USE_EABI_HARDFLOAT=0'],
   'vfp3:on': {
      'CPPDEFINES' : ['CAN_USE_VFP_INSTRUCTIONS']
   },
   'simulator:none': {
      'CCFLAGS': ['-mfloat-abi=softfp'],
   }
},


You'll need to comment out the 'vfp3' and 'simulator' sections AND remove the trailing comma at the end of the 'CPPDEFINES' line so that it now looks like this:

'armeabi:softfp' : {
   'CPPDEFINES' : ['USE_EABI_HARDFLOAT=0']
   # 'vfp3:on': {
   #  'CPPDEFINES' : ['CAN_USE_VFP_INSTRUCTIONS']
   # },
   # 'simulator:none': {
   #  'CCFLAGS': ['-mfloat-abi=softfp'],
   # }
},

NB: remember that trailing comma!

And start the compilation:

$./configure
$make CFLAGS+=-O2 CXXFLAGS+=-O2

Note the use of the -O2 option, aparently this is to get around some compiler bug which seems to result in the following errors:

make[1]: Entering directory `/home/pi/node-v0.8.2/out'
  ACTION _home_pi_node_v0_8_2_deps_v8_tools_gyp_v8_gyp_v8_snapshot_target_run_mksnapshot /home/pi/node-v0.8.2/out/Release/obj.target/v8_snapshot/geni/snapshot.cc
pure virtual method called
terminate called without an active exception
Aborted
make[1]: *** [/home/pi/node-v0.8.2/out/Release/obj.target/v8_snapshot/geni/snapshot.cc] Error 134
make[1]: Leaving directory `/home/pi/node-v0.8.2/out'
make: *** [node] Error 2


First compilation took about 2 hours 20 minutes which then ended with the above problem. I've a 4Gb SanDisk Extreme SDHC card which might be a bit of an I/O bottleneck - some people have reported much faster compilation times. Anyway, successful compilation and linking took about 2 hours 40:

$ ls -l out/Release/node
-rwxr-xr-x 1 pi pi 9475651 Jul 17 20:10 out/Release/node

and running a simple server (found via Felix's node.js beginner's guide) :






:-)

References:
  1. Tom Gallacher's page on Node.js
  2. doowttam's posting on the R-Pi forums about nodejs 0.8.1
  3. Elsmorian's Nodejs on Raspberry Pi
  4. Cannot compile node.js 0.8.2 under CentOS 6.2 - compiler optimisation errors: use -O2 option for CFLAGS and CXXFLAGS
  5. Node.js compile fails in Amazon AMI x64 - snapshot.cc - pure functional method call - more on the compiler optimisation flags.
  6. Issue 2093: fix "pure virtual method called" error with gcc 4.4.6 + -fno-strict-aliasing (original link)

Tuesday, 17 April 2012

Privacy, Dataflow and Nissenbaum ... formalisation?

I read the article by Alexis Madrigal of The Atlantic about Helen Nissenbaum's approach to privacy. It is good to see someone talking about sharing of information as being good for privacy. Maybe this is one of the rare instances that the notion of privacy has been liberated from being all about hiding your data, protecting the "consumer" to actually, in my opinion, being about how data flows.

To quote from the article and given a good example:
This may sound simple, but it actually leads to different analyses of current privacy dilemmas and may suggest better ways of dealing with data on the Internet. A quick example: remember the hubbub over Google Street View in Europe? Germans, in particular, objected to the photo-taking cars. Many people, using the standard privacy paradigm, were like, "What's the problem? You're standing out in the street? It's public!" But Nissenbaum argues that the reason some people were upset is that reciprocity was a key part of the informational arrangement. If I'm out in the street, I can see who can see me, and know what's happening. If Google's car buzzes by, I haven't agreed to that encounter. Ergo, privacy violation.

First thing here is that Nissenbaum gets us past the privacy as a binary thing: its private or public where private means hidden. Nissenbaum actually promotes the idea of how we perceive the data flow rather than whether something is private or public; again quoting from the article:

Nissenbaum argues that the real problem "is the inapproproriateness of the flow of information due to the mediation of technology." In her scheme, there are senders and receivers of messages, who communicate different types of information with very specific expectations of how it will be used. Privacy violations occur not when too much data accumulates or people can't direct it, but when one of the receivers or transmission principles change. The key academic term is "context-relative informational norms." Bust a norm and people get upset. 

For a while I've been working on formalising architectures, ontologies, taxonomies and so on for privacy (privacy engineering) - the common factor in all of these is the data-flow. Actually I think some of this is quite simple when thought of in this manner, firstly we construct a simple data-flow model:



Aside: this is quite informal and the following just sketches out a line of thinking rather than being a definition.

Some information I flows from A to B. For this information I we can extract a number of aspects: sensitivity, information type, identity (amount of) etc. We can also ask the question of this particular interaction (A,I,B) of whether that information I is relevant to the particular set of transactions or services that B provides. If B requires a set of information H to work for fulfil the contract with A then I<=H in this case, which allows A to supply less but should discourage B asking for more.

We can also look at other factors in this to make that decision: the longevity of information in B, the ownership of the information once passed to B and importantly, whether B passes this information on - we come to this latter point later. Ultimately we can assign a weight to this data-flow, though what form of metric this is I don't have a good idea about at the moment but let's call it 'm', ie: a(I) is some measure of the 'amount of information' weighted by the various aspects and classifications. The above I<=H should then be rewritten as a(I)<=a(H) which better takes into account of the weightings of the information classifications.

This we can continue through a number of other flows and introduce a typing or taxonomic structure for the nodes:



As B is a bank then the amount of information required tends to be high, if C is on-line shop, then this tends to be lower and so on. Such a rule might be:

forall u:User, b:Bank, c:OnlineShop, d:NewsSite |
    a( u-->b ) => a( u-->c ) and
    a( u-->c ) => a( u-->d )
    ...

For each node, we can better describe the expectation in terms of this metric, ie: a(b) where b is the Bank node from above, we get the rule from earlier:

forall u:User, b:Bank |
    a( u-->b ) <= a(b)

Now our weighting function a deals with particular instances, where as we have stated that that there are expectation, so let's introduce a new function that computes a range for a given type, for example r(Bank) returns a range [ r_min, r_max ]. Then for a particular instance of Bank we get

forall b:Bank |
      r_min(Bank) <= a(b) <= r_max(Bank)

If a given instance, for example e in the above data-flow requires something outside the range for its type then we are "busting a norm" for that particular type, and following on from the above rules:


forall u:User, b:Bank |
      r_min(Bank) <= a(b) <= r_max(Bank)
         and
      a( u-->b ) <= a(b)


The next thing is to look at the next level in the data-flow graph, to where do B,C,D and E send their information, how much and how do these data-flows affect the first - I guess there's a very interesting feedback loop there. A few other things spring to mind as well: do we see a power law operating over the weighting of the data-flows? Does it matter to where and how much data flows?

Introduce a temporal dimension and plot the above over time and you get a picture of the change in norms and consumer expectations.

Getting back to Nissenbaum's thesis which is that the expectation of privacy over data-flows is the key and not whether the data-flows at all, I think we could reasonably model this.

Sunday, 24 January 2010

Finland's Internet for all

A rare moment of infrastructure provision sanity from the Finnish Government as reported by the BBC:
Delivering Finland's web 'human right'
NB: Emsalö is actually in Porvoo not Helsinki