325608f0998fda391486be69569ebe558b16484e
[aquarium] / src / main / scala / gr / grnet / aquarium / store / LocalFSEventStore.scala
1 /*
2  * Copyright 2011-2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
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.
16  *
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.
29  *
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.
34  */
35
36 package gr.grnet.aquarium.store
37
38 import gr.grnet.aquarium.Aquarium
39 import java.io.{FileOutputStream, File}
40 import gr.grnet.aquarium.util.{Loggable, stringOfStackTrace, makeBytes, UTF_8_Charset}
41 import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc}
42 import gr.grnet.aquarium.event.model.im.IMEventModel
43 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
44
45 /**
46  * This is used whenever the property `events.store.folder` is setup in aquarium configuration.
47  *
48  * This is mainly a debugging aid. You normally want to disable it in a production environment.
49  *
50  * @author Christos KK Loverdos <loverdos@gmail.com>
51  */
52
53 object LocalFSEventStore extends Loggable {
54   private[this] final val NewLine  = makeBytes("\n", UTF_8_Charset) // super-fluous!
55
56   private[this] def writeToFile(file: File,
57                                 dataHeader: String,
58                                 data: Array[Byte],
59                                 dataFooter: String,
60                                 appendString: Option[String] = None): Unit = {
61     val out = new FileOutputStream(file)
62
63     out.write(makeBytes(dataHeader, UTF_8_Charset))
64     out.write(data)
65     out.write(makeBytes(dataFooter, UTF_8_Charset))
66
67     appendString match {
68       case Some(s) ⇒
69         out.write(NewLine)
70         out.write(makeBytes(s, UTF_8_Charset))
71       case None ⇒
72     }
73
74     out.flush()
75     out.close()
76
77     logger.debug("Wrote to file {}", file.getCanonicalPath)
78   }
79
80   private[this] def dateTagForFolder(): String = {
81     new MutableDateCalc(TimeHelpers.nowMillis()).toYYYYMMDD
82   }
83
84   private[this] def createResourceEventsFolder(root: File): File = {
85     val folder0 = new File(root, "rc")
86     val folder = new File(folder0, "rc-%s".format(dateTagForFolder()))
87     folder.mkdirs()
88     folder
89   }
90
91   private[this] def createIMEventsFolder(root: File): File = {
92     val folder0 = new File(root, "im")
93     val folder = new File(folder0, "im-%s".format(dateTagForFolder()))
94     folder.mkdirs()
95     folder
96   }
97
98   private[this] def writeJson(tag: String,
99                               folder: File,
100                               jsonPayload: Array[Byte],
101                               occurredString: String,
102                               extraName: Option[String],
103                               isParsed: Boolean,
104                               appendString: Option[String]): Unit = {
105     val file = new File(
106       folder,
107       "%s-%s%s.%s.json".format(
108         tag,
109         occurredString,
110         extraName match {
111           case Some(s) ⇒ "-" + s
112           case None    ⇒ ""
113         },
114         if(isParsed) "p" else "u"
115       ))
116
117     val dataHeader = "// %s bytes of payload\n".format(jsonPayload.length)
118     val dataFooter = "\n" + dataHeader
119
120     writeToFile(
121       file,
122       dataHeader,
123       jsonPayload,
124       dataFooter,
125       appendString)
126   }
127
128   def storeUnparsedResourceEvent(aquarium: Aquarium, initialPayload: Array[Byte], exception: Throwable): Unit = {
129     for(root <- aquarium.eventsStoreFolder) {
130       val occurredMDC = new MutableDateCalc(TimeHelpers.nowMillis())
131       val occurredString = occurredMDC.toFilename_YYYYMMDDHHMMSSSSS
132       val rcEventsFolder = createResourceEventsFolder(root)
133       val trace = stringOfStackTrace(exception)
134
135       writeJson("rc", rcEventsFolder, initialPayload, occurredString, None, false, Some(trace))
136     }
137   }
138
139   def storeResourceEvent(aquarium: Aquarium, event: ResourceEventModel, initialPayload: Array[Byte]): Unit = {
140     if(!aquarium.saveResourceEventsToEventsStoreFolder) {
141       return
142     }
143
144     require(event ne null, "Resource event must be not null")
145
146     for(root <- aquarium.eventsStoreFolder) {
147       val occurredMDC = new MutableDateCalc(event.occurredMillis)
148       val occurredString = occurredMDC.toFilename_YYYYMMDDHHMMSSSSS
149       val rcEventsFolder = createResourceEventsFolder(root)
150
151       // Store parsed file
152       writeJson(
153         "rc",
154         rcEventsFolder,
155         initialPayload,
156         occurredString,
157         Some("[%s]-[%s]-[%s]-[%s]".format(
158           event.id,
159           event.userID,
160           event.resource,
161           event.instanceID)),
162         true,
163         None
164       )
165     }
166   }
167
168   def storeUnparsedIMEvent(aquarium: Aquarium, initialPayload: Array[Byte], exception: Throwable): Unit = {
169     for(root <- aquarium.eventsStoreFolder) {
170       val occurredMDC = new MutableDateCalc(TimeHelpers.nowMillis())
171       val occurredString = occurredMDC.toFilename_YYYYMMDDHHMMSSSSS
172       val imEventsFolder = createIMEventsFolder(root)
173       val trace = stringOfStackTrace(exception)
174
175       writeJson("im", imEventsFolder, initialPayload, occurredString, None, false, Some(trace))
176     }
177   }
178
179   def storeIMEvent(aquarium: Aquarium, event: IMEventModel, initialPayload: Array[Byte]): Unit = {
180     if(!aquarium.saveIMEventsToEventsStoreFolder) {
181       return
182     }
183
184     require(event ne null, "IM event must be not null")
185
186     for(root <- aquarium.eventsStoreFolder) {
187       val occurredMDC = new MutableDateCalc(event.occurredMillis)
188       val occurredString = occurredMDC.toFilename_YYYYMMDDHHMMSSSSS
189       val imEventsFolder = createIMEventsFolder(root)
190
191       writeJson(
192         "im",
193         imEventsFolder,
194         initialPayload,
195         occurredString,
196         Some("[%s]-[%s]".format(event.id, event.userID)),
197         true,
198         None
199       )
200     }
201   }
202 }