|
1 /* |
|
2 * Copyright ... |
|
3 */ |
|
4 package example |
|
5 |
|
6 import java.net.InetAddress |
|
7 import net.liftweb.common._ |
|
8 import net.liftweb.mapper._ |
|
9 import net.liftweb.util._ |
|
10 import net.liftweb.util.Helpers._ |
|
11 |
|
12 object InetAddressHelpers extends InetAddressHelpers |
|
13 |
|
14 trait InetAddressHelpers { |
|
15 def normalize(inet: String): Box[String] = tryo { |
|
16 InetAddress.getByName(inet).getHostAddress |
|
17 } |
|
18 |
|
19 def valid_?(inet: String): Boolean = (inet != null) && (tryo { |
|
20 InetAddress.getByName(inet) |
|
21 } isDefined ) && (inet.length > 0) |
|
22 |
|
23 def toBigInt(inet: InetAddress): BigInt = BigInt(1, inet.getAddress) |
|
24 |
|
25 def toInet(s: String): Box[InetAddress] = s match { |
|
26 case null | "" => Empty |
|
27 case _ => tryo { InetAddress.getByName(s) } |
|
28 } |
|
29 } |
|
30 |
|
31 /** |
|
32 * Postgres specific <code>InetAddress</code> mapping |
|
33 * (maps to ''inet'' data type). |
|
34 * Always use <code>asInet</code> to get <code>InetAddress</code> instance. |
|
35 */ |
|
36 abstract class MappedInetAddress[T<:Mapper[T]](owner: T) extends |
|
37 MappedString[T](owner, 128) with InetAddressHelpers { |
|
38 |
|
39 override def setFilter = normalizeFilter _ :: super.setFilter |
|
40 |
|
41 override def validate = (valid_?(i_is_!) match { |
|
42 case true => Nil |
|
43 case false => List(FieldError(this, "Invalid IP address")) |
|
44 }) ::: super.validate |
|
45 |
|
46 def normalizeFilter(inet: String): String = inet match { |
|
47 case null | "" => null |
|
48 case s => normalize(s) openOr null |
|
49 } |
|
50 |
|
51 def asInet: Box[InetAddress] = toInet(is) |
|
52 |
|
53 override def targetSQLType = Types.OTHER |
|
54 |
|
55 override def fieldCreatorString(d: DriverType, colName: String) = d match { |
|
56 case _d: BasePostgreSQLDriver => colName + " inet" |
|
57 case _ => super.fieldCreatorString(d, colName) |
|
58 } |
|
59 } |
|
60 |
|
61 /** |
|
62 * Caution: getters may return <code>null</code> value. |
|
63 * Always use <code>asInet</code>. |
|
64 */ |
|
65 abstract class MappedNullableInetAddress[T<:Mapper[T]](owner: T) extends |
|
66 MappedInetAddress[T](owner) { |
|
67 |
|
68 override def defaultValue = null |
|
69 |
|
70 override def validate = i_is_! match { |
|
71 case null | "" => Nil |
|
72 case _ => super.validate |
|
73 } |
|
74 |
|
75 override def dbNotNull_? = false |
|
76 } |
|
77 |
|
78 /** |
|
79 * IP network mapper via its start/end fields. |
|
80 * Implemented validation of IP overlaps, parent network, start, end. |
|
81 * Maintains network relationships (sub-net/super-net) and updates |
|
82 * parent field accordingly. |
|
83 */ |
|
84 object ExampleNetwork extends ExampleNetwork with |
|
85 LongKeyedMetaMapper[ExampleNetwork] with LongCRUDify[ExampleNetwork] { |
|
86 |
|
87 override def validation: List[ExampleNetwork => List[FieldError]] = |
|
88 startLtEnd _ :: checkOverlap _ :: super.validation |
|
89 |
|
90 def startLtEnd(n: ExampleNetwork): List[FieldError] = (for { |
|
91 s <- n.start.asInet |
|
92 e <- n.end.asInet |
|
93 } yield (toBigInt(s) < toBigInt(e))) match { |
|
94 case Full(true) => Nil |
|
95 case Full(false) => |
|
96 List(FieldError(n.start, "Start must be below end")) |
|
97 case _ => |
|
98 List(FieldError(n.start, S ? "Can't compare start/end IP")) |
|
99 } |
|
100 |
|
101 def checkOverlap(net: ExampleNetwork): List[FieldError] = { |
|
102 def run(f: MappedInetAddress[ExampleNetwork]) = { |
|
103 findAll(inNet(f):_*) filter {!_.comparePrimaryKeys(net)} filter { n => |
|
104 net.isSubnetOf(n) match { |
|
105 case f: Failure => true |
|
106 case _ => false |
|
107 }} map { n => |
|
108 FieldError(f, "Overlaps with: " + n.start + " - " + n.end) |
|
109 } |
|
110 } |
|
111 run(net.start) ::: run(net.end) |
|
112 } |
|
113 |
|
114 override def beforeSave = List(n => n.parent(findParent(a))) |
|
115 |
|
116 override def afterSave = List(n => children(n.parent.obj) foreach { child => |
|
117 child.parent(findParent(child)).save |
|
118 }) |
|
119 |
|
120 override def afterDelete = List(n => children(Full(n)) foreach { child => |
|
121 child.parent(findParent(child)).save |
|
122 }) |
|
123 |
|
124 def findParent(net: ExampleNetwork): Box[ExampleNetwork] = |
|
125 findAll(inNet(net):_*) filter {!_.comparePrimaryKeys(net)} sort { |
|
126 (n1, n2) => (for { |
|
127 s1 <- n1.size |
|
128 s2 <- n2.size |
|
129 } yield s1 < s2) openOr true } firstOption |
|
130 |
|
131 import OprEnum._ |
|
132 def inNet(inet: MappedInetAddress[ExampleNetwork]) = List( |
|
133 Cmp(start, <=, Full(inet.is), Empty, Empty), |
|
134 Cmp(end, >=, Full(inet.is), Empty, Empty) |
|
135 ) |
|
136 |
|
137 def inNet(net: ExampleNetwork): List[QueryParam[ExampleNetwork]] = |
|
138 inNet(net.start) ::: inNet(net.end) |
|
139 |
|
140 def getByNet(s: String, e: String) = Box(findAll(By(start, s), By(end, e))) |
|
141 |
|
142 def children(n: Box[ExampleNetwork]) = findAll(By(parent, n)) |
|
143 } |
|
144 |
|
145 class ExampleNetwork extends LongKeyedMapper[ExampleNetwork] with IdPK |
|
146 with InetAddressHelpers { |
|
147 |
|
148 override def getSingleton = ExampleNetwork |
|
149 |
|
150 object start extends MappedInetAddress(this) |
|
151 |
|
152 object end extends MappedInetAddress(this) |
|
153 |
|
154 object parent extends LongMappedMapper(this, ExampleNetwork) { |
|
155 override def dbIncludeInForm_? = false |
|
156 } |
|
157 |
|
158 def size: Box[BigInt] = for { |
|
159 s <- start.asInet |
|
160 e <- end.asInet |
|
161 } yield toBigInt(e) - toBigInt(s) + 1 |
|
162 |
|
163 /** |
|
164 * Checks if this network is subnet of the <code>other</code>. |
|
165 * @return true if this is subnet of the other |
|
166 * false if this is supernet |
|
167 * empty not related at all |
|
168 * failure if can't compare or network overlap |
|
169 */ |
|
170 def isSubnetOf(other: ExampleNetwork): Box[Boolean] = { |
|
171 def inside(what: BigInt, from: BigInt, to: BigInt): Boolean = |
|
172 (from <= what) && (what <= to) |
|
173 |
|
174 (for { |
|
175 si <- start.asInet |
|
176 ei <- end.asInet |
|
177 osi <- other.start.asInet |
|
178 oei <- other.end.asInet |
|
179 } yield { |
|
180 val s = toBigInt(si) |
|
181 val e = toBigInt(ei) |
|
182 val os = toBigInt(osi) |
|
183 val oe = toBigInt(oei) |
|
184 val s_inside = inside(s, os, oe) |
|
185 val e_inside = inside(e, os, oe) |
|
186 val os_inside = inside(os, s, e) |
|
187 val oe_inside = inside(oe, s, e) |
|
188 (s_inside, e_inside, os_inside, oe_inside) match { |
|
189 case (true, true, _, _) => Full(true) |
|
190 case (_, _, true, true) => Full(false) |
|
191 case (false, false, false, false) => Empty |
|
192 case (_, _, _, _) => Failure("IP address overlap with: " + other) |
|
193 } |
|
194 }).openOr(Failure("Failed to obtain inet addresses: this: " + this + |
|
195 ", other: " + other)) |
|
196 } |
|
197 } |
|
198 |
|
199 // vim: set ts=2 sw=2 et: |