The idea that Play would be for web developers, not just Java developers, turned out to be PDF—Adds support for PDF output based on HTML templates. □. Where those designations appear in the book, and Manning The JCP has played an instrumental role in organizing the Java community. Summary Play for Java shows you how to build Java-based web applications using the There are two pdf sample chapters provided, and they are in color. There's also a Play for Scala framework and a Play for Scala book from Manning. ).
|Language:||English, Spanish, Portuguese|
|Genre:||Politics & Laws|
|ePub File Size:||28.77 MB|
|PDF File Size:||11.86 MB|
|Distribution:||Free* [*Regsitration Required]|
Where those designations appear in the book, and Manning . Play Framework was created to revolutionize Java web application development. Play is built for. Play for Java shows you how to build Java-based web applications using the Play 2 framework. The book . For a Java developer, the Play web application framework is a breath of fresh air. . eBook $ pdf + ePub + kindle + liveBook. Summary Play for Java shows you how to build Java-based web book includes a free eBook in PDF, Kindle, and ePub formats from Manning Publications.
Specifically, many functional interfaces such as Comparator, Function, and Predicate that are used to pass lambda expressions provide methods that allow composition. Finding and matching Both of these ideas are exploited by the new Streams API we described earlier. A pipeline of operations can be viewed as a database-like query on the data source. Next, you apply a series of data processing operations on the stream: As a result, the lambda can be rewritten using recipe from figure 3. Parameterizing the behavior of filterApples and passing different filter strategies Multiple behaviors, one parameter As we explained earlier, behavior parameterization is great because it enables you to separate the logic of iterating the collection to filter and the behavior to apply on each element of that collection.
Adding basic authentication with filters. Fine-grained authentication with action composition. Chapter 11 Modules and deployment Splitting your application into multiple sub-applications. Chapter 12 Testing your application Testing Play applications. About the book For a Java developer, the Play web application framework is a breath of fresh air.
About the reader The book requires a background in Java. No knowledge of Play or of Scala is assumed. About the author Nicolas Leroux is a core developer of the Play framework. Play for Java combo added to cart.
Your book will ship via to:. Commercial Address. Play for Java eBook added to cart. Don't refresh or navigate away from the page. Scala in Action. Nilanjan Raychaudhuri Foreword by Chad Fowler. Akka in Action. Play for Scala Covers Play 2. Functional Programming in Scala. Joshua Suereth and Matthew Farwell.
Benjamin J. Evans and Martijn Verburg Foreword by Dr. Heinz Kabutz. Scala in Depth. What if you want to alter the filter traversing to enhance performance? You now have to modify the implementation of all of your methods instead of a single one. This is expensive from an engineering effort perspective. You could combine the color and weight into one method called filter.
You could add a flag to differentiate between color and weight queries. But never do this! Third attempt: This solution is extremely bad. First, the client code looks terrible. What do true and false mean? What if the farmer asks you to filter with different attributes of an apple, for example, its size, its shape, its origin, and so on?
Furthermore, what if the farmer asks you for more complicated queries that combine attributes, such as green apples that are also heavy? This can be fine for certain well-defined problems. But in this case what you need is a better way to tell your filterApples method the selection criteria for apples. In the next section we describe how to make use of behavior parameterization to attain that flexibility.
Behavior parameterization You saw in the previous section that you need a better way than adding lots of parameters to cope with changing requirements.
One possible solution is to model your selection criteria: We call this a predicate that is, a function that returns a boolean. Figure 2. What you just did is related to the strategy design pattern, which lets you define a family of algorithms, encapsulate each algorithm called a strategy , and select an algorithm at run-time. But how can you make use of the different implementations of ApplePredicate?
You need your filterApples method to accept ApplePredicate objects to test a condition on an Apple. This is what behavior parameterization means: To achieve this in the running example, you add a parameter to the filterApples method to take an ApplePredicate object. This has a great software engineering benefit: Fourth attempt: You can now create different ApplePredicate objects and pass them to the filterApples method.
Free flexibility! For example, if the farmer asks you to find all red apples that are heavier than g, all you need to do is create a class that implements the ApplePredicate accordingly. Your code is now flexible enough for any change of requirements involving the attributes of Apple: Note that in the previous example, the only code that really matters is the implementation of the test method, as illustrated in figure 2.
Unfortunately, because the filterApples method can only take objects, you have to wrap that code inside an ApplePredicate object. Parameterizing the behavior of filterApples and passing different filter strategies Multiple behaviors, one parameter As we explained earlier, behavior parameterization is great because it enables you to separate the logic of iterating the collection to filter and the behavior to apply on each element of that collection. As a consequence, you can reuse the same method and give it different behaviors to achieve different things, as illustrated in figure 2.
This is why behavior parameterization is a useful concept you should have in your toolset for creating flexible APIs. Parameterizing the behavior of filterApples and passing different filter strategies To make sure you feel comfortable with the idea of behavior parameterization, have a go at Quiz 2.
Quiz 2. Write a flexible prettyPrintApple method Write a prettyPrintApple method that takes a List of Apples and that can be parameterized with multiple ways to generate a String output from an apple a bit like multiple customized toString methods.
For example, you could tell your pretty-PrintApple method to print only the weight of each apple. To help you get started, we provide a rough skeleton of the prettyPrintApple method: First, you need a way to represent a behavior that takes an Apple and returns a formatted String result.
You did something similar when you created an ApplePredicate interface: You can do this by adding a parameter to prettyPrintApple: You do this by instantiating implementations of AppleFormatter and giving them as arguments to prettyPrintApple: Or try this: Behavior parameterization: Java has a mechanism called anonymous classes, which let you declare and instantiate a class at the same time.
They enable you to improve your code one step further by making it a little more concise. Section 2. They allow you to declare and instantiate a class at the same time. In other words, they allow you to create ad hoc implementations. Fifth attempt: First, they tend to be very bulky because they take a lot of space, as shown in the highlighted code here using the same two examples used previously: Second, many programmers find them confusing to use.
For example, Quiz 2. Try your hand at it. Anonymous class puzzler What will the output be when this code is executed: The answer is 5, because this refers to the enclosing Runnable, not the enclosing class MeaningOfThis.
Good code should be easy to comprehend at a glance. In the context of passing a simple piece of code for example, a boolean expression representing a selection criterion , you still have to create an object and explicitly implement a method to define a new behavior for example, the method test for Predicate or the method handle for EventHandler.
Sixth attempt: Behavior parameterization vs. Seventh attempt: At the moment, the filterApples method works only for Apple. This pattern lets you encapsulate a behavior a piece of code and parameterize the behavior of methods by passing and using these behaviors you create for example, different predicates for an Apple.
We mentioned earlier that this approach is similar to the strategy design pattern. You may have already used this pattern in practice. Many methods in the Java API can be parameterized with different behaviors.
These methods are often used together with anonymous classes. We show three examples, which should solidify the idea of passing code for you: Sorting with a Comparator Sorting a collection is a recurring programming task.
For example, say your farmer wants you to sort the inventory of apples based on their weight. Sound familiar? Yes, you need a way to represent and use different sorting behaviors to easily adapt to changing requirements.
In Java 8, a List comes with a sort method you could also use Collections. The behavior of sort can be parameterized using a java. Comparator object, which has the following interface: For example, you can use it to sort the inventory by increasing weight using an anonymous class: The internal details of how to sort are abstracted away. With a lambda expression it would look like this: Executing a block of code with Runnable Threads are like a lightweight process: But how can you tell a thread what block of code to run?
Several threads may run different code. What you need is a way to represent a piece of code to be executed later. In Java, you can use the Runnable interface to represent a block of code to be executed; note that the code will return no result that is, void: GUI event handling A typical pattern in GUI programming is to perform an action in response to a certain event such as clicking or hovering over text.
For example, if the user clicks the Send button, you may wish to display a popup or perhaps log the action in a file. Again, you need a way to cope with changes; you should be able to perform any response. Anonymous classes helped a bit before Java 8 to get rid of the verbosity associated with declaring multiple concrete classes for an interface that are needed only once. It lets you define a block of code that represents a behavior and then pass it around. But you saw that using anonymous classes to represent different behaviors is unsatisfying: In this chapter, we teach you about a new feature in Java 8 that tackles this problem: For now you can think of lambda expressions as anonymous functions, basically methods without declared names, but which can also be passed as arguments to a method as you can with an anonymous class.
We show how to construct them, where to use them, and how you can make your code more concise by using them. We also explain some new goodies such as type inference and new important interfaces available in the Java 8 API.
Finally, we introduce method references, a useful new feature that goes hand in hand with lambda expressions. This chapter is organized in such a way as to teach you step by step how to write more concise and flexible code.
At the end of this chapter, we bring together all the concepts taught into a concrete example: Lambdas in a nutshell A lambda expression can be understood as a concise representation of an anonymous function that can be passed around: But like a method, a lambda has a list of parameters, a body, a return type, and a possible list of exceptions that can be thrown.
Why should you care about lambda expressions? You saw in the previous chapter that passing code is currently tedious and verbose in Java. Well, good news! Lambdas fix this problem: But you no longer have to write clumsy code using anonymous classes to benefit from behavior parameterization!
Lambda expressions will encourage you to adopt the style of behavior parameterization that we described in the previous chapter. The net result is that your code will be clearer and more flexible. For example, using a lambda expression you can create a custom Comparator object in a more concise way.
We explain in the next section exactly where and how you can use lambda expressions. The lambda we just showed you has three parts, as shown in figure 3. Figure 3. A lambda expression is composed of parameters, an arrow, and a body. To illustrate further, the following listing shows five examples of valid lambda expressions in Java 8. Listing 3. Working through Quiz 3.
Quiz 3. Lambda syntax Based on the syntax rules just shown, which of the following are not valid lambda expressions? Only 4 and 5 are invalid lambdas. This lambda has no parameters and returns void. This lambda has no parameters and returns a String as an expression. This lambda has no parameters and returns a String using an explicit return statement. To make this lambda valid, curly braces are required as follows: To make this lambda valid, you can remove the curly braces and semicolon as follows: Or if you prefer, you can use an explicit return statement as follows: Table 3.
You could also use another lambda with the filter method you implemented in the previous chapter: You can use a lambda expression in the context of a functional interface. Because Predicate specifies only one abstract method: You already know several other functional interfaces in the Java API such as Comparator and Runnable, which we explored in chapter 2: An interface is still a functional interface if it has many default methods as long as it specifies only one abstract method.
To check your understanding, Quiz 3. Functional interface Which of these interfaces are functional interfaces? Only Adder is a functional interface. What can you do with functional interfaces? Lambda expressions let you provide the implementation of the abstract method of a functional interface directly inline and treat the whole expression as an instance of a functional interface more technically speaking, an instance of a concrete implementation of the functional interface.
The following code is valid because Runnable is a functional interface defining only one abstract method, run: Function descriptor The signature of the abstract method of the functional interface essentially describes the signature of the lambda expression. We call this abstract method a function descriptor. For example, the Runnable interface can be viewed as the signature of a function that accepts nothing and returns nothing void because it has only one abstract method called run, which accepts nothing and returns nothing void.
Java reuses existing nominal types provided by functional interfaces and maps them into a form of function types behind the scenes. We use a special notation throughout the chapter to describe the signatures of lambdas and functional interfaces.
This is exactly what the Runnable interface represents. We detail how the compiler checks whether a lambda is valid in a given context in section 3. For now, it suffices to understand that a lambda expression can be assigned to a variable or passed to a method expecting a functional interface as argument, provided the lambda expression has the same signature as the abstract method of the functional interface. For instance, in our earlier example, you could pass a lambda directly to the process method as follows: This is exactly the signature of the run method defined in the Runnable interface.
But they chose this way because it fits naturally without increasing the complexity of the language. In addition, most Java programmers are already familiar with the idea of an interface with a single abstract method for example, with event handling.
Try Quiz 3. Where can you use lambdas? Which of the following are valid uses of lambda expressions? Only 1 and 2 are valid. Note that running this code will do nothing because the body of the lambda is empty! What about FunctionalInterface? This annotation is used to indicate that the interface is intended to be a functional interface. You can think of it like the Override notation to indicate that a method is overridden.
A recurrent pattern in resource processing for example, dealing with files or databases is to open a resource, do some processing on it, and then close the resource. The setup and cleanup phases are always similar and surround the important code doing the processing. This is called the execute around pattern, as illustrated in figure 3.
Step 1: Remember behavior parameterization This current code is limited. You can read only the first line of the file. Does this sound familiar? Yes, you need to parameterize the behavior of processFile.
You need a way to pass behavior to processFile so it can execute different behaviors using a BufferedReader. Passing behavior is exactly what lambdas are for. So what should the new processFile method look like if you wanted to read two lines at once? You basically need a lambda that takes a BufferedReader and returns a String. Step 2: Use a functional interface to pass behaviors We explained earlier that lambdas can be used only in the context of a functional interface.
Step 3: Execute a behavior! You now need only a way to execute the code represented by the lambda inside the body of processFile. Remember, lambda expressions let you provide the implementation of the abstract method of a functional interface directly inline, and they treat the whole expression as an instance of a functional interface. You can therefore call the method process on the resulting BufferedReaderProcessor object inside the processFile body to perform the processing: Step 4: Pass lambdas You can now reuse the processFile method and process files in different ways by passing different lambdas.
But you had to define your own interfaces. In the next section we explore new interfaces that were added to Java 8 that you can reuse to pass multiple different lambdas. Using functional interfaces As you learned in section 3.
Functional interfaces are useful because the signature of the abstract method can describe the signature of a lambda expression. The signature of the abstract method of a functional interface is called a function descriptor.
So in order to use different lambda expressions, you need a set of functional interfaces that can describe common function descriptors.
There are several functional interfaces already available in the Java API such as Comparable, Runnable, and Callable, which you saw in section 3. The Java library designers for Java 8 have helped you by introducing several new functional interfaces inside the java. We describe the interfaces Predicate, Consumer, and Function next, and a more complete list is available in table 3.
Predicate The java. You might want to use this interface when you need to represent a boolean expression that uses an object of type T.
For example, you can define a lambda that accepts String objects, as shown in the following listing. We come back to these in section 3. Consumer The java. You might use this interface when you need to access an object of type T and perform some operations on it. For example, you can use it to create a method forEach, which takes a list of Integers and applies an operation on each element of that list.
In the following listing you use this forEach method combined with a lambda to print all the elements of the list. Working with a Consumer 3. Function The java.
In the listing that follows we show how you can use it to create a method map to transform a list of Strings into a list of Integers containing the length of each String. Working with a Function Primitive specializations We described three functional interfaces that are generic: There are also functional interfaces that are specialized with certain types. To refresh a little: This is due to how generics are internally implemented.
This mechanism is called boxing. The opposite approach that is, converting a reference type into a corresponding primitive type is called unboxing. Java also has an autoboxing mechanism to facilitate the task for programmers: For example, this is why the following code is valid an int gets boxed to an Integer: Other languages such as Scala have only reference types. We revisit this issue in chapter Boxed values are essentially a wrapper around primitive types and are stored on the heap.
Therefore, boxed values use more memory and require additional memory lookups to fetch the wrapped primitive value. Java 8 brings a specialized version of the functional interfaces we described earlier in order to avoid autoboxing operations when the inputs or outputs are primitives. In general, the names of functional interfaces that have a specialization for the input type parameter are preceded by the appropriate primitive type, for example, DoublePredicate, IntConsumer, LongBinaryOperator, IntFunction, and so on.
The Function interface has also variants for the output type parameter: You can always make your own if needed! The left side of the table is a list representing the types of the arguments. In this case it represents a function with two arguments of respectively generic type T and U and that has a return type of R. To check your understanding so far, have a go at Quiz 3. As a further exercise, come up with valid lambda expressions that you can use with these functional interfaces.
To summarize the discussion about functional interfaces and lambdas, table 3. Note that none of the functional interfaces allow for a checked exception to be thrown. You have two options if you need a lambda expression to throw an exception: For example, in section 3. In this case you can explicitly catch the checked exception: Next, we explain some more advanced details: Type checking, type inference, and restrictions When we first mentioned lambda expressions, we said that they let you generate an instance of a functional interface.
In order to have a more formal understanding of lambda expressions, you should know what the actual type of a lambda is. Type checking The type of a lambda is deduced from the context in which the lambda is used. Note that if the lambda expression were throwing an exception, then the declared throws clause of the abstract method would also have to match. Same lambda, different functional interfaces Because of the idea of target typing, the same lambda expression can be associated with different functional interfaces if they have a compatible abstract method signature.
For example, both interfaces Callable and PrivilegedAction described earlier represent functions that accept nothing and return a generic type T. The following two assignments are therefore valid: In table 3. A given class instance expression can appear in two or more different contexts, and the appropriate type argument will be inferred as exemplified here: They can get their target type from an assignment context, method invocation context parameters and return , and a cast context.
To check your knowledge, try Quiz 3. How could you fix the problem? The context of the lambda expression is Object the target type. It can also be used to do something slightly different: Type inference You can simplify your code one step further. The Java compiler deduces what functional interface to associate with a lambda expression from its surrounding context the target type , meaning it can also deduce an appropriate signature for the lambda because the function descriptor is available through the target type.
The benefit is that the compiler has access to the types of the parameters of a lambda expression, and they can be omitted in the lambda syntax. In other words, the Java compiler infers the types of the parameters of a lambda as shown here: The benefits of code readability are more noticeable with lambda expressions that have several parameters.
For example, the following lambda captures the variable portNumber: Lambdas are allowed to capture that is, to reference in their bodies instance variables and static variables without restrictions. In other words, lambda expressions can capture local variables that are assigned to them only once.
Restrictions on local variables You may be asking yourself why local variables have these restrictions. Instance variables are stored on the heap, whereas local variables live on the stack. If a lambda could access the local variable directly and the lambda were used in a thread, then the thread using the lambda could try to access the variable after the thread that allocated the variable had deallocated it.
Hence, Java implements access to a free local variable as access to a copy of it rather than access to the original variable. This makes no difference if the local variable is assigned to only once—hence the restriction. Second, this restriction also discourages typical imperative programming patterns which, as we explain in later chapters, prevent easy parallelization that mutate an outer variable.
Closure You may have heard of the term closure and may be wondering whether lambdas meet the definition of a closure not to be confused with the Clojure programming language. To put it scientifically, a closure is an instance of a function that can reference nonlocal variables of that function with no restrictions.
For example, a closure could be passed as argument to another function.
It could also access and modify variables defined outside its scope. Now Java 8 lambdas and anonymous classes do something similar to closures: But they have a restriction: Those variables have to be implicitly final.
It helps to think that lambdas close over values rather than variables. Allowing capture of mutable local variables opens new thread-unsafe possibilities, which are undesirable instance variables are fine because they live on the heap, which is shared across threads. Think of them as shorthand versions of certain lambdas. Method references Method references let you reuse existing method definitions and pass them just like lambdas. In some cases they appear more readable and feel more natural than using lambda expressions.
In a nutshell Why should you care about method references? Method references can be seen as shorthand for lambdas calling only a specific method. Indeed, a method reference lets you create a lambda expression from an existing method implementation. But by referring to a method name explicitly, your code can gain better readability. How does it work? When you need a method reference, the target reference is placed before the delimiter:: For example, Apple:: Recipe for constructing method references There are three main kinds of method references: A method reference to a static method for example, the method parseInt of Integer, written Integer:: A method reference to an instance method of an arbitrary type for example, the method length of a String, written String:: A method reference to an instance method of an existing object for example, suppose you have a local variable expensiveTransaction that holds an object of type Transaction, which supports an instance method getValue; you can write expensiveTransaction:: The idea with the second kind of method references such as String:: The shorthand rules to refactor a lambda expression to an equivalent method reference follow simple recipes, shown in figure 3.
Recipes for constructing method references for three different types of lambda expressions Note that there are also special forms of method references for constructors, array constructors, and super-calls.
The sort method on a List expects a Comparator as parameter. You can define a lambda expression that leverages the method compareToIgnoreCase in the String class as follows note that compareToIgnoreCase is predefined in the String class: Using the recipes described previously, the example can also be written using a method reference as follows: To check your understanding of method references, have a go at Quiz 3.
Method references What are equivalent method references for the following lambda expressions? This lambda expression forwards its argument to the static method parseInt of Integer. This method takes a String to parse and returns an Integer.
As a result, the lambda can be rewritten using recipe from figure 3. This lambda uses its first argument to call the method contains on it. Because the first argument is of type List, you can use recipe from figure 3. But you can do something similar with constructors of a class. Constructor references You can create a reference to an existing constructor using its name and the keyword new as follows: It works similarly to a reference to a static method.
If you have a two-argument constructor, Apple String color, Integer weight , it fits the signature of the BiFunction interface, so you can do this, which is equivalent to The capability of referring to a constructor without instantiating it enables interesting applications. For example, you can use a Map to associate constructors with a string value. You can then create a method giveMeFruit that, given a String and an Integer, can create different types of fruits with different weights: Constructor references You saw how to transform zero-, one-, and two-argument constructors into constructor references.
What would you need to do in order to use a constructor reference for a three-argument constructor such as Color int, int, int? You saw that the syntax for a constructor reference is ClassName:: But you need a functional interface that will match the signature of that constructor reference. We put it all into practice in the next section! So the hard part is done!
But how can you pass an ordering strategy to the sort method? Well, the sort method has the following signature: This is how you can pass different strategies in Java: We say that the behavior of sort is parameterized: Your first solution looks like this: Use an anonymous class Rather than implementing Comparator for the purpose of instantiating it once, you saw that you could use an anonymous class to improve your solution: Use lambda expressions But your current solution is still verbose.
Java 8 introduces lambda expressions, which provide a lightweight syntax to achieve the same goal: You saw that a lambda expression can be used where a functional interface is expected. As a reminder, a functional interface is an interface defining only one abstract method. The signature of the abstract method called function descriptor can describe the signature of a lambda expression. Your new improved solution looks therefore as follows: So you can rewrite your solution like this: Comparator has a static helper method called comparing that takes a Function extracting a Comparable key and produces a Comparator object we explain why interfaces can have static methods in chapter 9.
It can be used as follows note that you now pass a lambda with only one argument: Use method references We explained that method references are syntactic sugar for lambda expressions that forwards their arguments. You can use a method reference to make your code slightly less verbose assuming a static import of java. Why is this better than code prior to Java 8? Useful methods to compose lambda expressions Several functional interfaces in the Java 8 API contain convenient methods.
Specifically, many functional interfaces such as Comparator, Function, and Predicate that are used to pass lambda expressions provide methods that allow composition. What does this mean?
In practice it means you can combine several simple lambda expressions to build more complicated ones. For example, you can combine two predicates into a larger predicate that performs an or operation between the two predicates. Moreover, you can also compose functions such that the result of one becomes the input of another function. After all, this goes against the definition of a functional interface!
We explain them in detail in chapter 9. For now, just trust us and read chapter 9 later when you want to find out more about default methods and what you can do with them. So you can simply modify the previous example to sort the apples by decreasing weight by reusing the initial Comparator: Chaining Comparators This is all nice, but what if you find two apples that have the same weight?
Which apple should have priority in the sorted list? You may want to provide a second Comparator to further refine the comparison. For example, after two apples are compared based on their weight, you may want to sort them by country of origin. The thenComparing method allows you to do just that. It takes a function as parameter just like the method comparing and provides a second Comparator if two objects are considered equal using the initial Comparator. The only thing in color in the book, however, is the cover.
It's MUCH easier to make sense of the book when different sections, modules, etc. If the book is in black and white, the sample chapters should black and white as well. Play for Java is written well and adequately illustrated, and, to me, its structure seems very good for helping newcomers get started with Play 2, plus the basics of web application development. The book also could help more experienced web app developers get up to speed with this powerful development platform.
Along the way, you are shown how to use Play 2 to create, expand and enhance a web-based warehouse management application that must meet specific needs. This can be good basic training for those preparing to do web app work in the real world. The book shows how to use three different IDEs with Play 2: Eclipse, IntelliJ, and my favorite, NetBeans.
You also can stick with the Play 2 console and a text editor, if you desire. But you do not have to know or learn Scala. However, you might enjoy studying it. This book meets my needs on two fronts: And I have been wanting to learn more tools that can help make the web app development process a bit less intimidating. I am glad to have Play 2 and Play for Java in my toolbox.
Could've been a bit less wordy and over-enthusiastic. First of all, I'd like to avoid commenting on Play itself, because I don't have enough prod.
This is a review on a book, not on the Play framework. It gives you fast and straight info right from the beginning. First, it quickly sums up how do Play-ish apps differ from common servlet-based apps, and then without losing any time it quickly shows you how to smash a working CRUD app. Brilliant, exactly what I needed!
After a rapid first part, things slow down a lot. Go get some coffee, you're gonna need it. The book will try to explain everything in the world - what is an enterprise application, what is HTTP, what is an MVC pattern and it tries to sell you some of its rather opinionated ways of building the domain model. Final part is pretty useful again, and it teaches you real-world practical things that you're gonna wanna need if you're ever going to deploy Play - security, async stuff promises , deployment, modularization.
Last chapter is on testing, which is a bit sad. But if you ask me, I think that testing should have been introduced way sooner and authors should have payed it a little bit more attention.
It simply feels like a second class citizen here. Major problem for this book is screwed-up formatting. Absolutely horrible mess - I suggest grabbing sources from github and save some nerve. Another thing that bothered me quite a bit was the "sales style" of the writing. Author is trying to convince you that you can use Play for everything you might ever need. I would actually appreciate a chapter on integrating with other popular technologies that Java programmers are likely to use.
One page! I simply think that books as this one should be focusing on explaining the technology, not on selling it. If you're Play fanboi, you're gonna be super happy with this book. If you're not or if you're just a curious stranger , try leveling up some marketing-sales-rubbish filtering skills.
It is actually a pretty good book once you do that. See all 17 reviews. Amazon Giveaway allows you to run promotional giveaways in order to create buzz, reward your audience, and attract new followers and customers.
Learn more about Amazon Giveaway. This item: Play for Java: Covers Play 2. Set up a giveaway. Customers who bought this item also bought. Akka in Action. Raymond Roestenburg. Spring Microservices in Action.