Added REST functionality for the Bill. RabbitMQProducer was refined. UserActor sends...
authorProdromos Gerakios <pgerakios@grnet.gr>
Fri, 3 Aug 2012 11:21:05 +0000 (14:21 +0300)
committerProdromos Gerakios <pgerakios@grnet.gr>
Fri, 3 Aug 2012 11:21:05 +0000 (14:21 +0300)
src/main/scala/gr/grnet/aquarium/actor/ActorRole.scala
src/main/scala/gr/grnet/aquarium/actor/message/GetUserBillRequest.scala [new file with mode: 0644]
src/main/scala/gr/grnet/aquarium/actor/message/GetUserBillResponse.scala [new file with mode: 0644]
src/main/scala/gr/grnet/aquarium/actor/service/user/UserActor.scala
src/main/scala/gr/grnet/aquarium/charging/bill/BillEntry.scala [new file with mode: 0644]
src/main/scala/gr/grnet/aquarium/connector/rabbitmq/RabbitMQProducer.scala
src/main/scala/gr/grnet/aquarium/service/FinagleRESTService.scala
src/main/scala/gr/grnet/aquarium/service/RESTPaths.scala

index 4f05fef..29d5fac 100644 (file)
@@ -36,7 +36,7 @@ package gr.grnet.aquarium.actor
 
 import service.user.UserActor
 import gr.grnet.aquarium.actor.message.event.{ProcessIMEvent, ProcessResourceEvent}
-import gr.grnet.aquarium.actor.message.{GetUserWalletRequest, GetUserStateRequest, GetUserBalanceRequest}
+import message.{GetUserBillRequest, GetUserWalletRequest, GetUserStateRequest, GetUserBalanceRequest}
 import gr.grnet.aquarium.actor.message.config.{InitializeUserActorState, AquariumPropertiesLoaded, ActorConfigurationMessage}
 
 /**
@@ -90,6 +90,7 @@ case object UserActorRole
                           classOf[ProcessIMEvent],
                           classOf[GetUserWalletRequest],
                           classOf[GetUserBalanceRequest],
+                          classOf[GetUserBillRequest],
                           classOf[GetUserStateRequest]),
                       Set(classOf[InitializeUserActorState],
                           classOf[AquariumPropertiesLoaded]))
diff --git a/src/main/scala/gr/grnet/aquarium/actor/message/GetUserBillRequest.scala b/src/main/scala/gr/grnet/aquarium/actor/message/GetUserBillRequest.scala
new file mode 100644 (file)
index 0000000..7bf20b5
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials
+ *      provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+
+package gr.grnet.aquarium.actor.message
+
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
+
+/**
+ *
+ * @author Prodromos Gerakios <pgerakios@grnet.gr>
+ */
+
+case class GetUserBillRequest(userID: String, timeslot : Timeslot, timestamp: Long) extends ActorMessage with UserActorRequestMessage {
+  def referenceTimeMillis = timestamp
+}
+
diff --git a/src/main/scala/gr/grnet/aquarium/actor/message/GetUserBillResponse.scala b/src/main/scala/gr/grnet/aquarium/actor/message/GetUserBillResponse.scala
new file mode 100644 (file)
index 0000000..d18cc7d
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials
+ *      provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+
+package gr.grnet.aquarium.actor.message
+
+import gr.grnet.aquarium.AquariumInternalError
+import gr.grnet.aquarium.charging.bill.BillEntry
+
+/**
+ *
+ * @author Prodromos Gerakios <pgerakios@grnet.gr>
+ */
+
+case class GetUserBillResponse(
+                                   balance: Either[String, GetUserBillResponseData],
+                                   override val suggestedHTTPStatus: Int = 200)
+  extends UserActorResponseMessage({balance match {
+    case Left(s) => Left(s)
+    case Right(GetUserBillResponseData(userID,billEntry)) => Right(billEntry)
+  }}, suggestedHTTPStatus) {
+  def userID = balance match {
+    case Left(error) ⇒
+      throw new AquariumInternalError("Could not obtain userID. %s".format(error))
+
+    case Right(data) ⇒
+      data.userID
+  }
+}
+
+case class GetUserBillResponseData(userID: String, billEntry: BillEntry)
\ No newline at end of file
index fc644fb..a8db62b 100644 (file)
@@ -42,8 +42,12 @@ import gr.grnet.aquarium.actor._
 import gr.grnet.aquarium.actor.message.event.{ProcessResourceEvent, ProcessIMEvent}
 import gr.grnet.aquarium.actor.message.config.{InitializeUserActorState, AquariumPropertiesLoaded}
 import gr.grnet.aquarium.util.date.TimeHelpers
-import gr.grnet.aquarium.event.model.im.IMEventModel
-import gr.grnet.aquarium.actor.message.{GetUserWalletResponseData, GetUserWalletResponse, GetUserWalletRequest, GetUserStateResponse, GetUserBalanceResponseData, GetUserBalanceResponse, GetUserStateRequest, GetUserBalanceRequest}
+import gr.grnet.aquarium.event.model.im.{BalanceEvent, IMEventModel}
+import message._
+import config.AquariumPropertiesLoaded
+import config.InitializeUserActorState
+import event.ProcessIMEvent
+import event.ProcessResourceEvent
 import gr.grnet.aquarium.util.{LogHelpers, shortClassNameOf}
 import gr.grnet.aquarium.{Aquarium, AquariumInternalError}
 import gr.grnet.aquarium.computation.BillingMonthInfo
@@ -52,6 +56,21 @@ import gr.grnet.aquarium.charging.state.{WorkingAgreementHistory, WorkingUserSta
 import gr.grnet.aquarium.charging.reason.{InitialUserActorSetup, RealtimeChargingReason}
 import gr.grnet.aquarium.policy.{PolicyDefinedFullPriceTableRef, StdUserAgreement}
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
+import message.GetUserBalanceRequest
+import message.GetUserBalanceResponse
+import message.GetUserBalanceResponseData
+import message.GetUserStateRequest
+import message.GetUserStateResponse
+import message.GetUserWalletRequest
+import message.GetUserWalletResponse
+import message.GetUserWalletResponseData
+import scala.Left
+import gr.grnet.aquarium.charging.state.WorkingAgreementHistory
+import scala.Some
+import scala.Right
+import gr.grnet.aquarium.policy.StdUserAgreement
+import gr.grnet.aquarium.charging.state.UserStateBootstrap
+import gr.grnet.aquarium.charging.bill.BillEntry
 
 /**
  *
@@ -359,6 +378,7 @@ class UserActor extends ReflectiveRoleableActor {
       updateLatestResourceEventIDFrom(rcEvent)
     }
 
+    var oldTotalCredits = this._workingUserState.totalCredits
     // FIXME check these
     if(nowYear != eventYear || nowMonth != eventMonth) {
       DEBUG(
@@ -380,14 +400,27 @@ class UserActor extends ReflectiveRoleableActor {
     else {
       computeBatch()
     }
-    aquarium(Aquarium.EnvKeys.rabbitMQProducer).
-    sendMessage("{\"userid\": \"%s\", \"state\": %s}".
-                  format(this._userID,
-                  this._workingUserState.totalCredits >= 0.0))
+    if(oldTotalCredits * this._workingUserState.totalCredits < 0)
+      BalanceEvent.send(aquarium,this._workingUserState.userID,
+                        this._workingUserState.totalCredits>=0)
     DEBUG("Updated %s", this._workingUserState)
     logSeparator()
   }
 
+  def onGetUserBillRequest(event: GetUserBillRequest): Unit = {
+    try{
+      val timeslot = event.timeslot
+      val state= if(haveWorkingUserState) Some(this._workingUserState) else None
+      val billEntry = BillEntry.fromWorkingUserState(timeslot,state)
+      val billData = GetUserBillResponseData(this._userID,billEntry)
+      sender ! GetUserBillResponse(Right(billData))
+    } catch {
+      case e:Exception =>
+       e.printStackTrace()
+       sender ! GetUserBillResponse(Left("Internal Server Error [AQU-BILL-0001]"), 500)
+    }
+  }
+
   def onGetUserBalanceRequest(event: GetUserBalanceRequest): Unit = {
     val userID = event.userID
 
diff --git a/src/main/scala/gr/grnet/aquarium/charging/bill/BillEntry.scala b/src/main/scala/gr/grnet/aquarium/charging/bill/BillEntry.scala
new file mode 100644 (file)
index 0000000..3a33e71
--- /dev/null
@@ -0,0 +1,106 @@
+package gr.grnet.aquarium.charging.bill
+
+import gr.grnet.aquarium.charging.state.WorkingUserState
+import gr.grnet.aquarium.util.json.JsonSupport
+import com.ckkloverdos.resource.FileStreamResource
+import java.io.File
+import com.ckkloverdos.props.Props
+import gr.grnet.aquarium.converter.{PrettyJsonTextFormat, StdConverters}
+import gr.grnet.aquarium.{Aquarium, ResourceLocator, AquariumBuilder}
+import gr.grnet.aquarium.store.memory.MemStoreProvider
+import gr.grnet.aquarium.converter.StdConverters._
+import scala.Some
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
+
+/*
+* Copyright 2011-2012 GRNET S.A. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or
+* without modification, are permitted provided that the following
+* conditions are met:
+*
+*   1. Redistributions of source code must retain the above
+*      copyright notice, this list of conditions and the following
+*      disclaimer.
+*
+*   2. Redistributions in binary form must reproduce the above
+*      copyright notice, this list of conditions and the following
+*      disclaimer in the documentation and/or other materials
+*      provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+*
+* The views and conclusions contained in the software and
+* documentation are those of the authors and should not be
+* interpreted as representing official policies, either expressed
+* or implied, of GRNET S.A.
+*/
+
+
+/*
+* @author Prodromos Gerakios <pgerakios@grnet.gr>
+*/
+
+class EventEntry(id:String,
+                 eventType:String,
+                 unitPrice:String,
+                 startTime:String,
+                 endTime:String,
+                 ellapsedTime:String,
+                 credits:String) extends JsonSupport {
+
+}
+
+class ResourceEntry(val resourceName : String,
+                    val resourceType : String,
+                    val unitName : String,
+                    val totalCredits : String,
+                    val details : List[EventEntry]) extends JsonSupport {
+
+}
+
+class BillEntry(val id:String,
+                val userID : String,
+                val status : String,
+                val remainingCredits:String,
+                val deductedCredits:String,
+                val startTime:String,
+                val endTime:String,
+                val bill:List[ResourceEntry]
+              )  extends JsonSupport {
+
+}
+
+object BillEntry {
+  def fromWorkingUserState(t:Timeslot,w:Option[WorkingUserState]) : BillEntry = {
+    //TODO: get entries at timeslot "t"
+    val eventEntry = new EventEntry("1234","onOff","0.1","323232323","3232223456","10000","5.00")
+    val resourceEntry = new ResourceEntry("VM_1","vmtime","0.01","5.0",List(eventEntry))
+    new BillEntry("323232","loverdos@grnet.gr","ok","100.00","5.00","23023020302","23232323",
+                  List(resourceEntry))
+  }
+
+  //
+  def main(args: Array[String]) = {
+    //Console.err.println("JSON: " +  (new BillEntry).toJsonString)
+    val propsfile = new FileStreamResource(new File("a1.properties"))
+    var _props: Props = Props(propsfile)(StdConverters.AllConverters).getOr(Props()(StdConverters.AllConverters))
+    val aquarium = new AquariumBuilder(_props, ResourceLocator.DefaultPolicyModel).
+      update(Aquarium.EnvKeys.storeProvider, new MemStoreProvider).
+      update(Aquarium.EnvKeys.eventsStoreFolder,Some(new File(".."))).
+      build()
+    aquarium.start()
+    ()
+  }
+}
\ No newline at end of file
index bb36eae..b77af94 100644 (file)
@@ -1,9 +1,10 @@
 package gr.grnet.aquarium.connector.rabbitmq
 
 import conf.RabbitMQConsumerConf
+import conf.RabbitMQConsumerConf
 import conf.{RabbitMQKeys, RabbitMQConsumerConf}
 import conf.RabbitMQKeys.{RabbitMQConfKeys, RabbitMQConKeys}
-import gr.grnet.aquarium.{Configurable, ResourceLocator, AquariumBuilder, Aquarium}
+import gr.grnet.aquarium._
 import com.rabbitmq.client._
 import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference}
 import com.ckkloverdos.props.Props
@@ -12,6 +13,7 @@ import gr.grnet.aquarium.util.{Lock, Tags}
 import gr.grnet.aquarium.store.memory.MemStoreProvider
 import java.io.File
 import com.ckkloverdos.resource.FileStreamResource
+import scala.Some
 
 
 /*
@@ -49,6 +51,11 @@ import com.ckkloverdos.resource.FileStreamResource
  * or implied, of GRNET S.A.
  */
 
+/**
+ *
+ * @author Prodromos Gerakios <pgerakios@grnet.gr>
+ */
+
 class RabbitMQProducer extends Configurable {
   private[this] var _conf: RabbitMQConsumerConf = _
   private[this] var _factory: ConnectionFactory = _
@@ -56,33 +63,29 @@ class RabbitMQProducer extends Configurable {
   private[this] var _channel: Channel = _
   private[this] var _servers : Array[Address] = _
   private[this] final val lock = new Lock()
+  private[this] var _exchangeName : String = _
+  private[this] var _routingKey :String = _
 
   def propertyPrefix: Option[String] = Some(RabbitMQKeys.PropertiesPrefix)
   //  Some(RabbitMQConfKeys.imevents_credit)
 
 
   def configure(props: Props): Unit = {
-    var prop = props.get(RabbitMQConfKeys.imevents_credit).getOr("")
-    // Console.println("Prop: " + prop)
-    val Array(exchange, routing) = prop.split(":")
-    //Console.println("ex: " + exchange + " routing: " + routing)
-     _conf = RabbitMQConsumerConf(
-      tag = Tags.IMEventTag,
-      exchangeName = exchange,
-      routingKey = routing,
-      queueName = "",
-      connectionConf = RabbitMQKeys.makeConnectionConf(props),
-      exchangeConf = RabbitMQKeys.DefaultExchangeConf,
-      channelConf = RabbitMQKeys.DefaultChannelConf,
-      queueConf = RabbitMQKeys.DefaultQueueConf
-    )
+    val propName = RabbitMQConfKeys.imevents_credit
+    def exn () = throw new AquariumInternalError(new Exception, "While obtaining value for key %s in properties".format(propName))
+    val prop = props.get(propName).getOr(exn())
+    if (prop.isEmpty) exn()
+    val connectionConf = RabbitMQKeys.makeConnectionConf(props)
+    val Array(exchangeName, routingKey) = prop.split(":")
+    _exchangeName = exchangeName
+    _routingKey = routingKey
     _factory = new ConnectionFactory
-    _factory.setConnectionTimeout(_conf.connectionConf(RabbitMQConKeys.reconnect_period_millis).toInt)
-    _factory.setUsername(_conf.connectionConf(RabbitMQConKeys.username))
-    _factory.setPassword(_conf.connectionConf(RabbitMQConKeys.password))
-    _factory.setVirtualHost(_conf.connectionConf(RabbitMQConKeys.vhost))
-    _factory.setRequestedHeartbeat(_conf.connectionConf(RabbitMQConKeys.reconnect_period_millis).toInt)
-    _servers = _conf.connectionConf(RabbitMQConKeys.servers)
+    _factory.setConnectionTimeout(connectionConf(RabbitMQConKeys.reconnect_period_millis).toInt)
+    _factory.setUsername(connectionConf(RabbitMQConKeys.username))
+    _factory.setPassword(connectionConf(RabbitMQConKeys.password))
+    _factory.setVirtualHost(connectionConf(RabbitMQConKeys.vhost))
+    _factory.setRequestedHeartbeat(connectionConf(RabbitMQConKeys.reconnect_period_millis).toInt)
+    _servers = connectionConf(RabbitMQConKeys.servers)
   }
 
   private[this] def withChannel[A]( next : => A) = {
@@ -103,26 +106,8 @@ class RabbitMQProducer extends Configurable {
 
   def sendMessage(payload:String) =
     withChannel {
-      _channel.basicPublish(_conf.exchangeName, _conf.routingKey,
+      _channel.basicPublish(_exchangeName,_routingKey,
         MessageProperties.PERSISTENT_TEXT_PLAIN,
         payload.getBytes)
     }
-}
-
-object RabbitMQProducer {
-  val propsfile = new FileStreamResource(new File("aquarium.properties"))
-  @volatile private[this] var _props: Props = Props(propsfile)(StdConverters.AllConverters).getOr(Props()(StdConverters.AllConverters))
-  val aquarium = new AquariumBuilder(_props, ResourceLocator.DefaultPolicyModel).
-                update(Aquarium.EnvKeys.storeProvider, new MemStoreProvider).
-                update(Aquarium.EnvKeys.eventsStoreFolder,Some(new File(".."))).
-                build()
-
-
-  def main(args: Array[String]) = {
-    aquarium(Aquarium.EnvKeys.rabbitMQProducer).
-    sendMessage("{\"userid\": \"pgerakios@grnet.gr\", \"state\":true}")
-    Console.err.println("Message sent")
-    aquarium.stop()
-    ()
-  }
 }
\ No newline at end of file
index 2f376a7..0273a6a 100644 (file)
@@ -51,7 +51,7 @@ import java.net.InetSocketAddress
 import java.util.concurrent.{Executors, TimeUnit}
 import gr.grnet.aquarium.util.date.TimeHelpers
 import org.joda.time.format.ISODateTimeFormat
-import gr.grnet.aquarium.actor.message.{GetUserWalletRequest, UserActorRequestMessage, GetUserStateRequest, GetUserBalanceRequest, UserActorResponseMessage}
+import gr.grnet.aquarium.actor.message._
 import com.ckkloverdos.resource.StreamResource
 import com.ckkloverdos.maybe.{Just, Failed}
 import gr.grnet.aquarium.event.model.ExternalEventModel
@@ -59,6 +59,15 @@ import akka.util.{Timeout ⇒ ATimeout, Duration ⇒ ADuration}
 import akka.dispatch.{Future ⇒ AFuture}
 import com.fasterxml.jackson.databind.ObjectMapper
 import java.util
+import scala.Left
+import scala.Some
+import com.ckkloverdos.maybe.Failed
+import gr.grnet.aquarium.actor.message.GetUserStateRequest
+import scala.Right
+import com.ckkloverdos.maybe.Just
+import gr.grnet.aquarium.actor.message.GetUserBalanceRequest
+import gr.grnet.aquarium.actor.message.GetUserWalletRequest
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
 
 /**
  *
@@ -310,6 +319,12 @@ class FinagleRESTService extends Lifecycle with AquariumAwareSkeleton with Confi
         case RESTPaths.UserWalletPath(userID) ⇒
           // /user/(.+)/wallet/?
           callUserActor(GetUserWalletRequest(userID, millis))
+
+        case RESTPaths.UserBillPath(userID,st1,st2) ⇒
+          val t1 = st1.toLong
+          val t2 = st2.toLong
+          val t = Timeslot(t1,if(t2==0)Long.MaxValue else t2)
+          callUserActor(GetUserBillRequest(userID,t,millis))
       }
 
       val DefaultHandler: URIPF = {
index e8c588b..6fe8202 100644 (file)
@@ -57,24 +57,26 @@ object RESTPaths {
 
   final val ResourcesLogbackXMLPath = toResourcesPath(ResourceLocator.ResourceNames.LOGBACK_XML).r
 
-  final val ResourcesPolicyJSONPath = toResourcesPath(ResourceLocator.ResourceNames.POLICY_JSON).r
+    final val ResourcesPolicyJSONPath = toResourcesPath(ResourceLocator.ResourceNames.POLICY_JSON).r
 
-  final val ResourceEventPath = toEventPath("rcevent").r
+    final val ResourceEventPath = toEventPath("rcevent").r
 
-  final val IMEventPath = toEventPath("imevent").r
+    final val IMEventPath = toEventPath("imevent").r
 
-  /**
-   * Use this URI path to query for the user balance. The parenthesized regular expression part
-   * represents the user ID.
-   */
-  final val UserBalancePath = "/user/([^/]+)/balance/?".r
+    /**
+     * Use this URI path to query for the user balance. The parenthesized regular expression part
+     * represents the user ID.
+     */
+    final val UserBalancePath = "/user/([^/]+)/balance/?".r
 
-  /**
-   * Use this URI path to query for the user state.
-   */
-  final val UserStatePath = "/user/([^/]+)/state/?".r
+    /**
+     * Use this URI path to query for the user state.
+     */
+    final val UserStatePath = "/user/([^/]+)/state/?".r
 
-  final val UserWalletPath = "/user/([^/]+)/wallet/?".r
+    final val UserWalletPath = "/user/([^/]+)/wallet/?".r
+
+    final val UserBillPath = "/user/([^/]+)/bill/([0-9]+)/([0-9]+)/?".r
 
   final val UserActorCacheContentsPath = (AdminPrefix + "/cache/actor/user/contents").r
   final val UserActorCacheCountPath    = (AdminPrefix + "/cache/actor/user/size").r