Initial commit of Ocean's local development

This commit is contained in:
Thorsten Sommer 2014-04-26 11:18:56 +02:00
parent 9944a9e1df
commit 86451938ec
116 changed files with 3320 additions and 0 deletions

19
Configuration/Doc.go Normal file
View File

@ -0,0 +1,19 @@
/*
This package reads the configuration file from the disk (file: configuration.json).
This configuratin is just used to specific the configuration database to go further.
An example configuration.json file:
{
"ConfigDBHostname" : "localhost:27017",
"ConfigDBDatabase" : "MyDatabase",
"ConfigDBConfigurationCollection" : "Configuration",
"ConfigDBConfigurationCollectionUsername" : "ConfigurationUsername",
"ConfigDBConfigurationCollectionPassword" : "ConfigurationPassword"
}
Hint #1: Ocean is using MongoDB as database ;-)
Hint #2: Normally, you do not use this package at all, because the application configuration should persist
inside the configuration database. Your task: Provide a configuration.json file at the installation directory ;-)
*/
package Configuration

6
Configuration/Init.go Normal file
View File

@ -0,0 +1,6 @@
package Configuration
func init() {
readConfiguration()
isInit = true
}

View File

@ -0,0 +1,10 @@
package Meta
// The type which matches the configuration file:
type Configuration struct {
ConfigDBHostname string
ConfigDBDatabase string
ConfigDBConfigurationCollection string
ConfigDBConfigurationCollectionUsername string
ConfigDBConfigurationCollectionPassword string
}

View File

@ -0,0 +1,14 @@
package Configuration
import "github.com/SommerEngineering/Ocean/Configuration/Meta"
/*
Read the whole configuration and enable Ocean to get the configuration database.
Hint: Normally, you do not use this package at all, because the application configuration should persist
inside the configuration database.
*/
func Read() (result Meta.Configuration) {
result = configuration
return
}

View File

@ -0,0 +1,52 @@
package Configuration
import "encoding/json"
import "os"
import "path/filepath"
import "github.com/SommerEngineering/Ocean/Log"
import "github.com/SommerEngineering/Ocean/Log/Meta"
func readConfiguration() {
if isInit {
Log.LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityNone, Meta.ImpactNone, Meta.MessageNameINIT, `The configuration package is already init!`)
return
} else {
Log.LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameCONFIGURATION, `Init of configuration starting.`)
}
currentDir, dirError := os.Getwd()
if dirError != nil {
panic(`Was not able to read the working directory: ` + dirError.Error())
return
}
currentPath := filepath.Join(currentDir, filename)
if _, errFile := os.Stat(currentPath); errFile != nil {
if os.IsNotExist(errFile) {
panic(`It was not possible to find the necessary configuration file 'configuration.json' at the application directory.`)
} else {
panic(`There was an error while open the configuration: ` + errFile.Error())
}
}
file, fileError := os.Open(currentPath)
defer file.Close()
if fileError != nil {
panic(`The configuration file is not accessible: ` + fileError.Error())
return
}
decoder := json.NewDecoder(file)
decError := decoder.Decode(&configuration)
if decError != nil {
panic(`Decoding of the configuration file was not possible: ` + decError.Error())
}
Log.LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameINIT, `Init of configuration is done.`)
isInit = true
return
}

View File

@ -0,0 +1,11 @@
package Configuration
import "github.com/SommerEngineering/Ocean/Configuration/Meta"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
var (
filename = "configuration.json" // Where is the configuration located?
configuration Meta.Configuration = Meta.Configuration{} // The loaded configuration
isInit = false // Is the configuration loaded?
senderName LM.Sender = `System::Configuration`
)

View File

@ -0,0 +1,65 @@
package ConfigurationDB
import "labix.org/v2/mgo/bson"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func checkConfiguration() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Check now the configuration database.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Done checking the configuration database.`)
CheckSingleConfigurationPresentsAndAddIfMissing(`InternalCommPassword`, `please replace this with e.g. a random GUID, etc.`)
CheckSingleConfigurationPresentsAndAddIfMissing(`CustomerDBHost`, `localhost:27017`)
CheckSingleConfigurationPresentsAndAddIfMissing(`CustomerDBDatabase`, `Ocean`)
CheckSingleConfigurationPresentsAndAddIfMissing(`CustomerDBUsername`, `root`)
CheckSingleConfigurationPresentsAndAddIfMissing(`CustomerDBPassword`, `please replace this with a good password`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBHost`, `localhost:27017`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBDatabase`, `Ocean`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBUsername`, `root`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBPassword`, `please replace this with a good password`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBCacheSizeNumberOfEvents`, `50`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBCacheSizeTime2FlushSeconds`, `6`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogBufferSize`, `500`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDeviceDelayNumberEvents`, `600`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDeviceDelayTime2FlushSeconds`, `5`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogTimeoutSeconds`, `4`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogStaticFileRequests`, `false`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogUseDatabaseLogging`, `false`)
CheckSingleConfigurationPresentsAndAddIfMissing(`LogUseConsoleLogging`, `true`)
CheckSingleConfigurationPresentsAndAddIfMissing(`NumGenActiveHosts`, `please replace this with the correct hostname of the host which is the master number generator`)
CheckSingleConfigurationPresentsAndAddIfMissing(`NumGenGetHandler`, `http://localhost:80/next/number`)
CheckSingleConfigurationPresentsAndAddIfMissing(`NumGenBufferSize`, `12`)
CheckSingleConfigurationPresentsAndAddIfMissing(`OceanHostnameAndPort`, `:60000`)
CheckSingleConfigurationPresentsAndAddIfMissing(`OceanUtilizeCPUs`, `2`)
CheckSingleConfigurationPresentsAndAddIfMissing(`FilenameWebResources`, `web.zip`)
CheckSingleConfigurationPresentsAndAddIfMissing(`MapStaticFiles2Root`, `false`)
CheckSingleConfigurationPresentsAndAddIfMissing(`MapStaticFiles2RootRootFile`, `index.html`)
CheckSingleConfigurationPresentsAndAddIfMissing(`EnableStaticFiles`, `true`)
CheckSingleConfigurationPresentsAndAddIfMissing(`robots.txt`, `User-agent: *
Disallow:`)
}
/*
Use this function to ensure that the database contains at least a default value for the configuration.
*/
func CheckSingleConfigurationPresentsAndAddIfMissing(name, value string) {
if !checkSingleConfigurationPresents(name) {
addSingleConfiguration(name, value)
}
}
func checkSingleConfigurationPresents(name string) (result bool) {
selection := bson.D{{"Name", name}}
count, _ := collection.Find(selection).Count()
return count > 0
}
func addSingleConfiguration(name, value string) {
entry := ConfigurationDBEntry{}
entry.Name = name
entry.Value = value
collection.Insert(entry)
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Add a missing configuration to the configuration database.`, `Name=`+name, `Value=`+value)
}

10
ConfigurationDB/Doc.go Normal file
View File

@ -0,0 +1,10 @@
/*
This package provides the configuration database access: Ocean uses MongoDB as database! The configuration is represented as
name value pairs. Both, the name and the value, are strings. If you need numbers for the configuration, you have to convert
these strings in to numbers afterwards. Use the Read() function to read a specific configuration value.
To provide own application configurations, use the function CheckSingleConfigurationPresentsAndAddIfMissing() to ensure, that
at least a default value is present in the database. Collect all these function calls inside a init() function somewhere at your
application.
*/
package ConfigurationDB

38
ConfigurationDB/Init.go Normal file
View File

@ -0,0 +1,38 @@
package ConfigurationDB
import "labix.org/v2/mgo"
import "github.com/SommerEngineering/Ocean/Configuration"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func init() {
config := Configuration.Read()
// Connect to MongoDB:
if newSession, errDial := mgo.Dial(config.ConfigDBHostname); errDial != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `It was not possible to connect to the MongoDB host `+config.ConfigDBHostname, errDial.Error())
return
} else {
session = newSession
}
// Use the correct database:
db = session.DB(config.ConfigDBDatabase)
// Login:
if errLogin := db.Login(config.ConfigDBConfigurationCollectionUsername, config.ConfigDBConfigurationCollectionPassword); errLogin != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `It was not possible to login the user `+config.ConfigDBConfigurationCollectionUsername, errLogin.Error())
return
}
// Get the collection:
collection = db.C(config.ConfigDBConfigurationCollection)
// Take care about the index:
collection.EnsureIndexKey(`Name`)
collection.EnsureIndexKey(`Value`)
checkConfiguration()
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `The configuration database is now ready.`)
}

24
ConfigurationDB/Read.go Normal file
View File

@ -0,0 +1,24 @@
package ConfigurationDB
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
import "labix.org/v2/mgo/bson"
/*
This function reads the current configuration value.
*/
func Read(name string) (value string) {
if name == `` {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `Was not able to read a configuration out of the database.`, `The given name was nil!`)
return
}
result := ConfigurationDBEntry{}
if errFind := collection.Find(bson.D{{"Name", name}}).One(&result); errFind != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `Was not able to read a configuration out of the database.`, `Error while find.`, errFind.Error())
return
}
value = result.Value
return
}

View File

@ -0,0 +1,6 @@
package ConfigurationDB
type ConfigurationDBEntry struct {
Name string `bson:"Name"`
Value string `bson:"Value"`
}

View File

@ -0,0 +1,19 @@
package ConfigurationDB
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
/*
Do not use this type by your own! It is a Ocean internal type to provide a shutdown function for the configuration database.
*/
type ShutdownFunction struct {
}
/*
If the Ocean server is shutting down, this function is called to close the database.
*/
func (a ShutdownFunction) Shutdown() {
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Close now the configuration database connection.`)
db.Logout()
session.Close()
}

View File

@ -0,0 +1,13 @@
package ConfigurationDB
import "labix.org/v2/mgo"
import "github.com/SommerEngineering/Ocean/Configuration/Meta"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
var (
session *mgo.Session = nil
db *mgo.Database = nil
collection *mgo.Collection = nil
config Meta.Configuration = Meta.Configuration{}
senderName LM.Sender = `System::ConfigurationDB`
)

19
CustomerDB/AccessDB.go Normal file
View File

@ -0,0 +1,19 @@
package CustomerDB
import "labix.org/v2/mgo"
/*
Get the database instance of the MGo Mongo driver.
*/
func DB() (result *mgo.Database) {
result = db
return
}
/*
Get directly the GridFS instance of the Mgo Mongo driver.
*/
func GridFS() (result *mgo.GridFS) {
result = gridFS
return
}

5
CustomerDB/Doc.go Normal file
View File

@ -0,0 +1,5 @@
/*
This package provides access to the customer database. To specific the customer database, please use the configuration database.
By the way: Ocean uses MongoDB as database ;-) This package is just a facade to hide the complexity from MongoDB and its driver.
*/
package CustomerDB

52
CustomerDB/Init.go Normal file
View File

@ -0,0 +1,52 @@
package CustomerDB
import "labix.org/v2/mgo"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func init() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Init the customer database.`)
databaseHost := ConfigurationDB.Read(`CustomerDBHost`)
databaseDB := ConfigurationDB.Read(`CustomerDBDatabase`)
databaseUsername := ConfigurationDB.Read(`CustomerDBUsername`)
databasePassword := ConfigurationDB.Read(`CustomerDBPassword`)
// Connect to MongoDB:
if newSession, errDial := mgo.Dial(databaseHost); errDial != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `It was not possible to connect to the MongoDB host `+databaseHost, errDial.Error())
return
} else {
session = newSession
}
// Use the correct database:
db = session.DB(databaseDB)
if db == nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the customer database.`)
return
}
// Login:
if errLogin := db.Login(databaseUsername, databasePassword); errLogin != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `It was not possible to login the user `+databaseUsername, errLogin.Error())
return
}
// Get the GridFS:
gridFS = db.GridFS(`fs`)
if gridFS == nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the GridFS from the database.`)
return
}
// Ensure the indexes for the GridFS:
filesCollection := gridFS.Files
filesCollection.EnsureIndexKey(`uploadDate`)
filesCollection.EnsureIndexKey(`filename`)
filesCollection.EnsureIndexKey(`filename`, `uploadDate`)
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Customer database is now ready.`)
}

19
CustomerDB/Shutdown.go Normal file
View File

@ -0,0 +1,19 @@
package CustomerDB
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
/*
Please do not use this type. It is an internal type of Ocean to provide a shutdown function!
*/
type ShutdownFunction struct {
}
/*
This function is called if the Ocean server is shutting down.
*/
func (a ShutdownFunction) Shutdown() {
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Close now the customer database connection.`)
db.Logout()
session.Close()
}

11
CustomerDB/Variables.go Normal file
View File

@ -0,0 +1,11 @@
package CustomerDB
import "labix.org/v2/mgo"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
var (
session *mgo.Session = nil
db *mgo.Database = nil
gridFS *mgo.GridFS = nil
senderName LM.Sender = `System::CustomerDB`
)

122
ICCC/Data2Message.go Normal file
View File

@ -0,0 +1,122 @@
package ICCC
import "fmt"
import "reflect"
import "strconv"
func Data2Message(target interface{}, data map[string][]string) (channel, command string, obj interface{}) {
if data == nil || len(data) == 0 {
channel = ``
command = ``
obj = nil
return
}
element := reflect.ValueOf(target)
element = element.Elem()
elementType := element.Type()
channel = data[`channel`][0]
command = data[`command`][0]
for i := 0; i < element.NumField(); i++ {
field := element.Field(i)
switch field.Kind().String() {
case `int64`:
mapName := fmt.Sprintf(`int:%s`, elementType.Field(i).Name)
mapValue := data[mapName][0]
v, _ := strconv.ParseInt(mapValue, 10, 64)
field.SetInt(v)
case `string`:
mapName := fmt.Sprintf(`str:%s`, elementType.Field(i).Name)
mapValue := data[mapName][0]
field.SetString(mapValue)
case `float64`:
mapName := fmt.Sprintf(`f64:%s`, elementType.Field(i).Name)
mapValue := data[mapName][0]
v, _ := strconv.ParseFloat(mapValue, 64)
field.SetFloat(v)
case `bool`:
mapName := fmt.Sprintf(`bool:%s`, elementType.Field(i).Name)
mapValue := data[mapName][0]
v, _ := strconv.ParseBool(mapValue)
field.SetBool(v)
case `uint8`:
mapName := fmt.Sprintf(`ui8:%s`, elementType.Field(i).Name)
mapValue := data[mapName][0]
v, _ := strconv.ParseUint(mapValue, 16, 8)
field.SetUint(v)
case `slice`:
sliceInterface := field.Interface()
sliceKind := reflect.ValueOf(sliceInterface).Type().String()
switch sliceKind {
case `[]uint8`: // bytes
mapName := fmt.Sprintf(`ui8[]:%s`, elementType.Field(i).Name)
mapValues := data[mapName]
fieldLen := len(mapValues)
fieldData := make([]uint8, fieldLen, fieldLen)
for n, mapValue := range mapValues {
v, _ := strconv.ParseUint(mapValue, 16, 8)
fieldData[n] = byte(v)
}
fieldDataValue := reflect.ValueOf(fieldData)
field.Set(fieldDataValue)
case `[]int64`:
mapName := fmt.Sprintf(`int[]:%s`, elementType.Field(i).Name)
mapValues := data[mapName]
fieldLen := len(mapValues)
fieldData := make([]int64, fieldLen, fieldLen)
for n, mapValue := range mapValues {
v, _ := strconv.ParseInt(mapValue, 10, 64)
fieldData[n] = v
}
fieldDataValue := reflect.ValueOf(fieldData)
field.Set(fieldDataValue)
case `[]bool`:
mapName := fmt.Sprintf(`bool[]:%s`, elementType.Field(i).Name)
mapValues := data[mapName]
fieldLen := len(mapValues)
fieldData := make([]bool, fieldLen, fieldLen)
for n, mapValue := range mapValues {
v, _ := strconv.ParseBool(mapValue)
fieldData[n] = v
}
fieldDataValue := reflect.ValueOf(fieldData)
field.Set(fieldDataValue)
case `[]string`:
mapName := fmt.Sprintf(`str[]:%s`, elementType.Field(i).Name)
mapValues := data[mapName]
fieldDataValue := reflect.ValueOf(mapValues)
field.Set(fieldDataValue)
case `[]float64`:
mapName := fmt.Sprintf(`f64[]:%s`, elementType.Field(i).Name)
mapValues := data[mapName]
fieldLen := len(mapValues)
fieldData := make([]float64, fieldLen, fieldLen)
for n, mapValue := range mapValues {
v, _ := strconv.ParseFloat(mapValue, 64)
fieldData[n] = v
}
fieldDataValue := reflect.ValueOf(fieldData)
field.Set(fieldDataValue)
}
}
}
obj = target
return
}

4
ICCC/Doc.go Normal file
View File

@ -0,0 +1,4 @@
/*
This is the "[I]nter Data [C]enter Se[c]ure [C]ommunication" :)
*/
package ICCC

34
ICCC/HTTPConnector.go Normal file
View File

@ -0,0 +1,34 @@
package ICCC
import "fmt"
import "net/http"
import "github.com/SommerEngineering/Ocean/Tools"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func ICCCHandler(response http.ResponseWriter, request *http.Request) {
if errParse := request.ParseForm(); errParse != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameNETWORK, `Was not able to parse the HTTP form data from an ICCC message!`)
http.NotFound(response, request)
return
}
messageData := map[string][]string(request.PostForm)
channel := messageData[`channel`][0]
command := messageData[`command`][0]
password := messageData[`InternalCommPassword`][0]
if password != Tools.InternalCommPassword() {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityCritical, LM.ImpactNone, LM.MessageNamePASSWORD, `Received a ICCC message with wrong password!`, request.RemoteAddr)
http.NotFound(response, request)
return
}
key := fmt.Sprintf(`%s::%s`, channel, command)
listener := listeners[key]
if listener == nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to find the correct listener for these ICCC message.`, `channel=`+channel, `command`+command, `hostname=`+Tools.ThisHostname())
} else {
listener(messageData)
}
}

25
ICCC/Init.go Normal file
View File

@ -0,0 +1,25 @@
package ICCC
import "strings"
import "container/list"
import "github.com/SommerEngineering/Ocean/Tools"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func init() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Start init of ICCC.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Done init ICCC.`)
cacheListenerDatabase = list.New()
listeners = make(map[string]func(data map[string][]string))
allHostsIPAddresses := Tools.ReadAllIPAddresses4ThisHost()
oceanHostnameAndPort := ConfigurationDB.Read(`OceanHostnameAndPort`)
port := oceanHostnameAndPort[strings.Index(oceanHostnameAndPort, `:`):]
correctAddressWithPort = allHostsIPAddresses[0] + port
initDB()
registerHost2Database()
initCacheTimer()
}

58
ICCC/InitDB.go Normal file
View File

@ -0,0 +1,58 @@
package ICCC
import "labix.org/v2/mgo"
import "github.com/SommerEngineering/Ocean/CustomerDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func initDB() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Start init of the ICCC collections.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Done init the ICCC collection.`)
// Get the database:
db = CustomerDB.DB()
if db == nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the customer database.`)
return
}
// Get my collections:
collectionListener = db.C(`ICCCListener`)
collectionHosts = db.C(`ICCCHosts`)
// Take care about the indexes for ICCCListener:
collectionListener.EnsureIndexKey(`Command`)
collectionListener.EnsureIndexKey(`Command`, `IsActive`)
collectionListener.EnsureIndexKey(`Command`, `Channel`)
collectionListener.EnsureIndexKey(`Command`, `Channel`, `IsActive`)
collectionListener.EnsureIndexKey(`Channel`)
collectionListener.EnsureIndexKey(`Channel`, `IsActive`)
collectionListener.EnsureIndexKey(`Channel`, `Command`, `IPAddressPort`, `IsActive`)
collectionListener.EnsureIndexKey(`Channel`, `Command`, `IsActive`)
collectionListener.EnsureIndexKey(`IsActive`)
collectionListener.EnsureIndexKey(`IsActive`, `IPAddressPort`)
collectionListener.EnsureIndexKey(`IPAddressPort`)
indexName1 := mgo.Index{}
indexName1.Key = []string{`Channel`, `Command`, `IPAddressPort`}
indexName1.Unique = true
collectionListener.EnsureIndex(indexName1)
// Index for hosts:
collectionHosts.EnsureIndexKey(`Hostname`, `IPAddressPort`)
indexName2 := mgo.Index{}
indexName2.Key = []string{`Hostname`}
indexName2.Unique = true
collectionHosts.EnsureIndex(indexName2)
indexName3 := mgo.Index{}
indexName3.Key = []string{`IPAddressPort`}
indexName3.Unique = true
collectionHosts.EnsureIndex(indexName3)
}

41
ICCC/ListenerCache.go Normal file
View File

@ -0,0 +1,41 @@
package ICCC
import "fmt"
import "time"
import "labix.org/v2/mgo/bson"
import "github.com/SommerEngineering/Ocean/Shutdown"
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func initCacheTimer() {
go func() {
for {
if Shutdown.IsDown() {
return
}
lastCount := cacheListenerDatabase.Len()
selection := bson.D{{`IsActive`, true}}
entriesIterator := collectionListener.Find(selection).Iter()
entry := Scheme.Listener{}
cacheListenerDatabaseLock.Lock()
cacheListenerDatabase.Init()
for entriesIterator.Next(&entry) {
cacheListenerDatabase.PushBack(entry)
}
cacheListenerDatabaseLock.Unlock()
nextDuration := time.Duration(5) * time.Minute
if cacheListenerDatabase.Len() == 0 {
nextDuration = time.Duration(10) * time.Second
}
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameEXECUTE, `The listener cache was refreshed with the values from the database.`, fmt.Sprintf(`last count=%d`, lastCount), fmt.Sprintf(`new count=%d`, cacheListenerDatabase.Len()))
time.Sleep(nextDuration)
}
}()
}

95
ICCC/Message2Data.go Normal file
View File

@ -0,0 +1,95 @@
package ICCC
import "fmt"
import "reflect"
import "strconv"
func message2Data(channel, command string, message interface{}) (data map[string][]string) {
if message == nil {
data = make(map[string][]string)
return
}
element := reflect.ValueOf(message)
elementType := element.Type()
data = make(map[string][]string)
data[`command`] = []string{command}
data[`channel`] = []string{channel}
for i := 0; i < element.NumField(); i++ {
field := element.Field(i)
keyName := elementType.Field(i).Name
switch field.Kind().String() {
case `int64`:
key := fmt.Sprintf(`int:%s`, keyName)
data[key] = []string{strconv.FormatInt(field.Int(), 10)}
case `string`:
key := fmt.Sprintf(`str:%s`, keyName)
data[key] = []string{field.String()}
case `float64`:
key := fmt.Sprintf(`f64:%s`, keyName)
data[key] = []string{strconv.FormatFloat(field.Float(), 'f', 9, 64)}
case `bool`:
key := fmt.Sprintf(`bool:%s`, keyName)
data[key] = []string{strconv.FormatBool(field.Bool())}
case `uint8`: // byte
key := fmt.Sprintf(`ui8:%s`, keyName)
data[key] = []string{strconv.FormatUint(field.Uint(), 16)}
case `slice`:
sliceLen := field.Len()
if sliceLen > 0 {
sliceKind := field.Index(0).Kind()
key := ``
dataValues := make([]string, sliceLen, sliceLen)
switch sliceKind.String() {
case `uint8`: // bytes
key = fmt.Sprintf(`ui8[]:%s`, keyName)
values := field.Interface().([]uint8)
for index, value := range values {
dataValues[index] = strconv.FormatUint(uint64(value), 16)
}
case `int64`:
key = fmt.Sprintf(`int[]:%s`, keyName)
values := field.Interface().([]int64)
for index, value := range values {
dataValues[index] = strconv.FormatInt(value, 10)
}
case `bool`:
key = fmt.Sprintf(`bool[]:%s`, keyName)
values := field.Interface().([]bool)
for index, value := range values {
dataValues[index] = strconv.FormatBool(value)
}
case `string`:
key = fmt.Sprintf(`str[]:%s`, keyName)
values := field.Interface().([]string)
for index, value := range values {
dataValues[index] = value
}
case `float64`:
key = fmt.Sprintf(`f64[]:%s`, keyName)
values := field.Interface().([]float64)
for index, value := range values {
dataValues[index] = strconv.FormatFloat(value, 'f', 9, 64)
}
}
data[key] = dataValues
}
}
}
return
}

47
ICCC/Register2Database.go Normal file
View File

@ -0,0 +1,47 @@
package ICCC
import "labix.org/v2/mgo/bson"
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func register2Database(channel, command string) {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Register this ICCC command in to the database.`, `channel=`+channel, `command=`+command)
// Case: Exist and active :)
emptyEntry := Scheme.Listener{}
selection := bson.D{{`Channel`, channel}, {`Command`, command}, {`IPAddressPort`, correctAddressWithPort}, {`IsActive`, true}}
count1, _ := collectionListener.Find(selection).Count()
if count1 == 1 {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.SeverityHigh, LM.ImpactHigh, LM.MessageNameSTARTUP, `This ICCC command is already known and active.`, `Please shutdown the system next time!`)
return
}
// Case: Exist but not active
selection = bson.D{{`Channel`, channel}, {`Command`, command}, {`IPAddressPort`, correctAddressWithPort}, {`IsActive`, false}}
notActiveEntry := Scheme.Listener{}
collectionListener.Find(selection).One(&notActiveEntry)
if notActiveEntry != emptyEntry {
notActiveEntry.IsActive = true
collectionListener.Update(selection, notActiveEntry)
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.SeverityNone, LM.ImpactNone, LM.MessageNameSTARTUP, `This ICCC command is already known but it was not active.`, `The command is active now!`)
return
}
// Case: Not exist
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactNone, LM.MessageNameCONFIGURATION, `This ICCC command is not known.`, `Create now a new entry!`)
entry := Scheme.Listener{}
entry.Channel = channel
entry.Command = command
entry.IsActive = true
entry.IPAddressPort = correctAddressWithPort
if err := collectionListener.Insert(entry); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `It was not possible to add this ICCC command!`, err.Error(), `channel=`+channel, `command=`+command)
} else {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `This ICCC command is now known and active.`)
}
}

View File

@ -0,0 +1,26 @@
package ICCC
import "labix.org/v2/mgo/bson"
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
import "github.com/SommerEngineering/Ocean/Tools"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func registerHost2Database() {
host := Scheme.Host{}
host.Hostname = Tools.ThisHostname()
host.IPAddressPort = correctAddressWithPort
selection := bson.D{{`Hostname`, host.Hostname}, {`IPAddressPort`, host.IPAddressPort}}
count, _ := collectionHosts.Find(selection).Count()
if count == 1 {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `This host is already registered!`, `host=`+host.Hostname, `address=`+host.IPAddressPort)
} else {
if errInsert := collectionHosts.Insert(host); errInsert != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to register this host.`, errInsert.Error(), `host=`+host.Hostname, `address=`+host.IPAddressPort)
} else {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `This host is now registered.`, `host=`+host.Hostname, `address=`+host.IPAddressPort)
}
}
}

15
ICCC/Registrar.go Normal file
View File

@ -0,0 +1,15 @@
package ICCC
import "fmt"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func Registrar(channel, command string, callback func(data map[string][]string)) {
listenersLock.Lock()
defer listenersLock.Unlock()
register2Database(channel, command)
listeners[fmt.Sprintf(`%s::%s`, channel, command)] = callback
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The registrar has registered a new ICCC command.`, `channel=`+channel, `command=`+command)
}

6
ICCC/Scheme/Host.go Normal file
View File

@ -0,0 +1,6 @@
package Scheme
type Host struct {
Hostname string `bson:"Hostname"`
IPAddressPort string `bson:"IPAddressPort"`
}

8
ICCC/Scheme/Listener.go Normal file
View File

@ -0,0 +1,8 @@
package Scheme
type Listener struct {
Channel string `bson:"Channel"`
Command string `bson:"Command"`
IsActive bool `bson:"IsActive"`
IPAddressPort string `bson:"IPAddressPort"`
}

19
ICCC/Send.go Normal file
View File

@ -0,0 +1,19 @@
package ICCC
import "net/http"
import "net/url"
import "github.com/SommerEngineering/Ocean/Tools"
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func sendMessage(listener Scheme.Listener, data map[string][]string) {
valuesHTTP := url.Values(data)
valuesHTTP.Add(`InternalCommPassword`, Tools.InternalCommPassword())
if _, err := http.PostForm(`http://`+listener.IPAddressPort+`/ICCC`, valuesHTTP); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameNETWORK, `Was not able to send the ICCC message.`, err.Error())
}
return
}

30
ICCC/Shutdown.go Normal file
View File

@ -0,0 +1,30 @@
package ICCC
import "labix.org/v2/mgo/bson"
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
/*
Please do not use this type. It is an internal type of Ocean to provide a shutdown function!
*/
type ShutdownFunction struct {
}
/*
This function is called if the Ocean server is shutting down.
*/
func (a ShutdownFunction) Shutdown() {
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Shutting down now all ICCC listener for this host.`)
selection := bson.D{{`IPAddressPort`, correctAddressWithPort}}
entry := Scheme.Listener{}
iterator := collectionListener.Find(selection).Iter()
for iterator.Next(&entry) {
selectionUpdate := bson.D{{`Channel`, entry.Channel}, {`Command`, entry.Command}, {`IPAddressPort`, correctAddressWithPort}}
entry.IsActive = false
collectionListener.Update(selectionUpdate, entry)
}
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Done shutting down now all ICCC listener for this host.`)
}

27
ICCC/Variables.go Normal file
View File

@ -0,0 +1,27 @@
package ICCC
import "sync"
import "container/list"
import "labix.org/v2/mgo"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
const (
ChannelSYSTEM string = `System`
ChannelNUMGEN string = `System::NumGen`
ChannelSHUTDOWN string = `System::Shutdown`
ChannelSTARTUP string = `System::Startup`
ChannelICCC string = `System::ICCC`
)
var (
senderName LM.Sender = `ICCC`
db *mgo.Database = nil
collectionListener *mgo.Collection = nil
collectionHosts *mgo.Collection = nil
reservedSystemChannels []string = []string{ChannelSYSTEM, ChannelNUMGEN, ChannelSHUTDOWN, ChannelSTARTUP, ChannelICCC}
listeners map[string]func(data map[string][]string) = nil
listenersLock sync.RWMutex = sync.RWMutex{}
cacheListenerDatabase *list.List = nil
cacheListenerDatabaseLock sync.RWMutex = sync.RWMutex{}
correctAddressWithPort string = ``
)

26
ICCC/WriteMessage2All.go Normal file
View File

@ -0,0 +1,26 @@
package ICCC
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func WriteMessage2All(channel, command string, message interface{}) {
cacheListenerDatabaseLock.RLock()
defer cacheListenerDatabaseLock.RUnlock()
data := message2Data(channel, command, message)
counter := 0
for entry := cacheListenerDatabase.Front(); entry != nil; entry = entry.Next() {
listener := entry.Value.(Scheme.Listener)
if listener.Channel == channel && listener.Command == command {
go sendMessage(listener, data)
counter++
}
}
if counter == 0 {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not able to deliver this message, because no listener was found!`, `channel=`+channel, `command=`+command)
}
return
}

31
ICCC/WriteMessage2Any.go Normal file
View File

@ -0,0 +1,31 @@
package ICCC
import "github.com/SommerEngineering/Ocean/Tools"
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func WriteMessage2Any(channel, command string, message interface{}) {
cacheListenerDatabaseLock.RLock()
defer cacheListenerDatabaseLock.RUnlock()
data := message2Data(channel, command, message)
maxCount := cacheListenerDatabase.Len()
entries := make([]Scheme.Listener, 0, maxCount)
counter := 0
for entry := cacheListenerDatabase.Front(); entry != nil; entry = entry.Next() {
listener := entry.Value.(Scheme.Listener)
if listener.Channel == channel && listener.Command == command {
entries = entries[:len(entries)+1]
entries[counter] = listener
}
}
count := len(entries)
if count > 0 {
listener := entries[Tools.RandomInteger(count)]
go sendMessage(listener, data)
} else {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not able to deliver this message to any listener, because no listener was found!`, `channel=`+channel, `command=`+command)
}
}

19
Log/AddDevice.go Normal file
View File

@ -0,0 +1,19 @@
package Log
import "github.com/SommerEngineering/Ocean/Log/Device"
/*
Registering the logging devices. Normally, it is not necessary to call this function. To enable or disable a logging device,
please use the configuration database instead. But if you create your own logging device, let say a e-mail logger, then you
are able to use this function to activate your own logging device. It is save to use this function at any time and it is
thread-save ;-)
*/
func AddLoggingDevice(device Device.Device) {
newDevice := device
go func() {
mutexDevices.Lock()
devices.PushBack(newDevice)
mutexDevices.Unlock()
}()
}

16
Log/Array.go Normal file
View File

@ -0,0 +1,16 @@
package Log
import "container/list"
import "github.com/SommerEngineering/Ocean/Log/Meta"
func logEntryListToArray(data *list.List) (result []Meta.Entry) {
count := data.Len()
result = make([]Meta.Entry, count, count)
position := 0
for entry := data.Front(); entry != nil; entry = entry.Next() {
result[position] = entry.Value.(Meta.Entry)
position++
}
return
}

8
Log/Device/Device.go Normal file
View File

@ -0,0 +1,8 @@
package Device
import "github.com/SommerEngineering/Ocean/Log/Meta"
type Device interface {
Log(logEntries []Meta.Entry)
Flush()
}

4
Log/Device/Doc.go Normal file
View File

@ -0,0 +1,4 @@
/*
This is the logging device interface you have to fulfill to be a logging device :)
*/
package Device

View File

@ -0,0 +1,7 @@
package DeviceConsole
import "github.com/SommerEngineering/Ocean/Log"
func ActivateLoggingDevice() {
Log.AddLoggingDevice(Console{})
}

View File

@ -0,0 +1,17 @@
package DeviceConsole
import "fmt"
import "github.com/SommerEngineering/Ocean/Log/Meta"
type Console struct {
}
func (dev Console) Log(entries []Meta.Entry) {
for _, entry := range entries {
fmt.Println(entry.Format())
}
}
func (dev Console) Flush() {
// This is not necessary for a console logger
}

4
Log/DeviceConsole/Doc.go Normal file
View File

@ -0,0 +1,4 @@
/*
This is the console logging device. Any received entry is logged to the console.
*/
package DeviceConsole

View File

@ -0,0 +1,7 @@
package DeviceDatabase
import "github.com/SommerEngineering/Ocean/Log"
func ActivateLoggingDevice() {
Log.AddLoggingDevice(Database{})
}

View File

@ -0,0 +1,5 @@
/*
This is the database logging device. The logging events will be cached and after a time span or a maximum amount of
events, the logging entries are written to the database.
*/
package DeviceDatabase

View File

@ -0,0 +1,14 @@
package DeviceDatabase
func (dev Database) Flush() {
mutexCacheFull.Lock()
defer mutexCacheFull.Unlock()
amount := len(cache)
for counter := 0; counter < amount; counter++ {
write2Database(<-cache)
}
logDB.Logout()
logDBSession.Close()
}

View File

@ -0,0 +1,31 @@
package DeviceDatabase
import "strconv"
import "fmt"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func init() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Starting now the database logging.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Starting the database logging done.`)
initDatabase()
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogDBCacheSizeNumberOfEvents`)); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityHigh, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not possible to read the LogDBCacheSizeNumberOfEvents configuration.`, `The default value will be used.`, fmt.Sprintf(`Default value is %d.`, cacheSizeNumberOfEvents))
} else {
cacheSizeNumberOfEvents = value
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDBCacheSizeNumberOfEvents was loaded.`, fmt.Sprintf(`The value is %d.`, cacheSizeNumberOfEvents))
}
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogDBCacheSizeTime2FlushSeconds`)); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityHigh, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not possible to read the LogDBCacheSizeTime2FlushSeconds configuration.`, `The default value will be used.`, fmt.Sprintf(`Default value is %d.`, cacheSizeTime2FlushSeconds))
} else {
cacheSizeTime2FlushSeconds = value
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDBCacheSizeTime2FlushSeconds was loaded.`, fmt.Sprintf(`The value is %d.`, cacheSizeTime2FlushSeconds))
}
cache = make(chan LogDBEntry, cacheSizeNumberOfEvents)
initTimeout()
}

View File

@ -0,0 +1,165 @@
package DeviceDatabase
import "labix.org/v2/mgo"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func initDatabase() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Checking and init the logging database collection.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Checking and init the logging database collection done.`)
databaseHost := ConfigurationDB.Read(`LogDBHost`)
databaseDB := ConfigurationDB.Read(`LogDBDatabase`)
databaseUsername := ConfigurationDB.Read(`LogDBUsername`)
databasePassword := ConfigurationDB.Read(`LogDBPassword`)
// Connect to MongoDB:
if newSession, errDial := mgo.Dial(databaseHost); errDial != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `It was not possible to connect to the MongoDB host `+databaseHost, errDial.Error())
return
} else {
logDBSession = newSession
}
// Use the correct database:
logDB = logDBSession.DB(databaseDB)
// Login:
if errLogin := logDB.Login(databaseUsername, databasePassword); errLogin != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `It was not possible to login the user `+databaseUsername, errLogin.Error())
return
}
// Get the collection:
logDBCollection = logDB.C(`Logbook`)
//
// Take care about all the indexes:
//
indexTimeUTC := mgo.Index{}
indexTimeUTC.Key = []string{`TimeUTC`}
logDBCollection.EnsureIndex(indexTimeUTC)
indexProject := mgo.Index{}
indexProject.Key = []string{`Project`}
logDBCollection.EnsureIndex(indexProject)
indexSender := mgo.Index{}
indexSender.Key = []string{`Sender`}
logDBCollection.EnsureIndex(indexSender)
indexCategory := mgo.Index{}
indexCategory.Key = []string{`Category`}
logDBCollection.EnsureIndex(indexCategory)
indexLevel := mgo.Index{}
indexLevel.Key = []string{`Level`}
logDBCollection.EnsureIndex(indexLevel)
indexSeverity := mgo.Index{}
indexSeverity.Key = []string{`Severity`}
logDBCollection.EnsureIndex(indexSeverity)
indexImpact := mgo.Index{}
indexImpact.Key = []string{`Impact`}
logDBCollection.EnsureIndex(indexImpact)
indexMessageName := mgo.Index{}
indexMessageName.Key = []string{`MessageName`}
logDBCollection.EnsureIndex(indexMessageName)
indexMessageDescription := mgo.Index{}
indexMessageDescription.Key = []string{`MessageDescription`}
logDBCollection.EnsureIndex(indexMessageDescription)
indexProjectTimeUTC := mgo.Index{}
indexProjectTimeUTC.Key = []string{`Project`, `TimeUTC`}
logDBCollection.EnsureIndex(indexProjectTimeUTC)
indexProjectSender := mgo.Index{}
indexProjectSender.Key = []string{`Project`, `Sender`}
logDBCollection.EnsureIndex(indexProjectSender)
indexProjectCategory := mgo.Index{}
indexProjectCategory.Key = []string{`Project`, `Category`}
logDBCollection.EnsureIndex(indexProjectCategory)
indexProjectLevel := mgo.Index{}
indexProjectLevel.Key = []string{`Project`, `Level`}
logDBCollection.EnsureIndex(indexProjectLevel)
indexProjectSeverity := mgo.Index{}
indexProjectSeverity.Key = []string{`Project`, `Severity`}
logDBCollection.EnsureIndex(indexProjectSeverity)
indexProjectImpact := mgo.Index{}
indexProjectImpact.Key = []string{`Project`, `Impact`}
logDBCollection.EnsureIndex(indexProjectImpact)
indexProjectMessageName := mgo.Index{}
indexProjectMessageName.Key = []string{`Project`, `MessageName`}
logDBCollection.EnsureIndex(indexProjectMessageName)
indexProjectMessageDescription := mgo.Index{}
indexProjectMessageDescription.Key = []string{`Project`, `MessageDescription`}
logDBCollection.EnsureIndex(indexProjectMessageDescription)
indexProjectTimeUTCSender := mgo.Index{}
indexProjectTimeUTCSender.Key = []string{`Project`, `TimeUTC`, `Sender`}
logDBCollection.EnsureIndex(indexProjectTimeUTCSender)
indexProjectTimeUTCCategory := mgo.Index{}
indexProjectTimeUTCCategory.Key = []string{`Project`, `TimeUTC`, `Category`}
logDBCollection.EnsureIndex(indexProjectTimeUTCCategory)
indexProjectTimeUTCLevel := mgo.Index{}
indexProjectTimeUTCLevel.Key = []string{`Project`, `TimeUTC`, `Level`}
logDBCollection.EnsureIndex(indexProjectTimeUTCLevel)
indexProjectTimeUTCSeverity := mgo.Index{}
indexProjectTimeUTCSeverity.Key = []string{`Project`, `TimeUTC`, `Severity`}
logDBCollection.EnsureIndex(indexProjectTimeUTCSeverity)
indexProjectTimeUTCImpact := mgo.Index{}
indexProjectTimeUTCImpact.Key = []string{`Project`, `TimeUTC`, `Impact`}
logDBCollection.EnsureIndex(indexProjectTimeUTCImpact)
indexProjectTimeUTCMessageName := mgo.Index{}
indexProjectTimeUTCMessageName.Key = []string{`Project`, `TimeUTC`, `MessageName`}
logDBCollection.EnsureIndex(indexProjectTimeUTCMessageName)
indexProjectTimeUTCMessageDescription := mgo.Index{}
indexProjectTimeUTCMessageDescription.Key = []string{`Project`, `TimeUTC`, `MessageDescription`}
logDBCollection.EnsureIndex(indexProjectTimeUTCMessageDescription)
indexTimeUTCSender := mgo.Index{}
indexTimeUTCSender.Key = []string{`TimeUTC`, `Sender`}
logDBCollection.EnsureIndex(indexTimeUTCSender)
indexTimeUTCCategory := mgo.Index{}
indexTimeUTCCategory.Key = []string{`TimeUTC`, `Category`}
logDBCollection.EnsureIndex(indexTimeUTCCategory)
indexTimeUTCLevel := mgo.Index{}
indexTimeUTCLevel.Key = []string{`TimeUTC`, `Level`}
logDBCollection.EnsureIndex(indexTimeUTCLevel)
indexTimeUTCSeverity := mgo.Index{}
indexTimeUTCSeverity.Key = []string{`TimeUTC`, `Severity`}
logDBCollection.EnsureIndex(indexTimeUTCSeverity)
indexTimeUTCImpact := mgo.Index{}
indexTimeUTCImpact.Key = []string{`TimeUTC`, `Impact`}
logDBCollection.EnsureIndex(indexTimeUTCImpact)
indexTimeUTCMessageName := mgo.Index{}
indexTimeUTCMessageName.Key = []string{`TimeUTC`, `MessageName`}
logDBCollection.EnsureIndex(indexTimeUTCMessageName)
indexTimeUTCMessageDescription := mgo.Index{}
indexProjectTimeUTCMessageDescription.Key = []string{`TimeUTC`, `MessageDescription`}
logDBCollection.EnsureIndex(indexTimeUTCMessageDescription)
}

View File

@ -0,0 +1,39 @@
package DeviceDatabase
import "time"
import "github.com/SommerEngineering/Ocean/Shutdown"
// Case: The cache is full
func cacheFull() {
mutexCacheFull.Lock()
defer mutexCacheFull.Unlock()
if len(cache) < cacheSizeNumberOfEvents {
return
}
for counter := 0; counter < cacheSizeNumberOfEvents; counter++ {
write2Database(<-cache)
}
}
// Case: Time out
func initTimeout() {
go func() {
for {
if Shutdown.IsDown() {
return
}
time.Sleep(time.Duration(cacheSizeTime2FlushSeconds) * time.Second)
mutexCacheFull.Lock()
amount := len(cache)
for counter := 0; counter < amount; counter++ {
write2Database(<-cache)
}
mutexCacheFull.Unlock()
}
}()
}

View File

@ -0,0 +1,15 @@
package DeviceDatabase
import "github.com/SommerEngineering/Ocean/Log/Meta"
type Database struct {
}
func (dev Database) Log(entries []Meta.Entry) {
//
// Can not log here to prevent endless loop (consumer is also producer)
//
write2Cache(entries)
}

View File

@ -0,0 +1,16 @@
package DeviceDatabase
import "time"
type LogDBEntry struct {
TimeUTC time.Time `bson:"TimeUTC"`
Project string `bson:"Project"`
Sender string `bson:"Sender"`
Category string `bson:"Category"`
Level string `bson:"Level"`
Severity string `bson:"Severity"`
Impact string `bson:"Impact"`
MessageName string `bson:"MessageName"`
MessageDescription string `bson:"MessageDescription"`
Parameters []string `bson:"Parameters"`
}

View File

@ -0,0 +1,16 @@
package DeviceDatabase
import "sync"
import "labix.org/v2/mgo"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
var (
senderName LM.Sender = `System::Logger::Database`
mutexCacheFull sync.Mutex = sync.Mutex{}
cache chan LogDBEntry = nil
cacheSizeNumberOfEvents int = 50
cacheSizeTime2FlushSeconds int = 6
logDB *mgo.Database = nil
logDBSession *mgo.Session = nil
logDBCollection *mgo.Collection = nil
)

View File

@ -0,0 +1,24 @@
package DeviceDatabase
import "github.com/SommerEngineering/Ocean/Log/Meta"
func write2Cache(entries []Meta.Entry) {
for _, entry := range entries {
if len(cache) == cacheSizeNumberOfEvents {
go cacheFull()
}
logDBentry := LogDBEntry{}
logDBentry.Category = Meta.FormatCategory(entry.Category)
logDBentry.Impact = Meta.FormatImpact(entry.Impact)
logDBentry.Level = Meta.FormatLevel(entry.Level)
logDBentry.MessageDescription = entry.MessageDescription
logDBentry.MessageName = string(entry.MessageName)
logDBentry.Parameters = entry.Parameters
logDBentry.Project = entry.Project
logDBentry.Sender = string(entry.Sender)
logDBentry.Severity = Meta.FormatSeverity(entry.Severity)
logDBentry.TimeUTC = entry.Time
cache <- logDBentry
}
}

View File

@ -0,0 +1,7 @@
package DeviceDatabase
func write2Database(entry LogDBEntry) {
if err := logDBCollection.Insert(entry); err != nil {
// Can not log here to prevent endless loop (consumer is also producer)
}
}

43
Log/DeviceDelay.go Normal file
View File

@ -0,0 +1,43 @@
package Log
import "github.com/SommerEngineering/Ocean/Log/Meta"
import "github.com/SommerEngineering/Ocean/Log/Device"
func deviceDelay(newEntry Meta.Entry) {
defer checkDeviceDelaySize()
// Insert the new entry at the correct position (time)!
for logEvent := deviceDelayBuffer.Front(); logEvent != nil; logEvent = logEvent.Next() {
currentEvent := logEvent.Value.(Meta.Entry)
if newEntry.Time.Before(currentEvent.Time) {
mutexDeviceDelays.Lock()
deviceDelayBuffer.InsertBefore(newEntry, logEvent)
mutexDeviceDelays.Unlock()
return
}
}
// Default: Insert at the back!
mutexDeviceDelays.Lock()
deviceDelayBuffer.PushBack(newEntry)
mutexDeviceDelays.Unlock()
}
func checkDeviceDelaySize() {
mutexDeviceDelays.Lock()
if deviceDelayBuffer.Len() >= logDeviceDelayNumberEvents {
dataArray := logEntryListToArray(deviceDelayBuffer)
deviceDelayBuffer.Init()
mutexDevices.RLock()
for entry := devices.Front(); entry != nil; entry = entry.Next() {
dev := entry.Value.(Device.Device)
go dev.Log(dataArray)
}
mutexDevices.RUnlock()
}
mutexDeviceDelays.Unlock()
}

33
Log/Flush.go Normal file
View File

@ -0,0 +1,33 @@
package Log
import "time"
import "github.com/SommerEngineering/Ocean/Log/Device"
/*
Please do not call this function your self! This function allows Ocean to flush the logging at the shutting down case.
*/
func Flush() {
mutexChannel.Lock()
channelReady = false
close(entriesBuffer)
mutexChannel.Unlock()
// This is a bad design, but the scheduler need some time to write the last messages.
time.Sleep(15 * time.Second)
mutexDeviceDelays.Lock()
dataArray := logEntryListToArray(deviceDelayBuffer)
deviceDelayBuffer.Init()
mutexDeviceDelays.Unlock()
mutexDevices.RLock()
for entry := devices.Front(); entry != nil; entry = entry.Next() {
dev := entry.Value.(Device.Device)
dev.Log(dataArray) // Want to wait to complete, therefore no new thread here
go dev.Flush()
}
mutexDevices.RUnlock()
// This is a bad design, but the devices need (may) some time to write the last messages:
time.Sleep(15 * time.Second)
}

134
Log/Handlers.go Normal file
View File

@ -0,0 +1,134 @@
package Log
import "time"
import "fmt"
import "strings"
import "github.com/SommerEngineering/Ocean/Log/Meta"
func writeToChannel(logEntry Meta.Entry) {
select {
case entriesBuffer <- logEntry:
case <-time.After(time.Duration(int64(logBufferTimeoutSeconds)) * time.Second):
// Warn: Can not log here to prevent endless loop and memory leak!
fmt.Println(`Warning: Was not able to write to the logging buffer! Message=` + logEntry.Format())
}
}
/*
If, for any reason, you want to deliver a whole log entry to the logging system, then use this function to do so :) Normally,
just use the LogFull() and LogShort() functions instead ;) There is one reason to use this: If you have to deliver old log
events you have may imported or you have received a batch of log events of an remote system, etc. Because the LogShort() and
LogFull() methods are setting the time to the current UTC time. Therefore, TakeEntry() is the only way to provide the time!
*/
func TakeEntry(logEntry Meta.Entry) {
logEntry = clearEntry(logEntry)
mutexChannel.RLock()
defer mutexChannel.RUnlock()
if !channelReady {
mutexPreChannelBuffer.Lock()
defer mutexPreChannelBuffer.Unlock()
preChannelBuffer.PushBack(logEntry)
return
}
if !preChannelBufferUsed {
preChannelBufferUsed = true
mutexPreChannelBuffer.Lock()
for entry := preChannelBuffer.Front(); entry != nil; entry = entry.Next() {
writeToChannel(entry.Value.(Meta.Entry))
}
preChannelBuffer.Init()
mutexPreChannelBuffer.Unlock()
}
writeToChannel(logEntry)
}
/*
Create and deliver a full log message with all fields and with the current UTC time as logging time.
Sender The sender of this message (name of your component, like e.g. "APP::LOGIC::PAYPAL" etc.)
Category Use "CategoryBUSINESS" for business events (new payments, etc.), use "CategoryUSER" for log events
regarding users (e.g. new user registered, etc.) or "CategoryAPP" (for anything else).
Level Choose a logging level.
Severity Choose a degree of severity.
Impact Choose a degree of impact.
MessageName Choose a message name. This is like a common category for this event!
Description The logging message you want to deliver.
Parameters Provide as many additional parameters (type string) as you need.
*/
func LogFull(sender Meta.Sender, category Meta.Category, level Meta.Level, severity Meta.Severity, impact Meta.Impact, messageName Meta.MessageName, messageDescription string, parameters ...string) {
entry := Meta.Entry{}
entry.Project = projectName
entry.Time = time.Now().UTC()
entry.Category = category
entry.Level = level
entry.MessageDescription = messageDescription
entry.MessageName = messageName
entry.Parameters = parameters
entry.Severity = severity
entry.Impact = impact
entry.Sender = sender
TakeEntry(entry)
}
/*
Create and deliver a short log message with the current UTC time as logging time. The fields severity and impact
are both set to "none" value. Therefore, this short logging function does not fit for logging problems, errors etc.
Sender The sender of this message (name of your component, like e.g. "APP::LOGIC::PAYPAL" etc.)
Category Use "CategoryBUSINESS" for business events (new payments, etc.), use "CategoryUSER" for log events
regarding users (e.g. new user registered, etc.) or "CategoryAPP" (for anything else).
Level Choose a logging level.
MessageName Choose a message name. This is like a common category for this event!
Description The logging message you want to deliver.
Parameters Provide as many additional parameters (type string) as you need.
*/
func LogShort(sender Meta.Sender, category Meta.Category, level Meta.Level, messageName Meta.MessageName, messageDescription string, parameters ...string) {
entry := Meta.Entry{}
entry.Project = projectName
entry.Time = time.Now().UTC()
entry.Category = category
entry.Level = level
entry.MessageDescription = messageDescription
entry.MessageName = messageName
entry.Parameters = parameters
entry.Severity = Meta.SeverityNone
entry.Impact = Meta.ImpactNone
entry.Sender = sender
TakeEntry(entry)
}
func clearEntry(entry Meta.Entry) (result Meta.Entry) {
entry.MessageDescription = removeWhitespaces(entry.MessageDescription)
entry.Parameters = clearParameters(entry.Parameters)
result = entry
return
}
func clearParameters(oldParameters []string) (result []string) {
for n := 0; n < len(oldParameters); n++ {
oldParameters[n] = removeWhitespaces(oldParameters[n])
}
result = oldParameters
return
}
func removeWhitespaces(text string) (result string) {
text = strings.Replace(text, "\n", ` `, -1)
text = strings.Replace(text, "\t", ` `, -1)
text = strings.Replace(text, "\r", ``, -1)
result = text
return
}

52
Log/Init.go Normal file
View File

@ -0,0 +1,52 @@
package Log
import "github.com/SommerEngineering/Ocean/Log/Meta"
import "strconv"
import "container/list"
import "sync"
import "path/filepath"
import "os"
import "io/ioutil"
import "strings"
func readProjectName() {
if currentDir, dirError := os.Getwd(); dirError != nil {
panic(`Can not read the current working directory and therefore can not read the project name!`)
} else {
filename := filepath.Join(currentDir, `project.name`)
if _, errFile := os.Stat(filename); errFile != nil {
if os.IsNotExist(errFile) {
panic(`Can not read the project name file 'project.name': File not found!`)
} else {
panic(`Can not read the project name file 'project.name': ` + errFile.Error())
}
}
if projectNameBytes, errRead := ioutil.ReadFile(filename); errRead != nil {
panic(`Can not read the project name file 'project.name': ` + errRead.Error())
} else {
projectName = string(projectNameBytes)
projectName = strings.TrimSpace(projectName)
}
}
}
func init() {
readProjectName()
mutexDeviceDelays = sync.Mutex{}
mutexPreChannelBuffer = sync.Mutex{}
mutexChannel = sync.RWMutex{}
preChannelBuffer = list.New()
deviceDelayBuffer = list.New()
devices = list.New()
initTimer()
initCode()
}
func initCode() {
entriesBuffer = make(chan Meta.Entry, logBufferSize)
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, `Starting`, `The logger is now starting.`, `logBufferSize=`+strconv.Itoa(int(logBufferSize)), `logBufferTimeoutSeconds=`+strconv.Itoa(int(logBufferTimeoutSeconds)))
go scheduler(entriesBuffer)
}

9
Log/LoggingIsReady.go Normal file
View File

@ -0,0 +1,9 @@
package Log
/*
This function is used just internal by Ocean. Please do not call this function by your self!
*/
func LoggingIsReady() {
channelReady = true
preChannelBufferUsed = false
}

27
Log/Meta/Category.go Normal file
View File

@ -0,0 +1,27 @@
package Meta
type Category byte
const (
CategoryBUSINESS = Category(iota)
CategorySYSTEM = Category(iota)
CategoryAPP = Category(iota)
CategoryUSER = Category(iota)
)
func FormatCategory(cat Category) (result string) {
switch cat {
case CategoryBUSINESS:
result = `C:BUSINESS`
case CategoryAPP:
result = `C:APP`
case CategorySYSTEM:
result = `C:SYSTEM`
case CategoryUSER:
result = `C:USER`
default:
result = `C:N/A`
}
return
}

16
Log/Meta/Entry.go Normal file
View File

@ -0,0 +1,16 @@
package Meta
import "time"
type Entry struct {
Project string
Time time.Time
Sender Sender
Category Category
Level Level
Severity Severity
Impact Impact
MessageName MessageName
MessageDescription string
Parameters []string
}

96
Log/Meta/Format.go Normal file
View File

@ -0,0 +1,96 @@
package Meta
import "fmt"
import "time"
import "strconv"
import "strings"
func (entry Entry) Format() (result string) {
lenSender := len(entry.Sender)
if lenSender > 40 {
lenSender = 40
}
lenMessageName := len(entry.MessageName)
if lenMessageName > 26 {
lenMessageName = 26
}
lenProject := len(entry.Project)
if lenProject > 10 {
lenProject = 10
}
messageDescription := entry.MessageDescription
messageDescription = strings.Replace(messageDescription, `\n`, ` `, -1)
messageDescription = strings.Replace(messageDescription, `\t`, ` `, -1)
messageDescription = strings.Replace(messageDescription, `\r`, ` `, -1)
result = fmt.Sprintf(` [■] P:%10s [■] %s [■] %10s [■] %11s [■] %10s [■] %10s [■] sender: %-40s [■] name: %-26s [■] %s [■]`, entry.Project[:lenProject], formatTime(entry.Time), FormatCategory(entry.Category), FormatLevel(entry.Level), FormatSeverity(entry.Severity), FormatImpact(entry.Impact), entry.Sender[:lenSender], entry.MessageName[:lenMessageName], messageDescription)
for _, param := range entry.Parameters {
paramText := param
paramText = strings.Replace(paramText, `\n`, ` `, -1)
paramText = strings.Replace(paramText, `\t`, ` `, -1)
paramText = strings.Replace(paramText, `\r`, ` `, -1)
result = fmt.Sprintf(`%s %s [■]`, result, paramText)
}
return
}
func formatTime(t1 time.Time) (result string) {
var year int = t1.Year()
var month int = int(t1.Month())
var day int = int(t1.Day())
var minutes int = int(t1.Minute())
var hours int = int(t1.Hour())
var seconds int = int(t1.Second())
var milliseconds int = int(float64(t1.Nanosecond()) / 1000000.0)
var monthText, dayText, minutesText, hoursText, secondsText, millisecondsText string
if month >= 1 && month <= 9 {
monthText = fmt.Sprintf(`0%d`, month)
} else {
monthText = strconv.Itoa(month)
}
if day >= 1 && day <= 9 {
dayText = fmt.Sprintf(`0%d`, day)
} else {
dayText = strconv.Itoa(day)
}
if minutes >= 0 && minutes <= 9 {
minutesText = fmt.Sprintf(`0%d`, minutes)
} else {
minutesText = strconv.Itoa(minutes)
}
if hours >= 0 && hours <= 9 {
hoursText = fmt.Sprintf(`0%d`, hours)
} else {
hoursText = strconv.Itoa(hours)
}
if seconds >= 0 && seconds <= 9 {
secondsText = fmt.Sprintf(`0%d`, seconds)
} else {
secondsText = strconv.Itoa(seconds)
}
if milliseconds >= 0 && milliseconds <= 9 {
millisecondsText = fmt.Sprintf(`00%d`, milliseconds)
} else if milliseconds >= 10 && milliseconds <= 99 {
millisecondsText = fmt.Sprintf(`0%d`, milliseconds)
} else {
millisecondsText = strconv.Itoa(milliseconds)
}
result = fmt.Sprintf(`%d%s%s %s%s%s.%s`, year, monthText, dayText, hoursText, minutesText, secondsText, millisecondsText)
return
}

33
Log/Meta/Impact.go Normal file
View File

@ -0,0 +1,33 @@
package Meta
type Impact byte
const (
ImpactNone = Impact(iota)
ImpactLow = Impact(iota)
ImpactMiddle = Impact(iota)
ImpactHigh = Impact(iota)
ImpactCritical = Impact(iota)
ImpactUnknown = Impact(iota)
)
func FormatImpact(pri Impact) (result string) {
switch pri {
case ImpactCritical:
result = `I:CRITICAL`
case ImpactHigh:
result = `I:HIGH`
case ImpactLow:
result = `I:LOW`
case ImpactMiddle:
result = `I:MIDDLE`
case ImpactNone:
result = `I:NONE`
case ImpactUnknown:
result = `I:UNKNOWN`
default:
result = `I:N/A`
}
return
}

33
Log/Meta/Level.go Normal file
View File

@ -0,0 +1,33 @@
package Meta
type Level byte
const (
LevelWARN = Level(iota)
LevelDEBUG = Level(iota)
LevelERROR = Level(iota)
LevelINFO = Level(iota)
LevelTALKATIVE = Level(iota)
LevelSECURITY = Level(iota)
)
func FormatLevel(lvl Level) (result string) {
switch lvl {
case LevelDEBUG:
result = `L:DEBUG`
case LevelERROR:
result = `L:ERROR`
case LevelINFO:
result = `L:INFO`
case LevelSECURITY:
result = `L:SECURITY`
case LevelTALKATIVE:
result = `L:TALKATIVE`
case LevelWARN:
result = `L:WARN`
default:
result = `L:N/A`
}
return
}

34
Log/Meta/MessageNames.go Normal file
View File

@ -0,0 +1,34 @@
package Meta
type MessageName string
const (
MessageNameSTARTUP = `Startup`
MessageNameINIT = `Init`
MessageNameSHUTDOWN = `Shutdown`
MessageNameEXECUTE = `Execute`
MessageNameDATABASE = `Database`
MessageNameNETWORK = `Network`
MessageNameLOGIN = `Login`
MessageNameLOGOUT = `Logout`
MessageNameSESSION = `Session`
MessageNameTIMEOUT = `Timeout`
MessageNameFILESYSTEM = `Filesystem`
MessageNameCOMMUNICATION = `Communication`
MessageNameWRITE = `Write`
MessageNameREAD = `Read`
MessageNameALGORITHM = `Algorithm`
MessageNameCONFIGURATION = `Configuration`
MessageNameTIMER = `Timer`
MessageNameINPUT = `Input`
MessageNameOUTPUT = `Output`
MessageNameBROWSER = `Browser`
MessageNameSECURITY = `Security`
MessageNameNOTFOUND = `NotFound`
MessageNameANALYSIS = `Analysis`
MessageNameSTATE = `State`
MessageNameGENERATOR = `Generator`
MessageNamePRODUCER = `Producer`
MessageNameCONSUMER = `Consumer`
MessageNamePASSWORD = `Password`
)

3
Log/Meta/Sender.go Normal file
View File

@ -0,0 +1,3 @@
package Meta
type Sender string

33
Log/Meta/Severity.go Normal file
View File

@ -0,0 +1,33 @@
package Meta
type Severity byte
const (
SeverityNone = Severity(iota)
SeverityLow = Severity(iota)
SeverityMiddle = Severity(iota)
SeverityHigh = Severity(iota)
SeverityCritical = Severity(iota)
SeverityUnknown = Severity(iota)
)
func FormatSeverity(pri Severity) (result string) {
switch pri {
case SeverityCritical:
result = `S:CRITICAL`
case SeverityHigh:
result = `S:HIGH`
case SeverityLow:
result = `S:LOW`
case SeverityMiddle:
result = `S:MIDDLE`
case SeverityNone:
result = `S:NONE`
case SeverityUnknown:
result = `S:UNKNOWN`
default:
result = `S:N/A`
}
return
}

38
Log/Scheduler.go Normal file
View File

@ -0,0 +1,38 @@
package Log
import "github.com/SommerEngineering/Ocean/Log/Meta"
import "time"
// Note: The scheduler is the consumer for the logging channel!
func scheduler(logBuffer chan Meta.Entry) {
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameSTARTUP, `The scheduler runs now.`)
var stopNextTime = false
for {
if stopNextTime {
break
}
nextEntry, ok := <-logBuffer
if !ok {
// The channel was closed!
stopNextTime = true
nextEntry = Meta.Entry{}
nextEntry.Project = projectName
nextEntry.Time = time.Now().UTC()
nextEntry.Sender = senderName
nextEntry.Category = Meta.CategorySYSTEM
nextEntry.Level = Meta.LevelWARN
nextEntry.Severity = Meta.SeverityCritical
nextEntry.Impact = Meta.ImpactNone
nextEntry.MessageName = Meta.MessageNameCOMMUNICATION
nextEntry.MessageDescription = `The logging channel was closed!`
}
deviceDelay(nextEntry)
}
LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityCritical, Meta.ImpactNone, Meta.MessageNameSHUTDOWN, `The scheduler is down now.`)
}

61
Log/SetConfiguration.go Normal file
View File

@ -0,0 +1,61 @@
package Log
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func SetBufferSize(bufferSize int) {
logBufferSize = bufferSize
}
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func SetTimeoutSeconds(timeoutSeconds int) {
logBufferTimeoutSeconds = timeoutSeconds
}
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func SetDeviceDelayNumberEvents(numberEvents int) {
logDeviceDelayNumberEvents = numberEvents
}
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func SetDeviceDelayTimeoutSeconds(seconds int) {
logDeviceDelayTimeoutSeconds = seconds
}
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func SetProjectName(project string) {
projectName = project
}
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func ApplyConfigurationChanges() {
mutexChannel.Lock()
channelReady = false
close(entriesBuffer)
mutexChannel.Unlock()
initCode()
}

37
Log/Timer.go Normal file
View File

@ -0,0 +1,37 @@
package Log
import "time"
import "fmt"
import "github.com/SommerEngineering/Ocean/Log/Device"
import "github.com/SommerEngineering/Ocean/Log/Meta"
func initTimer() {
if timerIsRunning == true {
LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityHigh, Meta.ImpactNone, Meta.MessageNameSTARTUP, `The logging timer is already running.`)
return
}
timerIsRunning = true
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameSTARTUP, `Create the logging timer now.`, fmt.Sprintf(`Timeout=%d seconds`, logDeviceDelayTimeoutSeconds))
go func() {
for {
time.Sleep(time.Duration(logDeviceDelayTimeoutSeconds) * time.Second)
mutexDeviceDelays.Lock()
dataArray := logEntryListToArray(deviceDelayBuffer)
deviceDelayBuffer.Init()
mutexDeviceDelays.Unlock()
mutexDevices.RLock()
for entry := devices.Front(); entry != nil; entry = entry.Next() {
dev := entry.Value.(Device.Device)
go dev.Log(dataArray)
}
mutexDevices.RUnlock()
}
}()
}

25
Log/Variables.go Normal file
View File

@ -0,0 +1,25 @@
package Log
import "github.com/SommerEngineering/Ocean/Log/Meta"
import "container/list"
import "sync"
var (
entriesBuffer chan Meta.Entry = nil
logBufferSize int = 500
logBufferTimeoutSeconds int = 4
logDeviceDelayNumberEvents int = 600
logDeviceDelayTimeoutSeconds int = 5
channelReady bool = false
preChannelBufferUsed bool = false
preChannelBuffer *list.List = nil
deviceDelayBuffer *list.List = nil
devices *list.List = nil
mutexDeviceDelays sync.Mutex = sync.Mutex{}
mutexPreChannelBuffer sync.Mutex = sync.Mutex{}
mutexChannel sync.RWMutex = sync.RWMutex{}
mutexDevices sync.RWMutex = sync.RWMutex{}
timerIsRunning bool = false
projectName string = `not set`
senderName Meta.Sender = `System::Log`
)

38
MimeTypes/Init.go Normal file
View File

@ -0,0 +1,38 @@
package MimeTypes
var allTypes [32]MimeType
func init() {
allTypes[0] = TypeWebHTML
allTypes[1] = TypeWebCSS
allTypes[2] = TypeWebJavaScript
allTypes[3] = TypeXML
allTypes[4] = TypeArchiveZIP
allTypes[5] = TypeArchiveGZ
allTypes[6] = TypeWebOCTET
allTypes[7] = TypeDocumentPDF
allTypes[8] = TypeDocumentLaTeX
allTypes[9] = TypeShockwave
allTypes[10] = TypeArchiveTAR
allTypes[11] = TypeAudioWAV
allTypes[12] = TypeAudioMP3
allTypes[13] = TypeAudioAAC
allTypes[14] = TypeAudioOGG
allTypes[15] = TypeAudioWMA
allTypes[16] = TypeImageGIF
allTypes[17] = TypeImageCommon
allTypes[18] = TypeUnknown
allTypes[19] = TypeImageJPEG
allTypes[20] = TypeImagePNG
allTypes[21] = TypePlainText
allTypes[22] = TypeVideoMPEG
allTypes[23] = TypeVideoMOV
allTypes[24] = TypeVideoAVI
allTypes[25] = TypeVideoMP4
allTypes[26] = TypeFontEOT
allTypes[27] = TypeFontOTF
allTypes[28] = TypeImageSVG
allTypes[29] = TypeFontTTF
allTypes[30] = TypeFontWOFF
allTypes[31] = TypeWebJSON
}

34
MimeTypes/KnownTypes.go Normal file
View File

@ -0,0 +1,34 @@
package MimeTypes
var TypeWebHTML = MimeType{MimeType: "text/html", FileExtension: []string{".html", ".htm"}}
var TypeWebCSS = MimeType{MimeType: "text/css", FileExtension: []string{".css"}}
var TypeWebJavaScript = MimeType{MimeType: "text/javascript", FileExtension: []string{".js"}}
var TypeXML = MimeType{MimeType: "text/xml", FileExtension: []string{".xml"}}
var TypeArchiveZIP = MimeType{MimeType: "application/zip", FileExtension: []string{".zip"}}
var TypeArchiveGZ = MimeType{MimeType: "application/gzip", FileExtension: []string{".gz"}}
var TypeWebOCTET = MimeType{MimeType: "application/octet-stream", FileExtension: []string{".bin", ".exe", ".dll", ".class"}}
var TypeDocumentPDF = MimeType{MimeType: "application/pdf", FileExtension: []string{".pdf"}}
var TypeDocumentLaTeX = MimeType{MimeType: "application/x-latex", FileExtension: []string{".tex", ".latex"}}
var TypeShockwave = MimeType{MimeType: "application/x-shockwave-flash", FileExtension: []string{".swf"}}
var TypeArchiveTAR = MimeType{MimeType: "application/x-tar", FileExtension: []string{".tar"}}
var TypeAudioWAV = MimeType{MimeType: "application/x-wav", FileExtension: []string{".wav"}}
var TypeAudioMP3 = MimeType{MimeType: "audio/mpeg", FileExtension: []string{".mp3"}}
var TypeAudioAAC = MimeType{MimeType: "audio/aac", FileExtension: []string{".aac", ".m4a"}}
var TypeAudioOGG = MimeType{MimeType: "audio/ogg", FileExtension: []string{"vogg", ".oga"}}
var TypeAudioWMA = MimeType{MimeType: "audio/x-ms-wma", FileExtension: []string{".wma"}}
var TypeImageGIF = MimeType{MimeType: "image/gif", FileExtension: []string{".gif"}}
var TypeImageCommon = MimeType{MimeType: "image", FileExtension: []string{}}
var TypeUnknown = MimeType{MimeType: "application/octet-stream", FileExtension: []string{}}
var TypeImageJPEG = MimeType{MimeType: "image/jpeg", FileExtension: []string{".jpg", ".jpeg"}}
var TypeImagePNG = MimeType{MimeType: "image/png", FileExtension: []string{".png"}}
var TypePlainText = MimeType{MimeType: "text/plain", FileExtension: []string{".txt"}}
var TypeVideoMPEG = MimeType{MimeType: "video/mpeg", FileExtension: []string{".mpeg", ".mpg"}}
var TypeVideoMOV = MimeType{MimeType: "video/quicktime", FileExtension: []string{".mov", ".qt"}}
var TypeVideoAVI = MimeType{MimeType: "video/x-msvideo", FileExtension: []string{".avi"}}
var TypeVideoMP4 = MimeType{MimeType: "video/mp4", FileExtension: []string{".mp4"}}
var TypeFontEOT = MimeType{MimeType: "application/vnd.ms-fontobject", FileExtension: []string{".eot"}}
var TypeFontOTF = MimeType{MimeType: "application/font-sfnt", FileExtension: []string{".otf"}}
var TypeImageSVG = MimeType{MimeType: "image/svg+xml", FileExtension: []string{".svg"}}
var TypeFontTTF = MimeType{MimeType: "application/font-sfnt", FileExtension: []string{".ttf"}}
var TypeFontWOFF = MimeType{MimeType: "application/font-woff", FileExtension: []string{".woff"}}
var TypeWebJSON = MimeType{MimeType: "application/json", FileExtension: []string{".json"}}

22
MimeTypes/MimeTypes.go Normal file
View File

@ -0,0 +1,22 @@
package MimeTypes
import "strings"
type MimeType struct {
MimeType string
FileExtension []string
}
func DetectType(filename string) (mime MimeType, err error) {
for _, typeElement := range allTypes {
for _, extension := range typeElement.FileExtension {
if strings.HasSuffix(filename, extension) {
mime = typeElement
return
}
}
}
mime = TypeUnknown
return
}

7
MimeTypes/Write2HTTP.go Normal file
View File

@ -0,0 +1,7 @@
package MimeTypes
import "net/http"
func Write2HTTP(response http.ResponseWriter, mime MimeType) {
response.Header().Add(`Content-Type`, mime.MimeType)
}

6
NumGen/BadNumber.go Normal file
View File

@ -0,0 +1,6 @@
package NumGen
func BadNumber() (result int64) {
result = badNumber64
return
}

30
NumGen/GetNumber.go Normal file
View File

@ -0,0 +1,30 @@
package NumGen
import "net/http"
import "net/url"
import "strconv"
import "github.com/SommerEngineering/Ocean/Shutdown"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func GetNextInt64(name string) (result int64) {
result = badNumber64
if Shutdown.IsDown() {
return
}
if responseData, errRequest := http.PostForm(getHandler, url.Values{"name": {name}, "password": {correctPassword}}); errRequest != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameGENERATOR, `Requesting the next number was not possible.`, errRequest.Error())
return
} else {
nextNumberText := responseData.Header.Get(`nextNumber`)
if number, errAtio := strconv.Atoi(nextNumberText); errAtio != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameGENERATOR, `It was not possible to convert the answer into an int64.`, errAtio.Error())
return
} else {
result = int64(number)
return
}
}
}

41
NumGen/HandlerGetNext.go Normal file
View File

@ -0,0 +1,41 @@
package NumGen
import "fmt"
import "net/http"
import "github.com/SommerEngineering/Ocean/Shutdown"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func HandlerGetNext(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
http.NotFound(response, request)
return
}
if !isActive {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactNone, LM.MessageNameCONFIGURATION, `Called the get handler on an inactive host.`, `Wrong configuration?`)
http.NotFound(response, request)
return
}
if correctPassword == `` {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameSECURITY, `No communication password was set.`)
http.NotFound(response, request)
return
}
name := request.FormValue(`name`)
pwd := request.FormValue(`password`)
if pwd != correctPassword {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityCritical, LM.ImpactNone, LM.MessageNamePASSWORD, `A wrong password was used to access this system handler.`, `This should never happens: Is this a hacking attempt?`, `IP address of requester=`+request.RemoteAddr)
http.NotFound(response, request)
return
}
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelDEBUG, LM.MessageNameANALYSIS, `Next number requested.`, name, pwd)
channel := requestChannel4Name(name)
nextNumber := <-channel
response.Header().Add(`nextNumber`, fmt.Sprintf(`%d`, nextNumber))
}

38
NumGen/Init.go Normal file
View File

@ -0,0 +1,38 @@
package NumGen
import "strings"
import "strconv"
import "github.com/SommerEngineering/Ocean/Tools"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func init() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Init the number generator.`)
channelListLock.Lock()
defer channelListLock.Unlock()
correctPassword = ConfigurationDB.Read(`InternalCommPassword`)
activeHost := ConfigurationDB.Read(`NumGenActiveHosts`)
isActive = strings.Contains(activeHost, Tools.ThisHostname())
getHandler = ConfigurationDB.Read(`NumGenGetHandler`)
if isActive {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.MessageNameCONFIGURATION, `The number generator is active on this host.`, `This host is producer and consumer.`)
channelBufferSizeText := ConfigurationDB.Read(`NumGenBufferSize`)
if bufferSizeNumber, errBufferSizeNumber := strconv.Atoi(channelBufferSizeText); errBufferSizeNumber != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactMiddle, LM.MessageNameCONFIGURATION, `Was not able to parse the configuration value of NumGenBufferSize.`, errBufferSizeNumber.Error(), `Use the default value now!`)
} else {
channelBufferSize = bufferSizeNumber
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The buffer size for the number generator was loaded.`, `Buffer size=`+channelBufferSizeText)
}
channelList = make(map[string]chan int64)
initDB()
} else {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.MessageNameCONFIGURATION, `The number generator is not active on this host.`, `This host is just a consumer.`)
}
}

28
NumGen/InitDB.go Normal file
View File

@ -0,0 +1,28 @@
package NumGen
import "labix.org/v2/mgo"
import "github.com/SommerEngineering/Ocean/CustomerDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func initDB() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Start init of number generator collection.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Done init of number generator collection.`)
// Get the database:
db = CustomerDB.DB()
if db == nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the customer database.`)
return
}
// Get my collection:
collectionNumGen = db.C(`NumGen`)
// Take care about the indexes:
indexName := mgo.Index{}
indexName.Key = []string{`Name`}
indexName.Unique = true
collectionNumGen.EnsureIndex(indexName)
}

68
NumGen/Producer.go Normal file
View File

@ -0,0 +1,68 @@
package NumGen
import "time"
import "labix.org/v2/mgo/bson"
import "github.com/SommerEngineering/Ocean/Shutdown"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func producer(name string) {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The NumGen producer is now starting.`, `name=`+name)
// Get my channel:
myChannel := requestChannel4Name(name)
// Read my next free number:
currentNextFreeNumber := nextFreeNumberFromDatabase(name)
// Where is the next "reload"?
nextReload := currentNextFreeNumber + int64(channelBufferSize)
// Set the next free number to the database:
updateNextFreeNumber2Database(name, nextReload)
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The NumGen producer is now running.`, `name=`+name)
for nextNumber := currentNextFreeNumber; true; {
if Shutdown.IsDown() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSHUTDOWN, `The NumGen producer is now down.`, `name=`+name)
return
}
if nextNumber > nextReload {
nextReload = nextReload + int64(channelBufferSize)
updateNextFreeNumber2Database(name, nextReload)
// Enables the administrator to monitor the frequence of chunks and is able to reconfigure the settings:
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelDEBUG, LM.MessageNamePRODUCER, `The NumGen producer creates the next chunk.`, `name=`+name)
}
// Enqueue the next number:
select {
case myChannel <- nextNumber:
nextNumber++
case <-time.After(time.Millisecond * 500):
}
}
}
func nextFreeNumberFromDatabase(name string) (result int64) {
selection := bson.D{{`Name`, name}}
searchResult := NumberGenScheme{}
count, _ := collectionNumGen.Find(selection).Count()
if count == 1 {
collectionNumGen.Find(selection).One(&searchResult)
result = searchResult.NextFreeNumber
} else {
searchResult.Name = name
searchResult.NextFreeNumber = startValue64
collectionNumGen.Insert(searchResult)
result = searchResult.NextFreeNumber
}
return
}
func updateNextFreeNumber2Database(name string, nextFreeNumber int64) {
selection := bson.D{{`Name`, name}}
collectionNumGen.Update(selection, NumberGenScheme{Name: name, NextFreeNumber: nextFreeNumber})
}

38
NumGen/RequestChannel.go Normal file
View File

@ -0,0 +1,38 @@
package NumGen
import "github.com/SommerEngineering/Ocean/Shutdown"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func requestChannel4Name(name string) (result chan int64) {
if Shutdown.IsDown() {
return
}
if !isActive {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactNone, LM.MessageNameCONFIGURATION, `Called the requestChannel4Name() on an inactive host.`, `Wrong configuration?`)
return
}
channelListLock.RLock()
channel, isPresent := channelList[name]
channelListLock.RUnlock()
if isPresent {
result = channel
return
}
// Create the entry:
newChannel := make(chan int64, channelBufferSize)
result = newChannel
channelListLock.Lock()
channelList[name] = newChannel
channelListLock.Unlock()
// Create the new producer:
go producer(name)
return
}

6
NumGen/Scheme.go Normal file
View File

@ -0,0 +1,6 @@
package NumGen
type NumberGenScheme struct {
Name string `bson:"Name"`
NextFreeNumber int64 `bson:"NextFreeNumber"`
}

11
NumGen/Shutdown.go Normal file
View File

@ -0,0 +1,11 @@
package NumGen
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
type ShutdownFunction struct {
}
func (a ShutdownFunction) Shutdown() {
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Shutting down the number generator.`)
}

22
NumGen/Variables.go Normal file
View File

@ -0,0 +1,22 @@
package NumGen
import "sync"
import "labix.org/v2/mgo"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
var (
correctPassword string = ``
senderName LM.Sender = `System::NumGen::Producer`
isActive bool = false
getHandler string = ``
db *mgo.Database = nil
collectionNumGen *mgo.Collection = nil
channelBufferSize int = 10
channelList map[string]chan int64 = nil
channelListLock sync.RWMutex = sync.RWMutex{}
)
const (
badNumber64 int64 = 9222222222222222222
startValue64 int64 = -9223372036854775808
)

17
Robots/Handler.go Normal file
View File

@ -0,0 +1,17 @@
package Robots
import "fmt"
import "net/http"
import "github.com/SommerEngineering/Ocean/Shutdown"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func HandlerRobots(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
http.NotFound(response, request)
return
}
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameNETWORK, `The robots.txt was requested.`, request.RemoteAddr)
fmt.Fprintf(response, `%s`, robotsContent)
}

10
Robots/Init.go Normal file
View File

@ -0,0 +1,10 @@
package Robots
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func init() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Init the robots component.`)
robotsContent = ConfigurationDB.Read(`robots.txt`)
}

9
Robots/Variables.go Normal file
View File

@ -0,0 +1,9 @@
package Robots
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
var (
senderName LM.Sender = `System::Robots`
robotsContent string = `User-agent: *
Disallow: /`
)

6
Shutdown/Check.go Normal file
View File

@ -0,0 +1,6 @@
package Shutdown
func IsDown() (result bool) {
result = stopAllRequests
return
}

13
Shutdown/Init.go Normal file
View File

@ -0,0 +1,13 @@
package Shutdown
import "container/list"
import "os/signal"
import "os"
func InitShutdown() {
shutdownHandlers = list.New()
// Apply the shutdown handler:
signal.Notify(shutdownSignal, os.Interrupt, os.Kill)
go executeShutdown()
}

29
Shutdown/Shutdown.go Normal file
View File

@ -0,0 +1,29 @@
package Shutdown
import "os"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
type ShutdownHandler interface {
Shutdown()
}
func AddShutdownHandler(handler ShutdownHandler) {
shutdownHandlers.PushBack(handler)
}
func executeShutdown() {
sig := <-shutdownSignal
stopAllRequests = true
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameSHUTDOWN, `The system was called to shutting down.`, sig.String(), `Call now all shutdown handlers.`)
for handler := shutdownHandlers.Front(); handler != nil; handler = handler.Next() {
h := handler.Value.(ShutdownHandler)
h.Shutdown()
}
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameSHUTDOWN, `The system is shutting down now.`)
Log.Flush()
os.Exit(6)
}

12
Shutdown/Variables.go Normal file
View File

@ -0,0 +1,12 @@
package Shutdown
import "os"
import "container/list"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
var (
shutdownSignal chan os.Signal = make(chan os.Signal)
shutdownHandlers *list.List = nil
senderName LM.Sender = `System::Shutdown`
stopAllRequests bool = false
)

View File

@ -0,0 +1,47 @@
package StaticFiles
import "io/ioutil"
import "bytes"
import "archive/zip"
import "github.com/SommerEngineering/Ocean/Shutdown"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func FindAndReadFile(filename string) (result []byte) {
if Shutdown.IsDown() {
return
}
// Prepare the path:
path := filename
// Read the content from the ZIP file:
reader, readerError := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData)))
if readerError != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameREAD, `Was not able to read the ZIP file.`, readerError.Error())
return
}
for _, file := range reader.File {
if file.Name == path {
fileReader, openError := file.Open()
defer fileReader.Close()
if openError == nil {
contentData, readError := ioutil.ReadAll(fileReader)
if readError != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameREAD, `Was not able to read the content of the desired file.`, readError.Error(), path)
return
}
result = contentData
return
}
}
}
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameNOTFOUND, `The desired file is not part of the ZIP file.`, `Do you use an old version?`, path)
return
}

57
StaticFiles/Handler.go Normal file
View File

@ -0,0 +1,57 @@
package StaticFiles
import "fmt"
import "strings"
import "net/http"
import "github.com/SommerEngineering/Ocean/Shutdown"
import "github.com/SommerEngineering/Ocean/MimeTypes"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func HandlerStaticFiles(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
http.NotFound(response, request)
return
}
// Prepare the path:
path := strings.Replace(request.RequestURI, `/staticFiles/`, ``, 1)
path = strings.Replace(path, `%20`, ` `, -1)
fileType := ``
// Determine the MIME type:
if mimeType, errMime := MimeTypes.DetectType(path); errMime != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityMiddle, LM.ImpactMiddle, LM.MessageNameNOTFOUND, `Was not able to detect the MIME type of the font.`, errMime.Error(), path)
http.NotFound(response, request)
return
} else {
fileType = mimeType.MimeType
}
if fileType == `` {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameNOTFOUND, `The mime type is unknown.`, path)
http.NotFound(response, request)
return
}
contentData := FindAndReadFile(path)
if contentData == nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `The desired file was not found.`, path)
http.NotFound(response, request)
return
}
fileLenText := fmt.Sprintf(`%d`, len(contentData))
response.Header().Add(`Content-Length`, fileLenText)
response.Header().Add(`Content-Type`, fileType)
response.Write(contentData)
if logStaticFileRequests {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameBROWSER, `A static file was requested.`, path)
}
return
http.NotFound(response, request)
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameNOTFOUND, `The desired file is not part of the ZIP file.`, `Do you use an old version?`, path)
return
}

44
StaticFiles/Init.go Normal file
View File

@ -0,0 +1,44 @@
package StaticFiles
import "io/ioutil"
import "github.com/SommerEngineering/Ocean/CustomerDB"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func init() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Starting now the static files component.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Starting the static files component done.`)
if ConfigurationDB.Read(`EnableStaticFiles`) != `true` {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Static files are disabled.`)
return
}
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Static files are enabled.`)
// Read the configuration:
if ConfigurationDB.Read(`MapStaticFiles2Root`) == `true` {
startFile4Map2Root = ConfigurationDB.Read(`MapStaticFiles2RootRootFile`)
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The desired root document was set.`, `rootDocument=`+startFile4Map2Root)
}
logStaticFileRequests = ConfigurationDB.Read(`LogStaticFileRequests`) == `true`
// Read the static files' data from GridFS:
gridFS := CustomerDB.GridFS()
if gridFile, errGridFile := gridFS.Open(`staticFiles.zip`); errGridFile != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to open the static files out of the GridFS!`, errGridFile.Error())
return
} else {
defer gridFile.Close()
if data, ioError := ioutil.ReadAll(gridFile); ioError != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to read the static files.`, ioError.Error())
return
} else {
zipData = data
}
}
return
}

19
StaticFiles/Map2Root.go Normal file
View File

@ -0,0 +1,19 @@
package StaticFiles
import "net/http"
import "github.com/SommerEngineering/Ocean/Shutdown"
func HandlerMapStaticFiles2Root(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
http.NotFound(response, request)
return
}
if request.RequestURI == `/` {
request.RequestURI = `/staticFiles/` + startFile4Map2Root
} else {
request.RequestURI = `/staticFiles` + request.RequestURI
}
HandlerStaticFiles(response, request)
}

10
StaticFiles/Variables.go Normal file
View File

@ -0,0 +1,10 @@
package StaticFiles
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
var (
senderName LM.Sender = `System::StaticFiles`
startFile4Map2Root string = `index.html`
logStaticFileRequests bool = false
zipData []byte = nil
)

8
System/ICCCStart.go Normal file
View File

@ -0,0 +1,8 @@
package System
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func icccSystemStart(data map[string][]string) {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The system is now up and ready.`)
}

90
System/Init.go Normal file
View File

@ -0,0 +1,90 @@
package System
import "fmt"
import "runtime"
import "strconv"
import "github.com/SommerEngineering/Ocean/ICCC"
import "github.com/SommerEngineering/Ocean/Shutdown"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/CustomerDB"
import "github.com/SommerEngineering/Ocean/NumGen"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func initSystem() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The system is now starting.`)
// Set the desired amount of CPUs:
utilizeCPUs := 2
if value, err := strconv.Atoi(ConfigurationDB.Read(`OceanUtilizeCPUs`)); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to read the OceanUtilizeCPUs configuration.`, `Use the default value instead.`)
} else {
utilizeCPUs = value
}
runtime.GOMAXPROCS(utilizeCPUs)
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration OceanUtilizeCPUs is set.`, fmt.Sprintf(`value=%d`, utilizeCPUs))
// Apply all desired logging devices:
initLoggingDevices()
// Set the logging buffer size:
logBufferSize := 500
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogBufferSize`)); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to read the LogBufferSize configuration.`, `Use the default value instead.`)
} else {
logBufferSize = value
}
Log.SetBufferSize(logBufferSize)
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogBufferSize is set.`, fmt.Sprintf(`value=%d`, logBufferSize))
// Set the logging device delay (number of events):
logDeviceDelayNumberEvents := 600
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogDeviceDelayNumberEvents`)); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to read the LogDeviceDelayNumberEvents configuration.`, `Use the default value instead.`)
} else {
logDeviceDelayNumberEvents = value
}
Log.SetDeviceDelayNumberEvents(logDeviceDelayNumberEvents)
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDeviceDelayNumberEvents is set.`, fmt.Sprintf(`value=%d`, logDeviceDelayNumberEvents))
// Set the logging device delay time to flush (seconds):
logDeviceDelayTime2FlushSeconds := 5
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogDeviceDelayTime2FlushSeconds`)); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to read the LogDeviceDelayTime2FlushSeconds configuration.`, `Use the default value instead.`)
} else {
logDeviceDelayTime2FlushSeconds = value
}
Log.SetDeviceDelayTimeoutSeconds(logDeviceDelayTime2FlushSeconds)
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDeviceDelayTime2FlushSeconds is set.`, fmt.Sprintf(`value=%d`, logDeviceDelayTime2FlushSeconds))
// Set the logging timeout (seconds):
logTimeoutSeconds := 3
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogTimeoutSeconds`)); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to read the LogTimeoutSeconds configuration.`, `Use the default value instead.`)
} else {
logTimeoutSeconds = value
}
Log.SetTimeoutSeconds(logTimeoutSeconds)
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogTimeoutSeconds is set.`, fmt.Sprintf(`value=%d`, logTimeoutSeconds))
// Apply these changes:
Log.ApplyConfigurationChanges()
Log.LoggingIsReady()
// Register all system shutdown handlers:
Shutdown.InitShutdown()
Shutdown.AddShutdownHandler(ICCC.ShutdownFunction{})
Shutdown.AddShutdownHandler(NumGen.ShutdownFunction{})
Shutdown.AddShutdownHandler(ConfigurationDB.ShutdownFunction{})
Shutdown.AddShutdownHandler(CustomerDB.ShutdownFunction{})
// The logging subsystem is not registered here, because it will be automated called at the end
// Register all system ICCC commands:
ICCC.Registrar(ICCC.ChannelSYSTEM, `System::Start`, icccSystemStart)
}

30
System/InitHandlers.go Normal file
View File

@ -0,0 +1,30 @@
package System
import "net/http"
import "github.com/SommerEngineering/Ocean/ICCC"
import "github.com/SommerEngineering/Ocean/WebContent"
import "github.com/SommerEngineering/Ocean/StaticFiles"
import "github.com/SommerEngineering/Ocean/NumGen"
import "github.com/SommerEngineering/Ocean/Robots"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func InitHandlers() {
initSystem()
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Register now all system handlers.`)
http.HandleFunc(`/framework/`, WebContent.HandlerDeliverFramework)
http.HandleFunc(`/staticFiles/`, StaticFiles.HandlerStaticFiles)
http.HandleFunc(`/next/number`, NumGen.HandlerGetNext)
http.HandleFunc(`/robots.txt`, Robots.HandlerRobots)
http.HandleFunc(`/ICCC`, ICCC.ICCCHandler)
if ConfigurationDB.Read(`MapStaticFiles2Root`) == "true" {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The static files are mapped to the root.`)
http.HandleFunc(`/`, StaticFiles.HandlerMapStaticFiles2Root)
}
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Done with registering all system handler.`)
}

View File

@ -0,0 +1,35 @@
package System
import "github.com/SommerEngineering/Ocean/Log/DeviceConsole"
import "github.com/SommerEngineering/Ocean/Log/DeviceDatabase"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
func initLoggingDevices() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Init the logging devices.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Init the logging devices done.`)
if ConfigurationDB.Read(`LogUseDatabaseLogging`) == `true` {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The database logger is active.`)
activateDatabaseLogger()
} else {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The database logger is NOT active.`)
}
if ConfigurationDB.Read(`LogUseConsoleLogging`) == `true` {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The console logger is active.`)
activateConsoleLogger()
} else {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The console logger is NOT active.`)
}
}
func activateDatabaseLogger() {
DeviceDatabase.ActivateLoggingDevice()
}
func activateConsoleLogger() {
DeviceConsole.ActivateLoggingDevice()
}

Some files were not shown because too many files have changed in this diff Show More