Why does my Lift Comet Actor stops working after deployment on Jetty? - scala

I'm playing around with Liftweb and its Comet support and I wrote some code which works fine on my local setup/computer. But as soon as I deploy the webapp to a production system (also Jetty), the CometActor is not doing anything. What is wrong with my code?
package code
package comet
import net.liftweb._
import http._
import net.liftweb.common.{Box, Full}
import net.liftweb.util._
import net.liftweb.actor._
import scala.language.postfixOps
import net.liftweb.util.Helpers._
import net.liftweb.http.js.JsCmds.{SetHtml}
import net.liftweb.http.js.jquery.JqJsCmds.{PrependHtml}
import net.liftweb.http.js.JE.{Str}
import _root_.scala.xml.{Text, NodeSeq}
import org.apache.commons.io.input._
case class LogLine(str: String)
class MyTailerListener(logActor: LiftActor) extends TailerListenerAdapter {
override def handle(line: String) {
logActor ! LogLine(line)
}
}
class CometLogEntry extends CometActor {
val listener = new MyTailerListener(this)
val tailer = Tailer.create(new java.io.File("/var/log/syslog"), listener)
override def defaultPrefix = Full("log_entry")
def render = bind("newest" -> <span id="newest">No log enties yet!</span>)
// Schedule an update every 5 seconds
Schedule.schedule(this, LogLine, 5 seconds)
override def lowPriority = {
case LogLine(str:String) => {
// Prepend the newest log line
partialUpdate(PrependHtml("newest", <li style="list-style-type: none;">{str}</li>))
Schedule.schedule(this, LogLine, 5 seconds)
}
}
}

I'm not sure if this will solve your problem, but your implementation of render could potentially cause issues. Render may be called at any time, not just when the CometActor is created. It should result in an up-to-date view of the component. In this case I'd either store the line you start your tail from so you can re-send the lines from that point or make your render function a NOOP.
Also, please use CSS Selectors instead of bind calls.

This line was causing the problem:
val tailer = Tailer.create(new java.io.File("/var/log/syslog"), listener)
On the server Jetty runs as user jetty which has no sufficient rights to read the file. Strange that I didn't get any "Permission denied" message or something. So, if I run Jetty as root my CometActor works well.
But running Jetty as root is considered dangerous. Here is a better solution:
sudo apt-get install acl
sudo setfacl -m u:jetty:r-x,g:adm:r-x /var/log/
sudo setfacl -m u:jetty:r--,g:adm:r-- /var/log/syslog

Related

How to embed Play 2.6 using Akka HTTP Server?

Play 2.5 used Netty as default and allowed easy embedding. https://www.playframework.com/documentation/2.5.x/ScalaEmbeddingPlay
How is this now done with Akka HTTP Server, which is now the default server backend?
The page https://www.playframework.com/documentation/2.6.x/ScalaEmbeddingPlay is missing.
The documentation was split in two pages:
For Akka HTTP Server: https://www.playframework.com/documentation/2.6.1/ScalaEmbeddingPlayAkkaHttp
For Netty Server: https://www.playframework.com/documentation/2.6.1/ScalaEmbeddingPlayNetty
You should use AkkaHttpServerComponents to avoid deprecated APIs:
import play.api.{ BuiltInComponents, NoHttpFiltersComponents }
import play.api.mvc._
import play.api.routing.Router
import play.api.routing.sird._
import play.core.server.AkkaHttpServerComponents
val server = new AkkaHttpServerComponents with BuiltInComponents with NoHttpFiltersComponents {
// To avoid using the deprecated Action builder while
// keeping the `Action` idiom.
private val Action = defaultActionBuilder
override def router: Router = Router.from {
case GET(p"") => Action {
Results.Ok("Hello, World")
}
}
}.server

Session doesn't work after upgrade to Play Framework 2.5.4, Scala

After upgrading my project to Play Framework 2.5.4 from 2.4.8, I'm having issue while working with session. There are to methods in my controller to test the issue:
def test = Action { implicit request =>
Redirect(controllers.routes.Carts.test2()).withSession(new Session(Map("user" -> "customer")))
}
def test2 = Action { implicit request =>
Ok(request.session.data.toString())
}
Basically test method adds session and redirects to test2 method. When I open page with test method url after redirect I'm getting what I need in the browser:
Map(user -> customer)
But if I refresh page after that I'm getting:
Map(csrfToken ->
ce1a6222484f378d38ab3534c2b400191270395d-1470238791014-c988ce3fe47259173166949a)
So, seems like session works for one request only and then overwrites with csrfToken. I have disabled all the filters. My class with filters looks like this:
class Filters #Inject() () extends HttpFilters {
val filters = Seq.empty
}
Can't understand what is wrong with my code. The same code was working fine before upgrade.
Check in ur application.conf if session.secure is true bring it to
play.http.session.secure=false

When I run tests with scalaTestPlus and Play framework, I get an error "no started application"

I'm trying to make scalaTestPlus work in my play application (I'm using Play! 2.2). It works well until I need a function coming from my application. For instance, if I run this very simple test (by launching in a sbt console "test-only TestName"):
import org.scalatestplus.play._
import org.scalatest._
import Matchers._
class Test extends PlaySpec {
"This test" must {
"run this very simple test without problem" in {
1 mustEqual 1
}
}
}
There is no problem, but as soon as I call a function from my app, like in this code:
class Test extends PlaySpec {
"This test" must {
"run this very simple test without problem" in {
models.Genre.genresStringToGenresSet(Option("test")) //Here is the problem
1 mustEqual 1
}
}
}
I get an error: java.lang.ExceptionInInitializerError: at... Cause: java.lang.RuntimeException: There is no started application (even if my application is running).
I'm probably missing something simple since I'm brand new to ScalaTest, so any help abut what I'm doing wrong would be apreciated ;)
You may need an application in scope when using PlaySpec, as some operations assume that there is a Play application available via Play.current:
class Test extends PlaySpec {
implicit override lazy val app: FakeApplication = FakeApplication(...)
"This test" must {
"run this very simple test without problem" in {
models.Genre.genresStringToGenresSet(Option("test")) //Here is the problem
1 mustEqual 1
}
}
}
Check the functional testing documentation for more details on FakeApplication.
However, I don't think you need should need this for model testing. In the normal ScalaTest docs for play, it seems to just mix inMockitoSugar. But your method call chain may invoke some global state of Play that does require an Application, in which case the FakeApplication is the way to go
As asked by #akauppi, here is a method that works perfectly for me:
import org.scalatestplus.play.{OneAppPerSuite, PlaySpec}
class A extends PlaySpec with OneAppPerSuite {
"a" must {
"return true" in {
//thanks to with OneAppPerSuite, it now works
models.Genre.genresStringToGenresSet(Option("test"))
1 mustBe 1
}
"return false" in {
1 mustBe 2
}
}
}
And I simply launch the test with sbt ~testOnly a

No JUnit tests found in Eclipse using Groovy + Cucumber

I have a Cucumber test in Groovy as follows
import org.junit.runner.RunWith
import cucumber.junit.Cucumber
import cucumber.junit.Cucumber.Options
#RunWith(Cucumber.class)
#Options(features = ["classpath:CarDrive.feature"])
public class FuelCarTest {
}
import cucumber.annotation.en.Given
import cucumber.annotation.en.Then
import cucumber.annotation.en.When
public class FuelCarSteps {
public FuelCarSteps() {
println "FuelCarSteps::FuelCarSteps"
}
#Given("I have a car")
def givenCar() {
println "I have a car"
}
#When("^you fill it with 50 litres of fuel")
def addFuel() {
println "add fuel"
}
#Then("^the tank contains 60 litres")
def checkBalance() {
println "TODO: add check here"
}
}
I can run the test fine using mvn test, but when I try to run it in Eclipse I get
No JUnit tests found
Tried cleaning, rebuilding & restarting
Hi you need to create run configuration for the each test in junit
Provide full path ( like package1.subpackege1.testclassname ) in class input in run config
Control, if you have any errors in the Groovy code, such as Type of expression is statically unknown: or similar. They don't show off as usual errors and could be almost invisible.

How to create a custom 404 page handler with Play 2.0?

What’s the preferred way to handle 404 errors with Play 2.0 and show a nice templated view?
You can override the onHandlerNotFound method on your Global object, e.g.:
object Global extends GlobalSettings {
override def onHandlerNotFound(request: RequestHeader): Result = {
NotFound(views.html.notFound(request))
}
}
Please note that there are really two different problems to solve:
Showing a custom 404 page when there is "no handler found", e.g. when the user goes to an invalid URL, and
Showing a custom 404 (NotFound) page as a valid outcome of an existing handler.
I think the OP was referring to #2 but answers referred to #1.
"No Handler Found" Scenario
In the first scenario, for "no handler found" (i.e. invalid URL), the other answers have it right but to be more detailed, per the Play 2.1 documentation as:
Step 1: add a custom Global object:
import play.api._
import play.api.mvc._
import play.api.mvc.Results._
object Global extends GlobalSettings {
override def onHandlerNotFound(request: RequestHeader): Result = {
NotFound(
views.html.notFoundPage(request.path)
)
}
}
Step 2: add the template. Here's mine:
#(path: String)
<html>
<body>
<h1>Uh-oh. That wasn't found.</h1>
<p>#path</p>
</body>
</html>
Step 3: tweak your conf/application.conf to refer to your new "Global". I put it in the controllers package but it doesn't have to be:
...
application.global=controllers.Global
Step 4: restart and go to an invalid URL.
"Real Handler can't find object" Scenario
In the second scenario an existing handler wants to show a custom 404. For example, the user asked for object "1234" but no such object exists. The good news is that doing this is deceptively easy:
Instead of Ok(), surround your response with NotFound()
For example:
object FruitController extends Controller {
def showFruit(uuidString: String) = Action {
Fruits.find(uuidString) match {
case Some(fruit) => Ok(views.html.showFruit(fruit))
// NOTE THE USE OF "NotFound" BELOW!
case None => NotFound(views.html.noSuchFruit(s"No such fruit: $uuidString"))
}
}
}
What I like about this is the clean separation of the status code (200 vs 404) from the HTML returned (showFruit vs noSuchFruit).
HTH
Andrew
If you want to do the same using Java instead of Scala you can do it in this way (this works for play framework 2.0.3):
Global.java:
import play.GlobalSettings;
import play.mvc.Result;
import play.mvc.Results;
import play.mvc.Http.RequestHeader;
public class Global extends GlobalSettings {
#Override
public Result onHandlerNotFound(RequestHeader request) {
return Results.notFound(views.html.error404.render());
}
}
Asumming that your 404 error template is views.html.error404 (i.e. views/error404.scala.html).
Please note that Play development team are making lots of efforts to move away from global state in Play, and hence GlobalSettings and the application Global object have been deprecated since version 2.4.
HttpErrorHandler.onClientError should be used instead of
GlobalSettings.onHandlerNotFound. Basically create a class that inherits from HttpErrorHandler, and provide an implementation for onClientError method.
In order to find out type of error (404 in your case) you need to read status code, which is passed as a one of the method arguments e.g.
if(statusCode == play.mvc.Http.Status.NOT_FOUND) {
// your code to handle 'page not found' situation
// e.g. return custom implementation of 404 page
}
In order to let Play know what handler to use, you can place your error handler in the root package or configure it in application.conf using play.http.errorHandler configuration key e.g.
play.http.errorHandler = "my.library.MyErrorHandler"
You can find more details on handling errors here: for Scala or Java.
This works in 2.2.1. In Global.java:
public Promise<SimpleResult> onHandlerNotFound(RequestHeader request) {
return Promise.<SimpleResult>pure(notFound(
views.html.throw404.render()
));
}
Ensure that you have a view called /views/throw404.scala.html
This works in 2.2.3 Play - Java
public Promise<SimpleResult> onHandlerNotFound(RequestHeader request) {
return Promise<SimpleResult>pure(Results.notFound(views.html.notFound404.render()));
}
html should be within /views/notFound404.scala.html
Dont forget to add Results.notFounf() and import play.mvc.Results;
For Java, if you want to just redirect to main page, I solved it by this.
#Override
public Promise<Result> onHandlerNotFound(RequestHeader request) {
return Promise.pure(redirect("/"));
}