Skip to content

Commit 1d7353b

Browse files
committed
Merge branch 'master' into update/html5shiv-3.7.3-1
2 parents 486adde + 8effc92 commit 1d7353b

24 files changed

Lines changed: 648 additions & 22 deletions

.mergify.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,22 @@ pull_request_rules:
77
- "#review-requested=0"
88
- "#changes-requested-reviews-by=0"
99
- label!=status:block-merge
10+
- label=status:merge-when-green
1011
actions:
1112
merge:
1213
method: merge
13-
strict: smart
1414

15-
- name: Delete the PR branch after merge
15+
- name: Delete the PR branch and remove label after merge
1616
conditions:
1717
- merged
1818
actions:
1919
delete_head_branch: {}
20+
label:
21+
remove: [ "status:merge-when-green" ]
2022

21-
- name: auto add wip
23+
- name: Labeling for Scala Steward PR's
2224
conditions:
23-
# match a few flavours of wip
24-
- title~=^(\[wip\]( |:) |\[WIP\]( |:) |wip( |:) |WIP( |:)).*
25+
- author=scala-steward
2526
actions:
2627
label:
27-
add: ["status:block-merge"]
28+
add: [ "type:updates" ]

.scala-steward.conf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pullRequests.frequency = "@monthly"
2+
3+
updates.ignore = [
4+
{ groupId = "org.scalameta", artifactId = "scalafmt-core" }
5+
]
6+
7+
commits.message = "${artifactName} ${nextVersion} (was ${currentVersion})"

.scalafmt.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ rewrite.rules = [ AvoidInfix, ExpandImportSelectors, RedundantParens, SortModifi
88
rewrite.sortModifiers.order = [ "private", "protected", "final", "sealed", "abstract", "implicit", "override", "lazy" ]
99
spaces.inImportCurlyBraces = true # more idiomatic to include whitepsace in import x.{ yyy }
1010
trailingCommas = always
11-
version = 2.6.3
11+
version = 2.6.4

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
dist: trusty
22
language: scala
3-
scala: 2.12.8
3+
scala: 2.12.13
44
jdk: oraclejdk8
55

66
before_script:

app/assets/css/modules/_oneTrust.styl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,47 @@ body .optanon-alert-box-wrapper .optanon-alert-box-bottom-padding
6161
&:hover
6262
background: green-dark
6363
color: white
64+
65+
//ontrust ccpa upgrades
66+
body
67+
#onetrust-pc-sdk .ot-switch
68+
width: 45px !important
69+
padding: 0 !important
70+
#onetrust-pc-sdk .ot-switch-nob:before
71+
transform: translateX(2px)
72+
.onetrust-pc-dark-filter
73+
background: rgba(0,0,0,.75)
74+
#onetrust-button-group
75+
display: flex !important
76+
flex-direction: row-reverse !important
77+
justify-content: flex-end !important
78+
text-align: left !important
79+
#onetrust-accept-btn-handler
80+
opacity: 1 !important
81+
transition: anime
82+
outline: none !important
83+
&:hover
84+
background-color: green !important
85+
border-color: green !important
86+
transition: anime
87+
#onetrust-pc-sdk #close-pc-btn-handler.ot-close-icon
88+
outline: none !important
89+
.ot-pc-footer-logo
90+
display: none
91+
#ot-sdk-cookie-policy,
92+
#cookie-policy-description,
93+
.ot-sdk-cookie-policy-group-desc
94+
font-size: 1rem !important
95+
line-height: 1.5 !important
96+
margin-bottom: 1.0625rem !important
97+
& p
98+
font-size: 1rem !important
99+
line-height: 1.5 !important
100+
margin-bottom: 1.0625rem !important
101+
#onetrust-pc-sdk .ot-cat-grp .ot-always-active
102+
color: #6cc04a
103+
#onetrust-pc-sdk .ot-tgl input:checked+.ot-switch .ot-switch-nob
104+
border-color: #6cc04a
105+
background-color: #d4e8cb
106+
&:before
107+
background-color: #6cc04a

app/controllers/Application.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,14 @@ class Application @Inject()(
164164
}
165165

166166
def onHandlerNotFound(route: String) = Action { implicit request =>
167-
if (route.endsWith("/")) {
167+
168+
if (route.startsWith("play-") && route.endsWith("-released") && !route.contains("-rc") && !route.contains("-m")) {
169+
val version = route
170+
.replace("play-", "")
171+
.replace("-released", "")
172+
.replace("-", ".")
173+
MovedPermanently(s"https://github.com/playframework/playframework/releases/tag/$version")
174+
} else if (route.endsWith("/")) {
168175
MovedPermanently("/" + request.path.take(request.path.length - 1).dropWhile(_ == '/'))
169176
} else {
170177
notFound

app/controllers/Blog.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package controllers
2+
3+
import java.io.InputStream
4+
5+
import javax.inject.Inject
6+
import javax.inject.Singleton
7+
import models._
8+
import org.apache.commons.io.IOUtils
9+
import play.api._
10+
import play.api.cache.SyncCacheApi
11+
import play.api.i18n.I18nSupport
12+
import play.api.i18n.Lang
13+
import play.api.mvc._
14+
import play.twirl.api.Html
15+
import utils.Markdown
16+
import views._
17+
18+
import scala.concurrent.ExecutionContext
19+
import scala.concurrent.Future
20+
21+
@Singleton
22+
class Blog @Inject()(
23+
components: ControllerComponents,
24+
)(implicit ec: ExecutionContext, val reverseRouter: _root_.controllers.documentation.ReverseRouter)
25+
extends AbstractController(components)
26+
with Common
27+
with I18nSupport {
28+
29+
val blogName = "Play Framework Blog"
30+
31+
def index() = Action { implicit request =>
32+
Ok(html.blog.index(blogName))
33+
}
34+
35+
def graal() = Action { implicit request =>
36+
Ok(html.blog.graal(blogName, "Running Play on GraalVM"))
37+
}
38+
39+
def socketio() = Action { implicit request =>
40+
Ok(html.blog.socketio(blogName, "Play socket.io support"))
41+
}
42+
43+
}

app/views/blog/graal.scala.html

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
@import controllers.documentation.ReverseRouter
2+
@(blogName:String, title: String)(implicit req: RequestHeader, reverseRouter: ReverseRouter)
3+
@main(title, blogName){
4+
<header id="top">
5+
<div class="wrapper">
6+
<h1>
7+
@blogName
8+
</h1>
9+
</div>
10+
</header>
11+
<section id="content">
12+
<article>
13+
<br/>
14+
<h1>@title</h1>
15+
16+
<p>
17+
Christian Schmitt, 10 MAY 2018
18+
</p>
19+
20+
21+
<p>On the 17th of April, Oracle Labs presented the community the first release cadence for their new universal virtual machine called <a href="https://www.graalvm.org/">GraalVM</a>. Graal is a Polyglot VM that can run multiple languages and can interop between them without any overhead. In this blog post I will go into details what this means for Scala and especially for Play Framework and what runtime characteristics the new VM has, when running Play on top of it.</p>
22+
<p>Graal currently comes in <a href="https://www.graalvm.org/downloads/">two flavors</a>, one is the Community Edition which is open source and comes with the same license as a regular OpenJDK VM. Sadly at the moment the Community Edition is only available for Linux, which is good for production but mostly not enough for everyday development if you are not running Linux on your development machine.</p>
23+
<p>There is also another edition called the Enterprise Edition which is not open source and you need to acquire a license to use it in production, but according to the docs it’s safe to use for development and evaluation. The Enterprise Edition is currently available for macOS and Linux, it has further benefits (comes with a smaller footprint and has more sandbox capabilities).</p>
24+
<p>In the future the Graal team will probably present us with more options regarding the operating system. For our blog post we stick to the Community Edition on Linux.</p>
25+
<h3 id="play-production-mode">Play Production Mode</h3>
26+
<p>Running Play or any Scala application on Graal is probably as easy as just switching to another Java VM. We will build the <a href="https://example.lightbend.com/v1/download/play-scala-starter-example">Play example project</a>, via <a href="https://www.playframework.com/documentation/2.6.x/Deploying#Using-the-SBT-assembly-plugin">sbt-assembly</a> and copy the production JAR to a regular server.</p>
27+
<p>After downloading Graal and unpacking it, one can just run the application via <code>$GRAAL_HOME/bin/java -Xms3G -Xmx3G -XX:+UseG1GC -jar play-scala-starter-example-assembly-1.0-SNAPSHOT.jar</code>. Keep in mind for a production run, one would use a service manager or run the application inside an orchestration system like Kubernetes.</p>
28+
<p>The Play application started without any problem and one could use <code>curl</code> to ensure it is running via <code>curl http://graalserver:9000/</code> and it will print the Play “hello world page”.</p>
29+
<h2 id="-performance-of-graal">“Performance” of Graal</h2>
30+
<p>After having the application running we can check how many requests/s it can serve via Graal, so we start up <code>wrk</code> with the following params: <code>wrk -c100 -d1m -t2 http://graalserver:9000</code> and get an output like that (after a few runs):</p>
31+
<pre><code>Running <span class="hljs-number">1</span>m test @@ <span class="hljs-keyword">http</span>://<span class="hljs-number">195.201</span><span class="hljs-number">.117</span><span class="hljs-number">.210</span>:<span class="hljs-number">9000</span>
32+
<span class="hljs-number">2</span> threads <span class="hljs-keyword">and</span> <span class="hljs-number">100</span> connections
33+
Thread Stats Avg Stdev Max +/- Stdev
34+
Latency <span class="hljs-number">33.83</span>ms <span class="hljs-number">62.66</span>ms <span class="hljs-number">1.56</span>s <span class="hljs-number">94.36</span>%
35+
Req/Sec <span class="hljs-number">2.15</span>k <span class="hljs-number">314.10</span> <span class="hljs-number">3.17</span>k <span class="hljs-number">68.92</span>%
36+
<span class="hljs-number">255800</span> requests <span class="hljs-keyword">in</span> <span class="hljs-number">1.00</span>m, <span class="hljs-number">1.76</span>GB <span class="hljs-built_in">read</span>
37+
Requests/<span class="hljs-built_in">sec</span>: <span class="hljs-number">4260.50</span>
38+
Transfer/<span class="hljs-built_in">sec</span>: <span class="hljs-number">30.07</span>MB
39+
</code></pre><p>We can also compare that with a regular JVM which will output the following (after a few runs):</p>
40+
<pre><code>Running <span class="hljs-number">1</span>m test @@ <span class="hljs-keyword">http</span>://<span class="hljs-number">195.201</span><span class="hljs-number">.117</span><span class="hljs-number">.210</span>:<span class="hljs-number">9000</span>
41+
<span class="hljs-number">2</span> threads <span class="hljs-keyword">and</span> <span class="hljs-number">100</span> connections
42+
Thread Stats Avg Stdev Max +/- Stdev
43+
Latency <span class="hljs-number">38.41</span>ms <span class="hljs-number">70.39</span>ms <span class="hljs-number">1.79</span>s <span class="hljs-number">97.70</span>%
44+
Req/Sec <span class="hljs-number">1.62</span>k <span class="hljs-number">219.60</span> <span class="hljs-number">3.10</span>k <span class="hljs-number">74.37</span>%
45+
<span class="hljs-number">193123</span> requests <span class="hljs-keyword">in</span> <span class="hljs-number">1.00</span>m, <span class="hljs-number">1.33</span>GB <span class="hljs-built_in">read</span>
46+
Requests/<span class="hljs-built_in">sec</span>: <span class="hljs-number">3216.56</span>
47+
Transfer/<span class="hljs-built_in">sec</span>: <span class="hljs-number">22.70</span>MB
48+
</code></pre><p>As we can see Graal will be way faster compared to a regular JVM. The performance boost probably comes from better <a href="https://en.wikipedia.org/wiki/Escape_analysis"><em>escape analysis</em></a>. Keep in mind that the performance will be less on your own tests since you probably won’t run “hello world” on your systems.</p>
49+
<h2 id="aot-compilation">AoT compilation</h2>
50+
<p>Currently Graal also has a way to compile a Java application to a single binary via <code>native-image</code>. However on Scala 2.12 <code>native-image</code> won’t work since Scala 2.12 relies on the <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/multiple-language-support.html#invokedynamic"><em>invokedynamic</em></a> bytecode instruction which as of now is <a href="https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md">not supported</a> in <a href="https://github.com/oracle/graal/tree/master/substratevm">SubstrateVM</a>. But for reference I tried to use native-image on Scala 2.11.</p>
51+
<p>To make that work I used <a href="https://www.playframework.com/documentation/2.6.x/Deploying#Using-the-dist-task">sbt-assembly</a> to create a standalone JAR that I can use as a reference to my <code>native-image</code>.</p>
52+
<p>Sadly that also won’t work and will fail with the following error:</p>
53+
<pre><code>native-image --no-server -jar target/scala<span class="hljs-string">-2</span>.11/play-scala-seed-assembly<span class="hljs-string">-1</span>.0-SNAPSHOT.jar
54+
classlist: 10,847.38 ms
55+
(cap): 4,676.51 ms
56+
setup: 5,769.91 ms
57+
warning: unknown locality of class Lplay/api/ApplicationLoader$JavaApplicationLoaderAdapter$1;, assuming class is not local. To remove the warning report an issue to the library or language author. The issue is caused by Lplay/api/ApplicationLoader$JavaApplicationLoaderAdapter$1; which is not following the naming convention.
58+
analysis: 8,730.53 ms
59+
<span class="hljs-keyword">error: </span>unsupported features in 3 methods
60+
Detailed message:
61+
<span class="hljs-keyword">Error: </span>Must not have a FileDescriptor in the image heap.
62+
Trace: object java.io.FileOutputStream
63+
object java.io.BufferedOutputStream
64+
</code></pre><h2 id="polyglot">Polyglot</h2>
65+
<p>One feature I was excited the most was support for Polyglot, which means that you can run other languages on top of the GraalVM. This is useful for interop with “native” languages or even JavaScript.</p>
66+
<p>Sadly in the current form JavaScript can’t run NodeJS code from a Java Context which means that if I start my program with <code>java my.package.Main</code> and try to call into JavaScript that it can’t run Node. See: <a href="https://github.com/graalvm/graaljs/issues/2">https://github.com/graalvm/graaljs/issues/2</a> for more details on the problem.</p>
67+
<p>But what worked perfectly fine, was calling into native code. In the following example I just try to make a request to example.com via libcurl and print the response code inside my play controller.</p>
68+
<p>For that to work we first need to create a C file:</p>
69+
<pre><code><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>
70+
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;curl/curl.h&gt;</span></span>
71+
72+
<span class="hljs-function"><span class="hljs-keyword">long</span> <span class="hljs-title">request</span><span class="hljs-params">()</span> </span>{
73+
CURL *curl = curl_easy_init();
74+
<span class="hljs-keyword">long</span> response_code = <span class="hljs-number">-1</span>;
75+
76+
<span class="hljs-keyword">if</span>(curl) {
77+
CURLcode res;
78+
curl_easy_setopt(curl, CURLOPT_URL, <span class="hljs-string">"http://example.com"</span>);
79+
res = curl_easy_perform(curl);
80+
<span class="hljs-keyword">if</span>(res == CURLE_OK) {
81+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &amp;response_code);
82+
}
83+
curl_easy_cleanup(curl);
84+
}
85+
86+
87+
<span class="hljs-keyword">return</span> response_code;
88+
}
89+
</code></pre><p>and turn it into bitcode via: <code>clang -c -O1 -emit-llvm graal.c</code> .</p>
90+
<p>Than we need to add <code>graal-sdk</code> to our <code>build.sbt</code> via:</p>
91+
<pre><code><span class="hljs-attribute">libraryDependencies</span> += <span class="hljs-string">"org.graalvm"</span> % <span class="hljs-string">"graal-sdk"</span> % <span class="hljs-string">"1.0.0-rc1"</span>
92+
</code></pre><p>After that we can change one of our Play controllers to invoke it:</p>
93+
<pre><code>private <span class="hljs-meta">val</span> cpart = {
94+
<span class="hljs-meta">val</span> polyglot = <span class="hljs-type">Context</span>
95+
.newBuilder()
96+
.allowAllAccess(<span class="hljs-literal">true</span>)
97+
.option(<span class="hljs-string">"llvm.libraries"</span>, <span class="hljs-string">"/usr/lib/libcurl.dylib"</span>)
98+
.build()
99+
<span class="hljs-meta">val</span> source = <span class="hljs-type">Source</span>
100+
.newBuilder(<span class="hljs-string">"llvm"</span>, <span class="hljs-function"><span class="hljs-keyword">new</span> <span class="hljs-title">File</span>("/<span class="hljs-type">Users</span>/play/projects/scala/play-scala-seed/graal.bc"))
101+
.<span class="hljs-title">build</span>()
102+
<span class="hljs-title">polyglot</span>.<span class="hljs-title">eval</span>(source)
103+
}
104+
105+
<span class="hljs-title">def</span> <span class="hljs-title">index</span>() = <span class="hljs-title">Action</span> { <span class="hljs-title">implicit</span> <span class="hljs-title">request</span>: <span class="hljs-type">Request</span>[<span class="hljs-type">AnyContent</span>] =&gt;</span>
106+
<span class="hljs-meta">val</span> responseValue = cpart.getMember(<span class="hljs-string">"request"</span>).execute()
107+
<span class="hljs-meta">val</span> responseCode = responseValue.asLong()
108+
109+
<span class="hljs-type">Ok</span>(s”$responseCode”)
110+
}
111+
</code></pre><p>Creating a polyglot <code>Context</code> from Java and calling into another language currently works for the following languages (some which might be more experimental than others): JavaScript, all languages which can be turned into bitcode (C, C++, Rust, etc…), Python 3, R and Ruby.</p>
112+
<h2 id="conclusion">Conclusion</h2>
113+
<p>In most cases Graal will actually run your Play application way faster than a regular JVM. Graal is especially good in running Scala code, since it has a way better <a href="https://en.wikipedia.org/wiki/Escape_analysis"><em>escape analysis</em></a>. However it can depend on your workload and what you do, so it’s probably a good idea to take a look at Graal by yourself.</p>
114+
<p>If you are trying to interop with other languages Graal might also be a really good fit, since most languages can just be executed/run from a simple “Context” and Graal will also try his best to make the code as performant as possible.</p>
115+
116+
117+
</article>
118+
</section>
119+
}
120+

app/views/blog/index.scala.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@import controllers.documentation.ReverseRouter
2+
@(title: String)(implicit req: RequestHeader, reverseRouter: ReverseRouter)
3+
@main(title, "blog"){
4+
<header id="top">
5+
<div class="wrapper">
6+
<h1>
7+
@title
8+
</h1>
9+
</div>
10+
</header>
11+
<section id="content">
12+
<br/>
13+
<article>
14+
<ul>
15+
<li><a href="@routes.Blog.graal()">Running Play on GraalVM</a></li>
16+
<li><a href="@routes.Blog.socketio()">Play socket.io support</a></li>
17+
</ul>
18+
</article>
19+
</section>
20+
}
21+

0 commit comments

Comments
 (0)