/
NettyServer.scala
188 lines (153 loc) · 5.21 KB
/
NettyServer.scala
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package play.core.server
import org.jboss.netty.buffer._
import org.jboss.netty.channel._
import org.jboss.netty.bootstrap._
import org.jboss.netty.channel.Channels._
import org.jboss.netty.handler.codec.http._
import org.jboss.netty.channel.socket.nio._
import org.jboss.netty.handler.stream._
import org.jboss.netty.handler.codec.http.HttpHeaders._
import org.jboss.netty.handler.codec.http.HttpHeaders.Names._
import org.jboss.netty.handler.codec.http.HttpHeaders.Values._
import org.jboss.netty.channel.group._
import java.util.concurrent._
import play.core._
import play.core.server.websocket._
import play.api._
import play.api.mvc._
import play.api.libs.iteratee._
import play.api.libs.iteratee.Input._
import play.api.libs.concurrent._
import scala.collection.JavaConverters._
import netty._
/**
* provides a stopable Server
*/
trait ServerWithStop {
def stop(): Unit
}
/**
* creates a Server implementation based Netty
*/
class NettyServer(appProvider: ApplicationProvider, port: Int, address: String = "0.0.0.0", val mode: Mode.Mode = Mode.Prod) extends Server with ServerWithStop {
def applicationProvider = appProvider
val bootstrap = new ServerBootstrap(
new org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()))
val allChannels = new DefaultChannelGroup
val defaultUpStreamHandler = new PlayDefaultUpstreamHandler(this, allChannels)
class DefaultPipelineFactory extends ChannelPipelineFactory {
def getPipeline = {
val newPipeline = pipeline()
newPipeline.addLast("decoder", new HttpRequestDecoder(4096, 8192, 8192))
newPipeline.addLast("encoder", new HttpResponseEncoder())
newPipeline.addLast("handler", defaultUpStreamHandler)
newPipeline
}
}
bootstrap.setPipelineFactory(new DefaultPipelineFactory)
allChannels.add(bootstrap.bind(new java.net.InetSocketAddress(address, port)))
mode match {
case Mode.Test =>
case _ => Logger("play").info("Listening for HTTP on port %s...".format(port))
}
override def stop() {
try {
Play.stop()
} catch {
case e => Logger("play").error("Error while stopping the application", e)
}
try {
super.stop()
} catch {
case e => Logger("play").error("Error while stopping akka", e)
}
mode match {
case Mode.Test =>
case _ => Logger("play").info("Stopping server...")
}
allChannels.close().awaitUninterruptibly()
bootstrap.releaseExternalResources()
}
}
/**
* bootstraps Play application with a NettyServer backened
*/
object NettyServer {
import java.io._
import java.net._
/**
* creates a NettyServer based on the application represented by applicationPath
* @param applicationPath path to application
*/
def createServer(applicationPath: File): Option[NettyServer] = {
// Manage RUNNING_PID file
java.lang.management.ManagementFactory.getRuntimeMXBean.getName.split('@').headOption.map { pid =>
val pidFile = Option(System.getProperty("pidfile.path")).map(new File(_)).getOrElse(new File(applicationPath.getAbsolutePath, "RUNNING_PID"))
// The Logger is not initialized yet, we print the Process ID on STDOUT
println("Play server process ID is " + pid)
if (pidFile.getAbsolutePath != "/dev/null") {
if (pidFile.exists) {
println("This application is already running (Or delete "+ pidFile.getAbsolutePath +" file).")
System.exit(-1)
}
new FileOutputStream(pidFile).write(pid.getBytes)
Runtime.getRuntime.addShutdownHook(new Thread {
override def run {
pidFile.delete()
}
})
}
}
try {
val server = new NettyServer(
new StaticApplication(applicationPath),
Option(System.getProperty("http.port")).map(Integer.parseInt(_)).getOrElse(9000),
Option(System.getProperty("http.address")).getOrElse("0.0.0.0"))
Runtime.getRuntime.addShutdownHook(new Thread {
override def run {
server.stop()
}
})
Some(server)
} catch {
case e => {
println("Oops, cannot start the server.")
e.printStackTrace()
None
}
}
}
/**
* attempts to create a NettyServer based on either
* passed in argument or `user.dir` System property or current directory
* @param args
*/
def main(args: Array[String]) {
args.headOption.orElse(
Option(System.getProperty("user.dir"))).map(new File(_)).filter(p => p.exists && p.isDirectory).map { applicationPath =>
createServer(applicationPath).getOrElse(System.exit(-1))
}.getOrElse {
println("Not a valid Play application")
}
}
/**
* provides a NettyServer for the dev environment
*/
def mainDev(sbtLink: SBTLink, port: Int): NettyServer = {
play.utils.Threads.withContextClassLoader(this.getClass.getClassLoader) {
try {
val appProvider = new ReloadableApplication(sbtLink)
new NettyServer(appProvider, port, mode = Mode.Dev)
} catch {
case e => {
throw e match {
case e: ExceptionInInitializerError => e.getCause
case e => e
}
}
}
}
}
}