2 * Copyright 2011-2012 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
30 * The views and conclusions contained in the software and
31 * documentation are those of the authors and should not be
32 * interpreted as representing official policies, either expressed
33 * or implied, of GRNET S.A.
36 package gr.grnet.aquarium.logic.accounting
38 import dsl.{Timeslot, DSLPolicy, DSL}
39 import gr.grnet.aquarium.Aquarium._
40 import java.io.{InputStream, FileInputStream, File}
42 import gr.grnet.aquarium.util.Loggable
43 import java.util.concurrent.atomic.AtomicReference
44 import gr.grnet.aquarium.Aquarium.Keys
45 import com.ckkloverdos.maybe.{Failed, NoVal, Just}
46 import collection.immutable.{TreeMap, SortedMap}
47 import gr.grnet.aquarium.{AquariumException, Aquarium}
48 import gr.grnet.aquarium.util.date.{MutableDateCalc, TimeHelpers}
51 * Searches for and loads the applicable accounting policy
53 * @author Georgios Gousios <gousiosg@gmail.com>
55 object Policy extends DSL with Loggable {
57 /* Pointer to the latest policy */
58 private[logic] lazy val policies = {
59 new AtomicReference[SortedMap[Timeslot, DSLPolicy]](reloadPolicies)
62 /* Pointer to the latest policy */
63 private lazy val currentPolicy = {new AtomicReference[DSLPolicy](latestPolicy)}
65 /* Aquarium to use for loading information about the policy store */
66 private var config: Aquarium = _
69 * Get the latest defined policy.
71 def policy = currentPolicy.get
74 * Get the policy that is valid at the specified time instance.
76 def policy(at: Date): DSLPolicy = {
78 a => (a._1.from.before(at) && a._1.to.after(at)) ||
79 (a._1.from.before(at) && a._1.to == Long.MaxValue)
83 throw new AquariumException("No valid policy for date: %s".format(new MutableDateCalc(at)))
88 * Get the policies that are valid between the specified time instances,
89 * in a map whose keys are sorted by time.
91 def policies(from: Date, to: Date): SortedMap[Timeslot, DSLPolicy] = {
93 a => a._1.from.before(from) &&
99 * Get the policies that are valid throughout the specified
100 * [[gr.grnet.aquarium.logic.accounting.dsl.Timeslot]]
102 def policies(t: Timeslot): SortedMap[Timeslot, DSLPolicy] = policies(t.from, t.to)
105 * Load and parse a policy from file.
107 def loadPolicyFromFile(pol: File): DSLPolicy = {
109 val stream = pol.exists() match {
111 logger.info("Using policy file %s".format(pol.getAbsolutePath))
112 new FileInputStream(pol)
114 logger.warn(("Cannot find user configured policy file %s, " +
115 "looking for default policy").format(pol.getAbsolutePath))
116 getClass.getClassLoader.getResourceAsStream("policy.yaml") match {
117 case x: InputStream =>
118 logger.warn("Using default policy, this is problably not what you want")
121 logger.error("No valid policy file found, Aquarium will fail")
129 * Trigger a policy update cycle.
131 def updatePolicies = synchronized {
132 //XXX: The following update should happen as one transaction
133 val tmpPol = reloadPolicies
134 currentPolicy.set(latestPolicy)
139 * Find the latest policy in the list of policies.
141 private def latestPolicy =
142 policies.get.foldLeft((Timeslot(new Date(1), new Date(2)) -> DSLPolicy.emptyPolicy)) {
144 if (acc._2 == DSLPolicy.emptyPolicy)
146 else if (p._1.after(acc._1))
153 * Set the configurator to use for loading policy stores. Should only
154 * used for unit testing.
156 def withConfigurator(config: Aquarium): Unit =
160 * Check whether the policy definition file (in whichever path) is
161 * newer than the latest stored policy, reload and set it as current.
162 * This method has side-effects to this object's state.
164 private[logic] def reloadPolicies: SortedMap[Timeslot, DSLPolicy] =
166 reloadPolicies(Instance)
168 reloadPolicies(config)
170 private def reloadPolicies(config: Aquarium):
171 SortedMap[Timeslot, DSLPolicy] = {
172 //1. Load policies from db
173 val pol = config.policyStore.loadPolicyEntriesAfter(0)
175 //2. Check whether policy file has been updated
176 val latestPolicyChange = if (pol.isEmpty) 0 else pol.last.validFrom
177 val policyf = Instance.findConfigFile(PolicyConfName, Keys.aquarium_policy, PolicyConfName)
180 if (policyf.exists) {
181 if (policyf.lastModified > latestPolicyChange) {
182 logger.info("Policy file updated since last check, reloading")
185 logger.info("Policy file not changed since last check")
188 logger.warn("User specified policy file %s does not exist, " +
189 "using stored policy information".format(policyf.getAbsolutePath))
193 val ts = TimeHelpers.nowMillis()
194 val parsedNew = loadPolicyFromFile(policyf)
195 val newPolicy = parsedNew.toPolicyEntry
197 config.policyStore.findPolicyEntry(newPolicy.id) match {
199 logger.warn("Policy file contents not modified")
202 val toUpdate = pol.last.copy(validTo = ts - 1)
203 config.policyStore.updatePolicyEntry(toUpdate)
204 config.policyStore.storePolicyEntry(newPolicy)
206 config.policyStore.storePolicyEntry(newPolicy)
211 config.policyStore.loadPolicyEntriesAfter(0).foldLeft(new TreeMap[Timeslot, DSLPolicy]()){
213 acc ++ Map(Timeslot(p.validFrom, p.validTo) -> parse(p.policyYAML))