-
Notifications
You must be signed in to change notification settings - Fork 5
/
revmgo.go
156 lines (140 loc) · 4.93 KB
/
revmgo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package revmgo
import (
"fmt"
"github.com/revel/revel"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"reflect"
)
var (
Session *mgo.Session // Global mgo Session
Dial string // http://godoc.org/gopkg.in/mgo.v2#Dial
Method string // clone, copy, new http://godoc.org/gopkg.in/mgo.v2#Session.New
mgoSessionDupl func() *mgo.Session // Holds a the function to call for a given Method
)
// Optimization: Stores the method to call in mgoSessionDupl so that it only
// has to be looked up once (or whenever Session changes)
func setDuplMethod() {
// Save which function to call for each request:
switch Method {
case "clone":
mgoSessionDupl = Session.Clone
case "copy":
mgoSessionDupl = Session.Copy
case "new":
mgoSessionDupl = Session.New
default:
mgoSessionDupl = Session.Clone
}
}
func AppInit() {
var err error
// Read configuration.
Dial = revel.Config.StringDefault("revmgo.dial", "localhost")
Method = revel.Config.StringDefault("revmgo.method", "clone")
revel.INFO.Printf("Dialing Mongo DB: %s with method %s", Dial, Method)
if err = MethodError(Method); err != nil {
revel.ERROR.Panic(err)
}
// Let's try to connect to Mongo DB right upon starting revel but don't
// raise an error. Errors will be handled if there is actually a request
if Session == nil {
Session, err = mgo.Dial(Dial)
if err != nil {
// Only warn since we'll retry later for each request
revel.WARN.Printf("Could not connect to Mongo DB. Error: %s", err)
} else {
setDuplMethod()
}
}
// register the custom bson.ObjectId binder
objId := bson.NewObjectId()
revel.TypeBinders[reflect.TypeOf(objId)] = ObjectIdBinder
}
func ControllerInit() {
revel.InterceptMethod((*MongoController).Begin, revel.BEFORE)
revel.InterceptMethod((*MongoController).End, revel.FINALLY)
}
type MongoController struct {
*revel.Controller
MongoSession *mgo.Session // named MongoSession to avoid collision with revel.Session
}
// Connect to mgo if we haven't already and return a copy/new/clone of the session
func (c *MongoController) Begin() revel.Result {
// We may not be connected yet if revel was started before Mongo DB or
// Mongo DB was restarted
if Session == nil {
var err error
Session, err = mgo.Dial(Dial)
if err != nil {
// Extend the error description to include that this is a Mongo Error
err = fmt.Errorf("Could not connect to Mongo DB. Error: %s", err)
return c.RenderError(err)
} else {
setDuplMethod()
}
}
// Calls Clone(), Copy() or New() depending on the configuration
c.MongoSession = mgoSessionDupl()
return nil
}
// Close the controller session if we have an active one.
func (c *MongoController) End() revel.Result {
// This is necessary since End() will be called no matter what
// (revel.FINALLY) so it may not be connected in which case MongoSession
// were a nil pointer and panic
if c.MongoSession != nil {
c.MongoSession.Close()
}
return nil
}
// No InterceptMethod for Jobs so get the Session and defer Session.Close()
func GetSession() (*mgo.Session, error) {
if Session == nil {
var err error
Session, err = mgo.Dial(Dial)
if err != nil {
err = fmt.Errorf("Could not connect to Mongo DB. Error: %s", err)
return nil, err
} else {
setDuplMethod()
}
}
return mgoSessionDupl(), nil
}
func MethodError(m string) error {
switch m {
case "clone", "copy", "new":
return nil
}
return fmt.Errorf("revmgo: Invalid session instantiation method '%s'", m)
}
// Custom TypeBinder for bson.ObjectId
// Makes additional Id parameters in actions obsolete
var ObjectIdBinder = revel.Binder{
// Make a ObjectId from a request containing it in string format.
Bind: revel.ValueBinder(func(val string, typ reflect.Type) reflect.Value {
if len(val) == 0 {
return reflect.Zero(typ)
}
if bson.IsObjectIdHex(val) {
objId := bson.ObjectIdHex(val)
return reflect.ValueOf(objId)
} else {
revel.ERROR.Print("ObjectIdBinder.Bind - invalid ObjectId!")
return reflect.Zero(typ)
}
}),
// Turns ObjectId back to hexString for reverse routing
Unbind: func(output map[string]string, name string, val interface{}) {
var hexStr string
hexStr = fmt.Sprintf("%s", val.(bson.ObjectId).Hex())
// not sure if this is too carefull but i wouldn't want invalid ObjectIds in my App
if bson.IsObjectIdHex(hexStr) {
output[name] = hexStr
} else {
revel.ERROR.Print("ObjectIdBinder.Unbind - invalid ObjectId!")
output[name] = ""
}
},
}