Lift Framework 3.2.0
The Lift Committers are pleased to announce the release of Lift 3.2.0 on January 27th, 2018. This release continues our new release cadence. This release has no code changes from the 3.2.0-RC1 release.
The Lift 3.2.0 release makes many notable improvements, but the biggest single theme throughout all the changes is that of improvement of Lift's failover and clustering story. This release, coupled with @joescii's lift-cluster module represents a giant leap forward in Lift's scalability story.
About Lift
The Lift Framework is a mature, advanced framework for the modern software engineer. There are Seven Things that set Lift apart from the other frameworks out there today: it's secure-by-default, developer-centric, scalable, capable of rich interactive behavior, modular, and designer-friendly. If you're new to Lift or interested in checking out what these things mean, we recommend checking out Simply Lift and The Lift Cookbook.
The Lift Mailing List is also a good resource for anyone to ask questions or just meet other Lift users. The Lift README is a good resource for figuring out how to use Lift in your project.
Changes
This release of Lift is composed of the following component releases:
Below is a list of changes since Lift 3.1 organized by the type of change and sorted by the PR number.
New Features
(#1770) Comet Rehydration
Since Comets were first introduced in Lift, it's been the case that if the Lift app server got restarted in the middle of a user's session and comets were involved then the user would need to refresh their entire page to reconnect to the server and have their comets start working again. By default, Lift is kind enough to do this page refresh for the users. However, this is a less than ideal experience for obvious reasons.
Now, after over a year's worth of work, @joescii has delivered Comet Rehydration to the Lift Framework.
Comet Rehydration is an optional feature that, when enabled, allows a user's browser to seamlessly reconnect to the comets on the server without needing to reload the page in many circumstances. In cases where significant changes have been delivered to an application, reloading the entire page may still be desirable. However, when deploying small bug fixes to users Comet Rehydration is a useful tool to do so without interrupting their experience in your application.
There are a couple of prerequisites to use rehydration effectively. Specifically,
- The
CometActor
s in your application should have a mechanism for reconstructing their intended state from cookies or by invoking methods on the client viaparitalUpdate
that will cause the client to transmit its current state to a new Comet. - If there is a significant amount of client-side JavaScript interacting with the comet, it would be advisable to version the RPC calls between the client and server so that the client knows when a full page reload is required. Such versions could be incremented to indicate changes in the protocol between the Comet and the browser or indicate that the browser needs to reload the page to fetch new JavaScript that the comet depends on (for example, a new version of jQuery).
We encourage all Lift developers to take a closer look at Comet Rehydration. We believe it will significantly improve the user experience for end-users of Lift applications when implemented with the prerequisites described above.
To start using rehydration, add the following to your Boot.boot
implementation:
LiftRules.enableCometRehydration()
(#1864) Add transform and flip methods to Box
Our Box
data type has learned a few new tricks.
The new transform
method allows callers to take a Box
and, via PartialFunction
, turn it into any other Box. If the PartialFunction
fails, the original Box is returned.
Some examples:
// Returns Full("alternative") because the partial function covers the case.
Full("error") transform {
case Full("error") => Full("alternative")
}
// Returns Full(1), this Full box unchanged, because the partial function doesn't cover the case.
Full(1) transform {
case Full(2) => Failure("error")
}
// Returns this Failure("another-error") unchanged because the partial function doesn't cover the case.
Failure("another-error") transform {
case Failure("error", Empty, Empty) => Full("alternative")
}
// Returns Full("alternative") for an Empty box since `partialFn` is defined for Empty
Empty transform {
case Empty => Full("alternative")
}
// Returns Empty because the partial function is not defined for Empty
Empty transform {
case Failure("error", Empty, Empty) => Full("alternative")
}
The new flip
method allows callers to take a Box
and, if it is an EmptyBox
(which is an Empty
or any sort of Failure
), flip it into a Full
with a specific type. If it is a Full
, an Empty
is returned.
Many thanks to @Bhashit for this contribution!
(#1866) New Optional Mongo Fields
@Bhashit made a number of additions to the optional mongo fields as a part of mongodb-record. Some fun additions that your code might benefit from include:
OptionalCaseClassField
OptionalJObjectField
OptionalUUIDRefField
OptionalObjectIdField
... and more! We've also deprecated some legacy field names and parameter names (e.g. rec
is now owner
) so you'll probably see some deprecation warnings crop up if you're using any of those.
(#1874) Support for HTTP patch method in RestHelper.
We now support the Patch
verb when using RestHelper
to build APIs. Also, as you would expect, there are XmlPatch
and JsonPatch
variants as there are with Get
, Post
, etc.
(#1893) ContainerVar serialization for anything Serializable
Lift has provided ContainerVar
for awhile for storing values in the underlying container session. (This is as opposed to the SessionVar
that stores things in Lift's session.) However, to use a ContainerVar
you need to provide some sort of ContainerSerializer
for the type that you're trying to serialize. Even though Lift has provided a handful of implementations for awhile, none of them would handle something as simple as Box[String]
.
@joescii was kind enough to add a ContainerSerializer
that works for anything extending Serializable
. This should give Lift developers using ContainerVar
a much more "batteries included" experience.
(#1906) Snippet Timers
Page loading slowly and you're not sure what code to blame? Want to just report all snippet timings to a metrics system for monitoring? Snippet Timers are for you! Snippet Timers enable global, per-request, or per-session timing of Snippet execution throughout your Lift application. By default, we package a LoggingSnippetTimer
that spits out these timings to the log system, but anything implementing the SnippetTimer
interface can be provided.
To get started you'll need to invoke LiftRules.installSnippetTimer
in Boot
to enable the feature. For example, to enable the logging snippet timer globally, just add the following line:
LiftRules.installSnippetTimer(LoggingSnippetTimer)
If you're only interested in logging in certain sessions or requests, you'll still need to invoke installSnippetTimer
at boot with the NoOpSnippetTimer
to enable the feature. Then, to enable logging snippet timing at some point in a request invoke:
LiftRules.snippetTimer.get.map(_.request(LoggingSnippetTimer))
The logging snippet timer will be enabled for the duration of the request. You can also do the same for a session.
LiftRules.snippetTimer.get.map(_.session(LoggingSnippetTimer))
Improvements
- During this release cycle, we also formalized our support policy.
- (#1865) Ensure the server/port combo of the original request is preserved. This resolves bug #1794, wherein our snapshotting of the underlying request object during some async operations was incomplete. This periodically caused misbehavior that broke the ability to retrieve the host and path of the request being handled by the async operations.
- (#1868) @Bhashit contributed some very nice documentation about Dependency Injection in Lift.
- (#1871) Bumped our logback version. This shouldn't affect applications using Lift directly, since we treat the logback dependency as something the application using Lift will provide. However, we do recommend that, if you haven't, you also upgrade to 1.2.3 or higher to resolve a security issue.
- (#1888, #1881) Various lift-json performance improvements.
- (#1889) Implementation of LiftRulesGuardedSetting. This type will eventually replace everything in
LiftRules
declared as avar
. The idea here is that we want to avoid folks from changing things inLiftRules
at runtime. We do that in a bit of an ad-hoc way now, in the sense that some settings will blow up in your face if you attempt to do so, but moving forward theLiftRulesGuardedSetting
is the way we're planning to standardize that behavior and make it more consistent. - (#1895) Make the servlet session ID configurable. Previously, Lift's servlet session identifier was hard-coded. This worked fine for a long time. However, recently we discovered that it doesn't play nice when used in conjunction with Jetty's Mongo persisted sessions plugin because of the
$
s in it. To resolve that, we've made it configurable throughLiftRules.servletSessionIdentifier
. - (#1907) Addition of
onShutdown
tobuildRoundtrip
. Previously, users ofbuildRoundtrip
had no way to get notified that the underlying comet actor had been shut down. This meant that they had no way to really know if they could free resources that might be associated with that connection in their application level code. To address this, we've added anonShutdown
argument to thebuildRoundtrip
function so developers can pass a handler when the underlying comet is actually shut down. - (#1908)
MetaProtoExtendedSession
cookies can now be restricted to the context path of your application. Or, really, any path you want - though we're skeptical restricting your cookies to/bananas
if your application isn't hosted at/bananas
would really be that useful. - (#1909) Move template cache defaulting to
LiftRules
. This change addresses some confusing behavior in how Lift creates the default template cache. Previously, if you were running in production mode and thetemplateCache
was set toEmpty
we would auto-magically create anInMemoryCache
and use that instead. Due to the way this was written, this effectively meant that turning off template caching in production was impossible. With this change we moved where the default gets calculated so it's actually possible to turn off the template cache in production mode if you would like to do that. - (#1910) Clarification of LAPinger documentation.
- (#1918) Logging improvements for various exceptions. @andreak had located a few spots that weren't properly printing the exception stack trace when exceptions were hit, and delivered a Quick Fix™ to that.
- (#1921) @joescii shipped some minor tweaks to support session serialization with lift-cluster.
Bug Fixes
- (#1903) Provide context path to session reload handler. There was a subtle change in behavior in Lift 3.0 that caused bad things to happen when a Lift application was deployed under a context path in an application server (so, somewhere other than
/
) and that Lift application detected that the comet session had disappeared. In previous versions of Lift this would just reload the page. In Lift 3.0, we changed that behavior to take you to the root of your application. However,/
is not always the root of the application. Now, we'll properly consider the context path when detecting what URL to send you to. This is, as always, customizable withLiftRules.noCometSessionCmd
. - (#1911) Consider
LiftRules.cometCreation
when building comets. ThisLiftRule
became ignored by accident during the great comet upgrade of the 3.0 release. We've added the line back that was missing, and plan to back-port this fix to the 3.0 and the 3.1 series. - (#1923) Validate that no-arg comet constructor doesn't exist before attempting backup constructor. This fixes a long-standing bug in Lift regarding Comet instantiation. Previously, if you had a comet that threw an exception in it's no-argument constructor we would unintentionally swallow that exception and attempt the backup, comet info constructor. Then we'd complain to the developer that the comet info constructor didn't exist. We're now a bit smarter about how we handle those errors, and check the Exception that the no-arg instantiation returns. If it's anything other than a "no such method" Exception, we'll re-throw immediately instead of trying the comet info constructor.
- (#1924) Correctly override list item markup in lift-markdown. This resolves an issue (#1810) where
InlineParser
would ignore custom decorators for list items. Thanks @ricsirigu for the fix! - (#1928) Add a configurable
maxIdLength
to mongo-record's string primary keys. Previously this was hard-coded to 12. - (#1930) Added a null guard to address tests that touch stateful features. Resoles an issue (#1929) where an alarming, but harmless
NullPointerException
would occur in tests that destroy aLiftSession
. Hat tip to @joescii for spotting it and delivering the fix.
Final Notes
This release represents six months of hard work on behalf of the contributors. Most of the contributions made to Lift are made on the contributor's own time without any kind of payment. If you use Lift, please take the time to thank a contributor the next time you see them. They'll appreciate knowing their work is valued.
Now, we look forward to Lift 3.3.0 around July. Lift 3.3.0-M1 is currently scheduled to be released February 1, 2018 — only a few days from now. If you're interested in our progress, you can follow along from the Milestones page.
As always, please reach out to us with any questions or concerns on the mailing list. We hope you enjoy Lift 3.2.0!