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}
/**
classOf[ProcessIMEvent],
classOf[GetUserWalletRequest],
classOf[GetUserBalanceRequest],
+ classOf[GetUserBillRequest],
classOf[GetUserStateRequest]),
Set(classOf[InitializeUserActorState],
classOf[AquariumPropertiesLoaded]))
--- /dev/null
+/*
+ * 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
+}
+
--- /dev/null
+/*
+ * 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
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
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
/**
*
updateLatestResourceEventIDFrom(rcEvent)
}
+ var oldTotalCredits = this._workingUserState.totalCredits
// FIXME check these
if(nowYear != eventYear || nowMonth != eventMonth) {
DEBUG(
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
--- /dev/null
+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
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
import gr.grnet.aquarium.store.memory.MemStoreProvider
import java.io.File
import com.ckkloverdos.resource.FileStreamResource
+import scala.Some
/*
* 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 = _
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) = {
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
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
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
/**
*
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 = {
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