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 java.io.{InputStream, FileInputStream, File}
41 import gr.grnet.aquarium.util.Loggable
42 import java.util.concurrent.atomic.AtomicReference
43 import collection.immutable.{TreeMap, SortedMap}
44 import gr.grnet.aquarium.{ResourceLocator, AquariumAwareSkeleton, AquariumException, Aquarium}
45 import gr.grnet.aquarium.util.date.{MutableDateCalc, TimeHelpers}
46 import com.ckkloverdos.maybe.{Failed, Just}
49 * Searches for and loads the applicable accounting policy
51 * @author Georgios Gousios <gousiosg@gmail.com>
53 object Policy extends DSL with Loggable with AquariumAwareSkeleton {
55 /* Pointer to the latest policy */
56 private[logic] lazy val policies = {
57 new AtomicReference[SortedMap[Timeslot, DSLPolicy]](reloadPolicies)
60 /* Pointer to the latest policy */
61 private lazy val currentPolicy = {new AtomicReference[DSLPolicy](latestPolicy)}
63 /* Aquarium to use for loading information about the policy store */
64 private var config: Aquarium = _
67 * Get the latest defined policy.
69 def policy = currentPolicy.get
72 * Get the policy that is valid at the specified time instance.
74 def policy(at: Date): DSLPolicy = {
76 a => (a._1.from.before(at) && a._1.to.after(at)) ||
77 (a._1.from.before(at) && a._1.to == Long.MaxValue)
81 throw new AquariumException("No valid policy for date: %s".format(new MutableDateCalc(at)))
86 * Get the policies that are valid between the specified time instances,
87 * in a map whose keys are sorted by time.
89 def policies(from: Date, to: Date): SortedMap[Timeslot, DSLPolicy] = {
91 a => a._1.from.before(from) &&
97 * Get the policies that are valid throughout the specified
98 * [[gr.grnet.aquarium.logic.accounting.dsl.Timeslot]]
100 def policies(t: Timeslot): SortedMap[Timeslot, DSLPolicy] = policies(t.from, t.to)
103 * Load and parse a policy from file.
105 def loadPolicyFromFile(pol: File): DSLPolicy = {
107 val stream = pol.exists() match {
109 logger.info("Using policy file %s".format(pol.getAbsolutePath))
110 new FileInputStream(pol)
112 logger.warn(("Cannot find user configured policy file %s, " +
113 "looking for default policy").format(pol.getAbsolutePath))
114 getClass.getClassLoader.getResourceAsStream("policy.yaml") match {
115 case x: InputStream =>
116 logger.warn("Using default policy, this is problably not what you want")
119 logger.error("No valid policy file found, Aquarium will fail")
127 * Trigger a policy update cycle.
129 def updatePolicies = synchronized {
130 //XXX: The following update should happen as one transaction
131 val tmpPol = reloadPolicies
132 currentPolicy.set(latestPolicy)
137 * Find the latest policy in the list of policies.
139 private def latestPolicy =
140 policies.get.foldLeft((Timeslot(new Date(1), new Date(2)) -> DSLPolicy.emptyPolicy)) {
142 if (acc._2 == DSLPolicy.emptyPolicy)
144 else if (p._1.after(acc._1))
151 * Set the configurator to use for loading policy stores. Should only
152 * used for unit testing.
154 def withConfigurator(config: Aquarium): Unit =
158 * Check whether the policy definition file (in whichever path) is
159 * newer than the latest stored policy, reload and set it as current.
160 * This method has side-effects to this object's state.
162 private[logic] def reloadPolicies: SortedMap[Timeslot, DSLPolicy] =
164 reloadPolicies(aquarium)
166 reloadPolicies(config)
168 private def reloadPolicies(config: Aquarium):
169 SortedMap[Timeslot, DSLPolicy] = {
170 //1. Load policies from db
171 val pol = config.policyStore.loadPolicyEntriesAfter(0)
173 //2. Check whether policy file has been updated
174 val latestPolicyChange = if (pol.isEmpty) 0 else pol.last.validFrom
176 val policyf = ResourceLocator.Resources.PolicyYAMLResource
179 if (policyf.exists) {
182 logger.warn("User specified policy file %s does not exist, " +
183 "using stored policy information".format(policyf.url))
187 val ts = TimeHelpers.nowMillis()
188 val parsedNewM = policyf.mapInputStream(parse).toMaybeEither
189 val parsedNew = parsedNewM match {
190 case Just(parsedNew) ⇒ parsedNew
191 case Failed(e) ⇒ throw e
193 val newPolicy = parsedNew.toPolicyEntry
195 config.policyStore.findPolicyEntry(newPolicy.id) match {
197 logger.warn("Policy file contents not modified")
200 val toUpdate = pol.last.copy(validTo = ts - 1)
201 config.policyStore.updatePolicyEntry(toUpdate)
202 config.policyStore.storePolicyEntry(newPolicy)
204 config.policyStore.storePolicyEntry(newPolicy)
209 config.policyStore.loadPolicyEntriesAfter(0).foldLeft(new TreeMap[Timeslot, DSLPolicy]()){
211 acc ++ Map(Timeslot(p.validFrom, p.validTo) -> parse(p.policyYAML))