Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

9/8/12

Phantom types in Java

Introduction
One of the things that I like about types in programming languages is that they're there to help you (although sometimes feels the opposite). The more things you can check at compile time, the less you need to do during tests (if you need to do it at all!).
One of those techniques are Phantom Types, and although well known in advanced languages (Haskell, Scala, OCaml, etc.) it seems to be relativelly unknown in Java despite the fact it can be perfectly used.

Since Java 1.5, we have parametric types (generics). Once you have that, you can add types parameters to other types...

public class MyTpye <TypeParameter> { ....

you can have methods with generic parameters

public <T> Result doSomething( MyType<T> p){
.... do something with p ...
}

or require specific type parameters on your methods (called a generic type invocation ).

public Result doSomethingStringy( MyType<String> p){
.... do something with p ...
}

A ghost in the (type) machine
So, what are phantom types?. Now, normally when you require a specific type in the parameter you use it in the method body (say, when you have sum(List<Integer> xs) you'll use the fact that you have a list of Integers to sum them) , but what happen if you don't? Now you have a parameter type that never appears in the body, although is there, requiring that specific parameter in the type and preventing compilation if doesn't match. That's a phantom type :)

A simple example
Here an example: let's model a plane that can be either flying or landed and a takeOff and land methods that can only be applied to landed and flying planes respectively.
First, let's define the different flight status as marker interfaces:




Now, our plane class parametrized by the flight status:

And finally, our flight controller class with the takeOff and land methods:

The interesting part is in the land and takeOff methods. For example, in the land method, we require a Plane<Flying> but we just return a landing plane, (without using the Flying interface in the body). That's how we enforce that a plane must be flying to be able to land.
What I wanted to show with this is pretty silly example is how you can use phantom types to enforce some rules.

What are they good for?
Ok, now you have a way to require at compile type certain parameter on a type, what can you use it for?
Turns out it you can use to enforce many constraints:
Enforce a particular state: in the previous example, we used phantom types to require a specific state in a method (flying/landing in this case). In the same way we can require a connection to be open when we do a query or close it:

public ResultSet execute( Connection<Open> c, Query q) ....

public void close( Connection<Open> c) ....

(but is not safe, will work a better example)

You can also use it for safer Ids:
Usually Ids are Int or Strings, and is very easy to mix them up, e.g. in buy(String productId, String customerId) you can swap the Ids by mistake and you can get subtle bugs (the productId might match a customerId). With phantom types you can define an Id class parametrized by the entity and you get buy(Id<Product> productId, Id<Customer> customerId) and you'll get a compiler error if you pass a product id where you expect a customer id.
The full example (in Scala):






Go ahead and Type!
As you see, phantom types are pretty straightforward and gives you more expressive power (in particular, will allow you to get more mileage from Java's type system). I hope they get more use in Java...

10/22/08

Simple Java tricks to protect your web application against SQL injection

Your application is vulnerable to SQL Injection when you send unfiltered strings to the database. Most modern ORM frameworks should take care of it (but don't take my word!... go ahead and check how secure your framework is).
Sometimes, you have to work with plain JDBC (or ODBC). Here is a couple of tricks that help:
  1. First and foremost, avoid concatenating strings for SQL queries. Use prepared statements unless is not possible (i.e. cases when you have undefined number of parameters)
  2. Leverage the language type system: If you're passing a number, use Integer instead of String... any invalid character will fail the conversion and will not reach the DB.
  3. If there's no option but concatenate strings, make sure the database comment quotes are escaped (for example, in DB2 you have to replace the single quote character with 2 single quote characters: instead of "SELECT * FROM users WHERE name='"+param+"'" use "SELECT * FROM users WHERE name='"+param.replaceAll("'","''")+"'"
For something a little more advanced, you can wrap the strings in some kind of "EscapedString" class, and use that class in the signature of the DAOs (related to 2. )

Note: by no means this is a comprehensive list. Application security is very hard, check your database documentation...

9/24/08

Java: The programming tool of choice for discriminating hackers

In spite of all the Java bashing that's so in vogue this days, Team Smartass won the ICFP Programming Contest using Java, giving bragging rights to say that Java is "The programming tool of choice for discriminating hackers".
while(tongue in cheek){
take(this);
(lisp (snobs all))
fuctional_programing = Maybe[zealots]
ruby('fanboys')
}
Another proof of what matters is not the tool, is how you use it...

2/3/08

Playing with Scala 4: Abstract types and Self-types

Thanks to the scala mailing list, and to Eric, I have a satisfying enough model of a graph to be used by the Dijkstra's algorithm.
(well, there already was a generic graph and directed graph written in Scala... talking about reinventing the wheel!. The solution in Eric's comment works great too... is almost the same, but he was faster than me on coming up with it :D )
http://www.scala-lang.org/intro/abstracttypes.html
http://www.scala-lang.org/intro/selfrefs.html
I just used the same code, and changed the nodes and edges to use sets instead of lists (really, we don't care about the order)
The Scala example of Graph uses abstract types: it declares type Node and type Edge as type "parameters" to be defined by concrete implementation. Even more, by declaring type Node <: NodeIntf, we're restricting Node to be subtypes of NodeIntf. The declaration "self: Node =>" tells "I'll use self as Node type"
Then I declared the trait Weighted and extended the class DirectedGraph adding my extensions to NodeImpl and EdgeImpl to use weighted trait and add the node label and the previous property. I had to add a collection of the adjacent arcs, because they weren't in the original Graph and is easier for the algorithm. I overrode the connectWith method to keep track of the adjacent arcs as they're added, also I defined our --> operator to call the connectWith method.

class DfsNode extends NodeImpl with Weighted {
var previous: DfsNode = _
var label:String = _
var nodeEdges: Set[Edge] = Set()

override def connectWith(node: Node): Edge = {
val newEdge=super.connectWith(node)
nodeEdges = nodeEdges + newEdge
newEdge
}

def -->(n2:Node):DfsEdge =connectWith(n2)

override def toString()= label+ " w:"+weight
}

Edges are simpler, just add the weight and override toString:
class DfsEdge(origin:Node, dest:Node) extends EdgeImpl(origin, dest) with Weighted {
override def toString()= from.label+"-->"+to.label+" w:"+weight
}

All are enclosed in the DfsGraph class extending the DirectedGraph class, now I can assign the concrete types for Node and Edge:
type Node= DfsNode
type Edge= DfsEdge

The DirectedGraph class has a addNode method. To keep the DSL-ish quality of the construction of the graph, I added the method addNewNode to take the node label as parameter
def addNewNode(l:String):Node={
val newNode=super.addNode
newNode.label=l
newNode
}

So we can write: graph addNewNode "nodeLabel".

The example looks like this:

objectExampleextendsApplication {

valgraph = newDfsGraph

valn1=graphaddNewNode"start"
valn2=graphaddNewNode"n2"
valn3=graphaddNewNode"n3"
valn4=graphaddNewNode"n4"
valn5=graphaddNewNode"n5"
valn6=graphaddNewNode"end"

n1-->n2weight=2
n1-->n3weight=1
n2-->n4weight=1
n3-->n4weight=3
n2-->n5weight=1
n4-->n6weight=1
n5-->n6weight=3

graph.shortestPath(n1,n6)
println("Path")
graph.pathToStart(n6).reverse.map(println(_))

}

Pretty easy to read, don't you think?
In the next post, I'll show you how we can use this algorithm to solve the "Abbott's revenge" type of maze. Ideally, I want to create an interpreter for the input, exploring Scala's features for creating language interpreters.
Here is the complete code:

package dfs2;

abstractclassGraph {
typeEdge
typeNode <: NodeIntf
abstractclassNodeIntf {
defconnectWith(node: Node): Edge
}
defnodes: Set[Node]
defedges: Set[Edge]
defaddNode: Node
}

abstractclassDirectedGraphextendsGraph {
typeEdge <: EdgeImpl
classEdgeImpl(origin: Node, dest: Node) {
deffrom = origin
defto = dest
}

classNodeImplextendsNodeIntf {
self: Node =>
defconnectWith(node: Node): Edge = {
valedge = newEdge(this, node)
edges = edges+edge
edge
}
}

protecteddefnewNode: Node
protecteddefnewEdge(from: Node, to: Node): Edge

varnodes: Set[Node] =Set()
varedges: Set[Edge] =Set()
defaddNode: Node = {
valnode = newNode
nodes = nodes+node
node
}
}

traitWeighted {
varweight= Float.PositiveInfinity
}
classDfsGraphextendsDirectedGraph{
classDfsNodeextendsNodeImplwithWeighted {
varprevious: DfsNode = _
varlabel:String = _
varnodeEdges: Set[Edge] = Set()

overridedefconnectWith(node: Node): Edge = {
valnewEdge=super.connectWith(node)
nodeEdges = nodeEdges+newEdge
newEdge
}

def-->(n2:Node):DfsEdge =connectWith(n2)

overridedeftoString()= label+" w:"+weight
}

classDfsEdge(origin:Node, dest:Node) extendsEdgeImpl(origin, dest) withWeighted {
overridedeftoString()= from.label+"-->"+to.label+" w:"+weight
}

defaddNewNode(l:String):Node={
valnewNode=super.addNode
newNode.label=l
newNode
}

typeNode= DfsNode
typeEdge= DfsEdge

protecteddefnewEdge(from: Node, to: Node): Edge =newDfsEdge(from,to)
protecteddefnewNode: Node = newDfsNode

defshortestPath(start: DfsNode, end: DfsNode) = {
varunvisited=nodes
start.weight=0
while (!unvisited.isEmpty){
valvertx=min(unvisited)
vertx.nodeEdges.map(improveDistance(_))
unvisited=unvisited-vertx
}
}

defimproveDistance(a:Edge) ={
if (a.from.weight+a.weight<a.to.weight) {
a.to.weight=a.from.weight+a.weight
a.to.previous=a.from
}
}

defmin(nodes: Set[DfsNode]): DfsNode = {
nodes.reduceLeft((a:DfsNode,b:DfsNode)=>if (a.weight<b.weight) aelseb )
}

defpathToStart(end:DfsNode):List[DfsNode] = {
if (end==null)
Nil
else
end::pathToStart(end.previous)
}
}

Subscribe to: Comments (Atom)

AltStyle によって変換されたページ (->オリジナル) /