Posted
almost 13 years
ago
The Lift team is pleased to announce Lift 2.2-RC1. In the month since the 2.2-M1 release, the team has closed 53 tickets and made significant improvements to Lift based on community feedback. Lift is an elegant, expressive framework that
... [More]
allows any size team build and maintain secure, highly interactive, scalable web applications quickly and efficiently. Lift is built on Scala and compiles to JVM byte-code. Lift applications deploy as WAR files on popular application servers and web containers including Jetty, Glassfish and Tomcat. Lift applications can be monitored and managed with the same proven infrastructure used to manage and monitor any Java web application. Lift is open source licensed under an Apache 2.0 license. Lift features include: Community... the Lift community is 2,400 members strong, super-active and always there to help with questions Best Comet (server-push) support that allows the creation of dynamic application such as Novell Vibe Super simple Ajax for creating highly interactive web applications without worrying about HTTP plumbing Secure by default... Lift apps are resistant to the OWASP top 10 vulnerabilities including XSS, XSRF, and parameter tampering Concise and Maintainable... Lift apps typically contain fewer lines of code than corresponding Rails apps, yet are type safe so that many errors are flagged by the compiler Scalable... Lift apps scale to millions of users across many servers, yet are highly efficient for single-box implementations Compatible... Lift apps can take advantage of any Java library as well as the growing collection of Scala libraries Lift 2.2-RC1 improvements include: HTML5 Support: Lift supports parsing HTML5 input files and rendering HTML5 to the browser in addition to Lift’s XHTML support Wiring: Spreadsheets meet web application yielding an automatic mechanism for updating dependent elements on a page, making it even easier to build dynamic apps with Lift Wizard and Screen Improvements: Build complex screens more easily with new helper methods for creating form elements and improved life-cycle callbacks CSS Selector Transforms Improvements: including appending attributes, multiple selectors applying to a single element, and element lifting Support for migratory sessions: ContainerVars provide type-safe, guaranteed serializable session variables that can migrate across application servers in a cluster Improved i18n: including per-page localization strings and localization strings and HTML stored in templates rather than Java resource files which makes editing much easier Security Improvements: including creation of new sessions on login MongoDB Improvements: performance improvements as well as new features Support for Scala 2.8.1 as well as 2.8.0 and 2.7.7 ProtoUser support for Record: Lift’s ProtoUser and CRUDify can be used on Record-based persistence classes as well as Mapper-based persistence classes Squeryl integration improvements: Lift is updated to use the latest version of Squeryl Lift-powered sites include: Foursquare: the multi-million user location based service that services millions of check-ins a day on their Lift-powered system Novell Vibe: enterprise collaboration software platform based on Google Wave Innovation Games: The fun way to do serious business — seriously Xerox/XMPie: the leading provider of software for cross-media, variable data one-to-one marketing Exchango: The easy and convenient way to give and get free stuff. Snapsort: Compare and decide on cameras No Fouls: Find pickup basketball games Please join the Lift community and help use grow Lift. And a super-big thanks to the 30+ Lift committers who have grown the Lift community and code-base to what it is today... and what it will be in the future! We expect to ship Lift 2.2 release on January 5th. Please test the 2.2-RC* series and help us identify bugs so we can make Lift 2.2 awesome!
Permalink
[Less]
|
Posted
almost 13 years
ago
Enjoy: Screencast video
The HTML Template:
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml">
Home
Welcome to your project!
This is my chat app, baby
... [More]
Chat Line 1
Chat Line 2
Chat line 3
Welcome to your Lift app at Time goes here
The Chat.scala file:
package code
package comet
import net.liftweb._
import http._
import actor._
import util._
import Helpers._
object ChatServer extends LiftActor with ListenerManager {
private var msgs = Vector("Welcome")
def createUpdate = msgs
override def lowPriority = {
case s: String => msgs :+= s; updateListeners()
}
}
class Chat extends CometActor with CometListener {
private var msgs: Vector[String] = Vector()
def registerWith = ChatServer
override def lowPriority = {
case v: Vector[String] => msgs = v; reRender()
}
def render = "li *" #> msgs & ClearClearable
}
And the ChatIn.scala snippet:
package code
package snippet
import net.liftweb._
import http._
import js._
import JsCmds._
import JE._
import comet.ChatServer
object ChatIn {
def render = SHtml.onSubmit(s => {
ChatServer ! s
SetValById("chat_in", "")
})
}
Permalink
[Less]
|
Posted
almost 13 years
ago
Lift and Dependency Injection Dependency injection is an important topic in the Java world. It's important because Java lacks certain basic features (e.g., functions) that tend to bind abstract interfaces to concrete implementations. Basically
... [More]
, it's so much easier to do MyInterface thing = new MyInterfaceImpl(), so most developers do just that. Scala's cake pattern goes a long way to help developers compose complex behaviors by combining Scala traits. Jonas Bonér wrote an excellent piece on Dependency Injection. The cake pattern only goes half way to giving a Java developer complete dependency injection functionality. The cake pattern allows you to compose the complex classes out of Scala traits, but the cake pattern is less helpful in terms of allowing you to make dynamic choices about which combination of cake to vend in a given situation. Lift provides a extra features that complete the dependency injection puzzle. Lift Libraries and Injector Lift is both a web framework and a set of Scala libraries. Lift's common, actor, json, and util packages provide common libraries for Scala developers to build their application. Lift's libraries are well tested, widely used, well supported, and released on a well defined schedule (montly milestones, quarterly releases). Lift's Injector trait forms the basis of dependency injection:
/** * A trait that does basic dependency injection. */trait Injector { implicit def inject[T](implicit man: Manifest[T]): Box[T]}
You can use this trait as follows:
object MyInjector extends Injector {...}val myThing: Box[Thing] = MyInjector.inject
The reason that the instance of MyThing is in a Box is because we're not guaranteed that MyInjector knows how to create an instance of Thing. Lift provides an implementation of Injector called SimpleInjector that allows you to register (and re-register) functions for injection:
object MyInjector extends SimpleInjectordef buildOne(): Thing = if (testMode) new Thing with TestThingy {} else new Thing with RuntimeThingy {}MyInjector.registerInjection(buildOne _) // register the function that builds Thing val myThing: Box[Thing] = MyInjector.inject
This isn't bad... it allows us to define a function that makes the injection-time decision, and we can change the function out during runtime (or test-time.) However, there are two problems: getting Boxes for each injection is less than optimal. Further, globally scoped functions mean you have to put a whole bunch of logic (test vs. production vs. xxx) into the function. SimpleInjector has lots of ways to help out.
object MyInjector extends SimpleInjector { val thing = new Inject(buildOne _) {} // define a thing, has to be a val so it's eagerly evaluated and registered}def buildOne(): Thing = if (testMode) new Thing with TestThingy {} else new Thing with RuntimeThingy {} val myThingBox: Box[Thing] = MyInjector.injectval myThing = MyInjector.thing.vend // vend an instance of Thing
Inject has a futher trick up its sleave... with Inject, you can scope the function... this is helpful for testing and if you need to change behavior for a particular call scope:
MyInjector.thing.doWith(new Thing with SpecialThing {}) { val t = MyInjector.thing.vend // an instance of SpecialThing val bt: Box[Thing] = MyInjector.inject // Full(SpecialThing) }MyInjector.thing.default.set(() => new Thing with YetAnotherThing {}) // set the global scope
Within the scope of the doWith call, MyInjector.thing will vend instances of SpecialThing. This is useful for testing as well as changing behavior within the scope of the call or globally. This gives us much of the functionality we get with dependency injection packages for Java. But within Lift WebKit, it gets better. Lift WebKit and enhanced injection scoping Lift's WebKit offers broad ranging tools for handling HTTP requests as well as HTML manipulation. Lift WebKit's Factory extends SimpleInjector, but adds the ability to scope the function based on current HTTP request or the current container session:
object MyInjector extends Factory { val thing = new FactoryMaker(buildOne _) {} // define a thing, has to be a val so it's eagerly evaluated and registered}
MyInjector.thing.session.set(new Thing with ThingForSession {}) // set the instance that will be vended for the duration of the session
MyInjector.thing.request.set(new Thing with ThingForRequest {}) // set the instance that will be vended for the duration of the request
WebKit's LiftRules is a Factory and many of the properties that LiftRules contains are FactoryMakers. This means that you can change behavior during call scope (useful for testing):
LiftRules.convertToEntity.doWith(true) { ... test that we convert certain characters to entities}
Or based on the current request (for example you can change the rules for calculating the docType during the current request):
if (isMobileReqest) LiftRules.docType.request.set((r: Req) => Full(DocType.xhtmlMobile))
Or based on the current session (for example, changing maxConcurrentRequests based on some rules when a session is created):
if (browserIsSomethingElse) LiftRules.maxConcurrentRequests.session.set((r: Req) => 32) // for this session, we allow 32 concurrent requests
Conclusion Lift's SimpleInjector/Factory facilities provide a powerful and flexible mechanism for vending instances based on a global function, call stack scoping, request and session scoping and provides more flexible features than most Java-based dependency injection frameworks without resorting to XML for configuration or byte-code rewriting magic.
Permalink
[Less]
|
Posted
almost 13 years
ago
The Lift team and the Lift community are proud to announce the availability of Lift 2.2-M1
Lift is an elegant, expressive web framework that allows any size team build and maintain secure, highly interactive, scalable web
... [More]
applicationsquickly and efficiently. Lift is built on theScala and compiles to JVM byte-code. Lift applications deploy as WAR files on popular application servers and web cotainers including Jetty, Glassfish and Tomcat. Lift applications can be monitored and managed with the same proven infrastructure used to manage and monitor any Java web application. Lift is open source licensed under an Apache 2.0 license.
Lift features include:
Community... the Lift community is 2,300 members strong, super-active and always there to help with questions
Best Comet (server-push) support that allows the creation of dynamic application such as Novell Pulse
Super simple Ajax for creating highly interactive web applications without worrying about HTTP plumbing
Secure by default... Lift apps are resistant to the OWASP top 10 vulnerabilities including XSS, XSRF, and parameter tampering
Concise and Maintainable... Lift apps typically contain fewer lines of code than corresponding Rails apps, yet are type safe so that many errors are flagged by the compiler
Scalable... Lift apps scale to millions of users across many servers, yet are highly efficient for single-box implementations
Compatible... Lift apps can take advantage of any Java library as well as the growing collection of Scala libraries
Lift 2.2-M1's new features include:
Designer-friendly templates
CSS Selector Transforms
Stateless rendering including the HTML pipeline
Separation of CRUDify and ProtoUser from Mapper so they can be used with any backing store
Support for MVC-style development
Lift-powered sites include:
Foursquare -- the multi-million user location based service that will soon surpass 1M+ checkins a day on their Lift-powered system
Novell Pulse -- enterprise collaboration software platform based on Google Wave
Innovation Games: The fun way to do serious business -- seriously
Xerox/XMPie -- the leading provider of software for cross-media, variable data one-to-one marketing
Snapsort -- Compare and decide on cameras
No Fouls -- Find pickup basketball games
Lift 2.2-M1's exhaustive change list:
See the Ticket Tracker for the full reference.
New Features:
Add full BSON support to lift-mongodb Issue: 568.
Support for Sybase ASE using JTDS JDBC driver for Lift 3.0 Issue: 620.
Add binding support to the embed tag Issue: 639.
Add XML matchers to TestResponse Issue: 640.
Add support for prefixed attributes to BindParam subclasses Issue: 647.
Support for multiple validation Issue: 664.
Update CRUDify and ProtoUser to be persistance agnostic Issue: 684.
Implement FuncBoxBindParam Issue: 690.
Review designer friendly and add bind by css selectors Issue: 695.
Add some simple MVC style helpers for Lift Issue: 703.
Fixed Bugs:
Save an edited db-record which has a custom primary key -> Trying to open an empty Box Issue: 552.
Browser Detection (for max simultaneous connections) Doesn't Handle Firefox 4 Issue: 646.
lift-mapper should not depend on H2 and Derby Issue: 648.
Chrome 7 barf on comet tags that include lift:when = ... Issue: 698.
Bug at OpenID's rawJUL2List Issue: 556.
Inconsistent rendering of notices in AJAX responses Issue: 589.
Add "body: NodeSeq" Parameter to Shtml.submit() Issue: 613.
All external links in SiteMap treated as same link Issue: 615.
CouchDB : StackOverflow in Issue: 649.
Values can't be extracted from JSON with Map[String, List[String]] Issue: 654.
SHtml.ajaxRadio() Issue: 657.
Change Loc.snippet to use currentValue Issue: 661.
JsonParser.Buffer incorrectly handles input Reader Issue: 671.
XMLApiHelpers has ambiguous implicit conversions from NodeSeq and Seq[Node] Issue: 678.
ImageResizer.getOrientation should handle Exceptions Issue: 682.
n.l.r.f.EmailField.emailPatter n is broken Issue: 691.
Performance degradation in JSON compact rendering Issue: 696.
\uffff is a valid char in JSON string literal Issue: 697.
POM Housekeeping Issue: 447.
Option bind, being able to remove large portion of html if none Issue: 629.
Misleading error message in net.liftweb.util.Props Issue: 645.
Other Changes:
OpenID error messages should be customizable Issue: 561.
Update vscaladoc packageLink defs with baseUri for external dependencies Issue: 579.
Update slf4j and logback revisions and make using logging backend more flexible Issue: 516.
Change log4j version in lift 2.1 from 1.2.14 to 1.2.16 Issue: 644.
ImageResizer support for gif, bmp detection and jpeg quality Issue: 662.
Consider ways of making work with custom locs Issue: 663.
Features that require session state should warn when container sessions are disabled Issue: 665.
LiftRules.uriNotFound does not support NotFoundAsTemplate when handling 404s for urls served by ResourceServer Issue: 668.
Add a class LocParam to SiteMap Issue: 679.
Make LiftScreen and Wizard stateful snippets Issue: 699.
Enrich archetypes to take CLI parameter logBackend Issue: 700.
Skip the tag at the beginning of an HTML file Issue: 702.
Please join the Lift community and help use grow Lift. And a super-big thanks to the 30+ Lift committers who have grown the Lift community and code-base to what it is today... and what it will be in the future!
Thank you, have fun!
The Lift Framework Team
Permalink
[Less]
|
Posted
almost 13 years
ago
Lift, State, and Scaling Lift is a web framework built on the Scala programming language. Lift takes advantage of many of Scala's features that allows developers to very concisely code secure, scalable, highly interactive web applications. Lift
... [More]
provides a full set of layered abstractions on top of HTTP and HTML from "close to the metal" REST abstractions up to transportation agnostic server push (Comet) support. Scala compiles to JVM byte-code and is compatible with Java libraries and the Java object model. Lift applications are typically deployed as WAR files in J/EE web containers... Lift apps run in Tomcat, Jetty, Glassfish, etc. just like any other J/EE web applications. Lift apps can generally be monitored and managed just like any Java web app. Web Applications, Sessions, and State All web applications are stateful in one way or another. Even a "static" web site is made up of the files that are served... the application's state is defined in those files. The site content may be served out of a database, but the content served does not depend on identity of the user or anything about the HTTP request except the contents of the HTTP request. These contents can include the URI, parameters, and headers. The complete value of the response can be calculated from the request without referencing any resources except the content resources. For the purpose of this discussion, I will refer to these as session-less requests. News sites like the UK Guardian, MSNBC, and others are prototypical examples of this kind of site. Sessions Some applications are customized on a user-by-user basis. These applications include the likes of Foursquare and others where many HTTP requests make up a "session" in which the results of previous HTTP requests change the behavior of future HTTP requests. Put in concrete terms, a user can log into a site and for some duration, the responses are specific to that user. There are many mechanisms for managing sessions, but the most common and secure method is creating a cryptographically unique token (a session id), and putting that token in the Set-Cookie response header such that the browser will present that Cookie in subsequent HTTP requests for a certain period of time. The server-side state is referenced by the Cookie and the state is made available to the web application during the scope of servicing the request and any mutations the web app makes to session state during the request are kept on the server and are available to the application in subsequent requests. Another available technique for managing state is to serialize application state in the Cookie and deliver it to the browser such that the server is not responsible for managing state across requests. As we've recently discovered, this is a tremendously insecure way to manage application state. Further, for any moderately complex application, the amount of data the needs to be transferred as part of each request and response is huge. Migratory Sessions Many web application managers allow for server-managed sessions to migrate across a cluster of web application servers. In some environments such as Ruby on Rails, this is a hard requirement because only one request at a time can be served per process, thus for any moderate traffic site, there must be multiple processes serving pages. There are many strategies for migrating state across processes: storing state on disk, in memcached, in a database (relational or NoSQL), or having some proprietary cluster communications protocol. In any of these scenarios sessions can migrate across the grid of processes serving requests for a given web application. Web applications that support migratory state are often referred to as "stateless" because the session state does not reside in the same process as the web application. Session Affinity Some applications require that all requests related to a particular session are routed to the same process and that process keeps session-related content in local memory. In a cluster, there are multiple mechanisms for achieving session affinity... the two most popular being HAProxy and Nginx. Availability, Scalability, Security, Performance, and User Experience There are many vectors on which to measure the over-quality of a web applications. Let's take a quick peek at each one. Availability Availability of an application is the amount of time it gives a meaningful response to a request. Highly available applications generally span multiple pieces of hardware and often multiple data centers. Highly available applications are also typically available during upgrades of part of the system that makes up the application. Highly available applications have very few single points of failure and those single points of failure are usually deployed on very reliable hardware. Scalability A scalable application can, within certain bounds, respond with similar performance to increased load by adding hardware to process more load. No system is infinitely or linearly scalable. However, many systems have grossly disproportionate load demands such that, for example, you can add a lot of web application front-ends to a Rails application before there's enough load on the back-end RDBMS such that scaling is impaired. Security The Internet is a dangerous place and no request that is received from the Internet can be trusted. Applications, frameworks, systems and everything else must be designed to be secure and resist attacks. The most common attacks on web application are listed in the OWASP Top Ten. Performance Web application performance can be measured on two vectors: response time to a request and system resources required to service the request. These two vectors are inter-dependent User Experience The user experience of a web app is an important measure of its quality. User experience can be measured on many different vectors including perceived responsiveness, visual design, interactivity, lack of "hicups", etc. Ultimately, because we're building applications for users, the user experience is very important. Lift's trade-offs Given the number and complexity related to the quality of a web application, there are a lot of trade-offs, implicit and explicit, to building a framework that allows developers and business people to deliver a great user experience. Let's talk for a minute about what Lift is and what it isn't. Lift is a web framework. It provides a set of abstractions over HTTP and HTML such that developers can write excellent web applications. Lift is persistence agnostic. You can use Lift with relational databases, file systems, NoSQL data stores, mule carts, etc. As long as you can materialize an object into the JVM where Lift is running, Lift can make use of that object. Lift sits on top of the JVM Lift applications execute in the Java Virtual Machine. The JVM is a very high performance computing system. There are raging debates as to the relative performance of JVM code and native machine code. No matter which benchmarks you look at, the JVM is a very fast performer. Lift apps take advantage of the JVM's performance characteristics. Moderately complex Lift apps that access the database can serve 1,000+ requests per second on quad-core Intel hardware. Even very complex Lift apps that make many back-end calls per request can serve hundreds of requests per second on EC2 large instances. Lift as proxy Many web applications, typically REST applications, provide a very thin layer on top of a backing data store. The web application serves a few basic functions to broker between the HTTP request and the backing store. These functions include: request and parameter validation, authentication, parameter unpacking, back-end service request, and translation of response data to wire format (typically XML or JSON). Lift can service these kinds of requests within the scope of a session or without any session at all, depending on application design. For more information on Lift's REST features, see Lift RestHelper. When running these kinds of services, Lift apps can be treated without regard for session affinity. Lift as HTML generator Lift has a power and secure templating mechanism. All Lift templates are expressed as valid XML and during the rendering process, Lift keeps the page in XML format. Pages rendered via Lift's templating mechanism are generally resistant to cross site scripting attacks and other attacks that insert malicious content in rendered pages. Lift's templating mechanism is designer friendly yet support complex and powerful substitution rules. Further, the rendered page can be evaluated and transformed during the final rendering phase to ensure that all script tags are at the bottom of the page, all CSS tags are at the top, etc. Lift's templating mechanism can be used to serve sessionless requests or serve requests within the context of a session. Further, pages can be marked as not requiring a session, yet will make session state available is the request was made in the context of a container session. Lift page rendering can even be done in parallel such that if there are long off-process components on the page (e.g., advertising servers), those components can be Sessionless Lift, forms and Ajax Lift applications can process HTML forms and process Ajax requests even if there's no session associated with the request. Such forms and Ajax requests have to have stable field names and stable URLs, but this is the same requirement as most web frameworks including Struts, Rails, and Django impose on their applications. In such a mode, Lift apps have the similar characteristics to web apps written on tops of Struts, Play, JSF and other popular Java web frameworks. Lift as Secure, Interactive App Platform Lift features require session affinity: GUID to function mapping, type-safe SessionVars and Comet. Applications that take advantage of these features need to have requests associated with the JVM that stores the session. I'll discuss the reason for this limitation, the down-side to the limitation, the downside to migratory session, and the benefits of these features. Application servers that support migratory sessions (sessions that are available to application servers running in multiple address spaces/processes) require a mechanism for transferring the state information between processes. This is typically (with the exception of Terracotta) done by serializing the stored data. Serialization is the process of converting rich data structures into a stream of bytes. Some of Scala's constructs are hard or impossible to serialize. For example, local variables that are mutated within a closure are promoted from stack variables to heap variables. When those variables are serialized at different times, the application winds up with two references even though the references are logically the same. Lift makes use of many of these constructs (I'll explain why next) and Lift's use of these constructs makes session serialization and migration impossible. It also means that Lift's type-safe SessionVars are not guaranteed to be serialized. One of the key Lift constructs is to map a cryptographically unique identifier in the browser to a function on the server. Lift uses Scala functions which close over scope, including all of the variables referenced by the function. This means that it's not necessary to expose primary keys to the client when editing a record in the database because the primary key of the record or the record itself is known to the function on the server. This guards against OWASP Vulnerability A4, Insecure Object References as well as Replay Attacks. From the developer's standpoint, writing Lift applications is like writing a VisualBasic application... the developer associates the user action with a function. Lift supplies the plumbing to bridge between the two. Lift's GUID to function mapping extends to Lift's Ajax support. Associating a button, checkbox, or other HTML element with an Ajax call is literally a single line:
SHtml.ajaxButton(PressMe, () => Alert("You pressed a button at "+Helpers.currentTimeFormatted)
Lift's Ajax support is simple, maintainable, and secure. There's no need to build and maintain routing. Lift has the most advanced server-push/Comet support of any web framework or any other system currently available. Lift's comet support relies on session affinity. Lift's comet support associates an Actor with a section of screen real estate. A single browser window may have many pieces of screen real estate associated with many of Lift's CometActors. When state changes in the Actor, the state change is pushed to the browser. Lift takes care of multiplexing a single HTTP connection to handle all the comet items on a given page, the versioning of the change deltas (if the HTTP connection is dropped while 3 changes become available, all 3 of those changes are pushed when the next HTTP request is made.) Further, Lift's comet support will work the same way once web sockets are available to the client and server... there will be no application code changes necessary for web sockets support. Lift's comet support requires that the connect is made from the browser back to the same JVM in which the CometActors are resident... the same JVM where the session is located. The downside to Lift's session affinity requirement mainly falls on the operations team. They must use a session aware load balancer or other mechanism to route incoming requests to the server that the session is associated with. This is easily accomplished with HAProxy and Nginx. Further, if the server running a given session goes down, the information associated with that session is lost (note that any information distributed off-session [into a database, into a cluster of Akka actors, etc.] is preserved.) But, Lift has extended session facilities that support re-creation of session information in the event of session lost. Lift also has heart-beat functionality so that sessions are kept alive as long as a browser page is open to the application, so user inactivity will not result in session timeouts. Compared to the operational cost of a session aware load balancer, there are many costs associated with migratory sessions. First, there must be a persistence mechanism for those sessions. Memcached is an unreliable mechanism as memcached instances have no more stability than the JVM which hosts the application and being a cache, some sessions may get expired. Putting session data in backing store such as MySQL or Cassandra increases the latency of requests. Further, the costs of serializing state, transmitting the state across the network, storing it, retrieving it, transmitting it across the network, and deserializing it all costs a lot of cycles and bandwidth. When your Lift application scales beyond a single server, beyond 100 requests per second, the costs of migrating state on every request becomes a significant operational issue. Session serialization can cause session information loss in the case of multiple requests being executed in multiple processes. It's common to have multiple tabs/windows open to the same application. If session data is serialized as a blob and two different requests from the same server are being executed at the same time, the last request to write session data into the store will over-write the prior session data. This is a concurrency problem and can lead to hard to debug issues in production because reproducing this kind of problem is non-trivial and this kind of problem is not expected by developers. The third issue with migratory sessions and session serialization is that the inability to store complex information in the session (e.g., a function that closes over scope) means that the developer has to write imperative code to serialize session state to implement complex user interactions like multi-screen wizards (which is a 400 line implementation in Lift). These complex, hand written serializations are error prone, can introduce security problems and are non-trivial to maintain. The operational costs of supporting session affinity are not materially different from the operational costs of providing backing store for migratory sessions. On the other hand, there are many significant downsides to migratory sessions. Let's explore the advantages of Lift's design. Lift's use of GUIDs associated with functions on the server: Increase the security of the application by guarding against cross site request forgeries, replay attacks, and insecure object referencesDecrease application development and maintenance time and costsIncrease application interactivity, thus a much better user experienceIncrease in application richness because of simpler Ajax, multi-page Wizards, and Comet Improved application performance because fewer cycles are spent serializing and transmitting session informationNo difference in scalability... just add more servers to the front end to scale the front end of your application The positive attributes of Lift's design decisions are evident at Foursquare which handles thousands of requests per second all served by Lift. There are very few sites that have more traffic than Foursquare. They have scaled their web front end successfully and securely with Lift. Other high volume sites including Novell are successfully scaling with Lift. If you are scaling your site, there are also commercial Lift Cloud manager tools that can help manage clusters of Lift's session requirements. Conclusion Lift provides a lot of choices for developing and deploying complex web applications. Lift can operate in a web container like any other Java web framework. If you choose to use certain Lift features and you are deploying across multiple servers, you need to have a session aware load balancer. Even when using Lift's session-affinity dependent features, Lift applications have higher performance, identical availability, identical scalability, better security, and better user experience than applications written with web frameworks such as Ruby on Rails, Struts, and GWT.
Permalink
[Less]
|
Posted
almost 13 years
ago
Lift 2.2-M1 introduced a new mechanism for transforming XHTML: CSS Selector Transforms (CssBindFunc). The new mechanism provides a subset of CSS selectors that can be used to transform NodeSeq => NodeSeq. Examples of this feature include:
... [More]
"#name" #> userName // replace the element with the id name with the variable userName "#chat_lines *" #> listOfChats // replace the content of chat_lines with each element of listOfChats ".pretty *" #> Unicorn // each element with CSS class pretty, replace content with Unicorn "dog=cat [href]" #> "http://dogscape.com" // set the href attribute of all elements with the dog attribute set to cat "#name" #> userName & "#age" #> userAge // set name to userName and age to userAge CSS Selector Transforms extends NodeSeq => NodeSeq... they are quite literally functions and can be passes as a parameter to anything expecting NodeSeq => NodeSeq or returned as a result for any method that returns NodeSeq => NodeSeq. Let's look at each of the pieces to see how they work. First, you must import net.liftweb.util._ and import Helpers._ These packages include the classes and the implicit conversions that make the CSS Selector tranforms work. The transform is defined by: String representing selector #> transform value. The selector is a String constant which implements the following subset of CSS Selectors: #id -- selects the element with the specified id .class -- selects all elements have a class attribute where one of the space-separated values equals class attr_name=attr_value -- selects all elements where the given attribute equals the given value You can replacement rules after the selector: none (e.g., "#id") replaces all matching elements with the values "#name" #> "David" // -> David * (e.g., "#id *") replaces the content children of the matching elements with the values "#name" #> "David" // -> [attr] (e.g., "#id [href]") replaces the matching attribute's value with the values (note in the case of the class attribute, the values are appended to the element's class attributes). "#link [href]" #> "http://dogscape.com" // Dogscape -> Dogscape The right hand side of the CSS Selector Transform can be one of the following: String -- a String constant, for example: "#name *" #> "David" // -> David "#name *" #> getUserNameAsString NodeSeq -- a NodeSeq constant, for example: "#name *" #> David // -> David "#name *" #> getUserNameAsHtml NodeSeq => NodeSeq -- a function that transforms the node (yes, it can be a CssBindFunc): "#name" #> (n: NodeSeq) => n % ("class" -> "dog") // -> Bindable -- something that implements the Bindable trait (e.g., MappedField and Record.Field) StringPromotable -- A constant that can be promoted to a String (Int, Symbol, Long or Boolean). There is an automatic (implicit) conversion from Int, Symbol, Long or Boolean to StringPromotable. "#id_like_cats" #> true "#number_of_cats" #> 2 IterableConst -- A Box, Seq, or Option of String, NodeSeq, or Bindable. Implicit conversions automatically promote the likes of Box[String], List[String], List[NodeSeq], etc. to IterableConst. "#id" #> (Empty: Box[String]) // Hi -> "#id" #> List("a", "b", "c") // -> abc "#id [href]" #> (None: Option[String]) -> Note that if you bind to the children of a selected element, multiple copies of the element result from bind to an IterableConst (if the element has an id attribute, the id attribute will be stripped after the first element): "#line *" #> List("a", "b", "c") // abc "#age *" #> (None: Option[NodeSeq]) // Dunno -> The above use cases may seem a little strange (they are not quite orthogonal), but they address common use cases in Lift. IterableFunc -- A Box, Seq, or Option of functions that transform NodeSeq => String, NodeSeq, Seq[String], Seq[NodeSeq], Box[String], Box[NodeSeq], Option[String] or Option[NodeSeq]. The same rules for handling multiple values in IterableConst apply to IterableFunc. Implicit conversions automatically promote the functions with the appropriate signature to an IterableFunc. You can chain CSS Selector Transforms with the & method: "#id" #> "33" & "#name" #> "David" & "#chat_line" #> List("a", "b", "c") & ClearClearable CSS Selector Transforms offer an alternative to Lift's traditional binding (See Helpers.bind()).
Permalink
[Less]
|
Posted
about 13 years
ago
The Lift team and the Lift community are proud to announce the availability of Lift Framework 2.1. Lift is an elegant, expressive web framework that allows any size team build and maintain secure, highly interactive, scalable web applications
... [More]
quickly and efficiently. Lift is built on the Scala and compiles to JVM byte-code. Lift applications deploy as WAR files on popular application servers and web cotainers including Jetty, Glassfish and Tomcat. Lift applications can be monitored and managed with the same proven infrastructure used to manage and monitor any Java web application. Lift is open source licensed under an Apache 2.0 license. Lift features include:
Community: the Lift community is 2,000 members strong, super-active and always there to help with questions
Best Comet (server-push) support that allows the creation of dynamic application such as Novell Pulse
Super simple Ajax for creating highly interactive web applications without worrying about HTTP plumbing
Secure by default: Lift apps are resistant to the OWASP top 10 vulnerabilities including XSS, XSRF, and parameter tampering
Concise and Maintainable: Lift apps typically contain fewer lines of code than corresponding Rails apps, yet are type safe so that many errors are flagged by the compiler
Scalable: Lift apps scale to millions of users across many servers, yet are highly efficient for single-box implementations
Compatible: Lift apps can take advantage of any Java library as well as the growing collection of Scala libraries
Lift 2.1's new features include:
Support for both Scala 2.7 and Scala 2.8.
Improved NoSQL support for MongoDB and CouchDB Squeryl support
Additionally, there are usual bug fixes and regular enhancements. Lift-powered sites include:
Foursquare: the multi-million user location based service that will soon surpass 1M+ checkins a day on their Lift-powered system
Novell Pulse: enterprise collaboration software platform based on Google Wave
Innovation Games: the fun way to do serious business -- seriously
Xerox/XMPie: the leading provider of software for cross-media, variable data one-to-one marketing
Snapsort: compare and decide on cameras
No Fouls: find pickup basketball games
Please join the Lift community and help use grow Lift. And a super-big thanks to the 30+ Lift committers and all the active community members who have grown the Lift community and code-base to what it is today -- and what it will be in the future! One more thing: We also have the all new website (liftweb.net) using the upcoming Lift CMS module! Thank you, have fun and have a great Scala LiftOff! - The Lift Framework team
References:
Website: http://www.liftweb.net
Wiki: http://www.assembla.com/wiki/show/liftweb
Discussion: http://groups.google.com/group/liftweb
ScalaLiftOff: http://scalaliftoff.com
Permalink
[Less]
|
Posted
about 13 years
ago
Announcing Lift 2.0 June 30th, 2010 The Lift team and the Lift community are proud to announce the availability of Lift 2.0 "Lift is the kind of web framework that enables you as a developer to concentrate on the big picture. Strong, expressive
... [More]
typing and higher-level features like the built-in Comet support allow you to focus on innovating instead of the plumbing. Building a rich, real-time web application like Novell Pulse requires a framework with the power of Lift under the covers." - David LaPalomento, Developer, Novell "Foursquare switched over to Scala & Lift last September and we've been thrilled with the results. The ease of developing complex interactive ajax web pages enabled a very rapid port from our previous platform. In addition, the support we've gotten from David Pollak and the rest of the Lift team has been invaluable. It's clear they are very committed to making sure that production Lift deployments get all the attention they need to succeed." - @harryh, Foursquare Engineering Tech Lead Lift is an elegant, expressive web framework that allows any size team build and maintain secure, highly interactive, scalable web applicationsquickly and efficiently. Lift is built on the Scala and compiles to JVM byte-code. Lift applications deploy as WAR files on popular application servers and web cotainers including Jetty, Glassfish and Tomcat. Lift applications can be monitored and managed with the same proven infrastructure used to manage and monitor any Java web application. Lift is open source licensed under an Apache 2.0 license. Lift features include: Community... the Lift community is 2,000 members strong, super-active and always there to help with questions Best Comet (server-push) support that allows the creation of dynamic application such as Novell Pulse Super simple Ajax for creating highly interactive web applications without worrying about HTTP plumbing Secure by default... Lift apps are resistant to the OWASP top 10 vulnerabilitys including XSS, XSRF, and parameter tampering Concise and Maintainable... Lift apps typically contain fewer lines of code than corresponding Rails apps, yet are type safe so that many errors are flagged by the compiler Scalable... Lift apps scale to millions of users across many servers, yet are highly efficient for single-box implementations Compatible... Lift apps can take advantage of any Java library as well as the growing collection of Scala libraries Lift 2.0's new features include: NoSQL support including built-in support for MongoDB and CouchDB High performance JSON support including an elegant JSON DSL and bidirectional JSON <-> Class conversion Powerful, concise REST support Support for enterprise infrastructure including JTA and LDAP Declarative systems for single Screen input and validation as well as multiple screen Wizards Radically improved development experience including much better error messages and support for dynamically changing system configuration Support for running Lift apps outside of a J/EE Servlet containers Improved Comet support including modern browser detection and better connection starvation detection Improved support for testing including super-concise dependency injection and run-mode detection Support for Simple Build Tool Performance improvements Lift-powered sites include: Foursquare -- the multi-million user location based service that will soon surpass 1M+ checkins a day on their Lift-powered system Novell Pulse -- enterprise collaboration software platform based on Google Wave Xerox/XMPie -- the leading provider of software for cross-media, variable data one-to-one marketing Snapsort -- Compare and decide on cameras No Fouls -- Find pickup basketball games Please join the Lift community and help use grow Lift. And a super-big thanks to the 30+ Lift committers who have grown the Lift community and code-base to what it is today... and what it will be in the future!
Permalink
[Less]
|
Posted
over 13 years
ago
Background
Much of the web is creating input forms for users to submit, validating those input forms and if the forms pass validation, an action is performed. If the forms don't pass validation, the user is told which fields caused the
... [More]
validation problems and is given an opportunity to fix the problems. Lift provides a single-screen input/validation mechanism called LiftScreen and a multi-page input/validation mechanism (with stateful next/previous buttons) called Wizard. This post will discuss LiftScreen and the next post will discuss Wizard.
Both Wizard and Screen share the following attributes:
All logic can be tested without involving HTTP
All logic is declarative
All state is managed by Lift
The back-button works as the user would expect it to work
The form elements are strongly typed
The rendering logic and templates is divorced from the form logic
Basics
First, let's declare a very simple input screen that asks your favorite ice cream flavor:
object AskAboutIceCream1 extends LiftScreen {
val flavor = field(S ? "What's your favorite Ice cream flavor", "")
def finish() {
S.notice("I like "+flavor.is+" too!")
}
}
We create an object, a Scala singleton, called AskAboutIceCream1 which extends LiftScreen. We declare a single field called flavor. In our view, we refer to the LiftScreen with the following code:
And we get a display:
Cancel
Finish
When we submit the form, a notice is displayed agreeing with our ice cream choice. But, we can enter a blank ice cream name and it will still be accepted. That's not optimal. We need to add some validation:
object AskAboutIceCream2 extends LiftScreen {
val flavor = field(S ? "What's your favorite Ice cream flavor", "",
trim,
valMinLen(2, "Name too short"),
valMaxLen(40, "That's a long name"))
def finish() {
S.notice("I like "+flavor.is+" too!")
}
}
This code trims the incoming string (removes any leading and trailing spaces) and then makes sure the length is reasonable. So, if we enter a blank value, we get:
Cancel
Finish
We can add another field, this time a Boolean which turns into a checkbox:
object AskAboutIceCream3 extends LiftScreen {
val flavor = field(S ? "What's your favorite Ice cream flavor", "",
trim, valMinLen(2,S ? "Name too short"),
valMaxLen(40,S ? "That's a long name"))
val sauce = field(S ? "Like chocalate sauce?", false)
def finish() {
if (sauce) {
S.notice(flavor.is+" tastes especially good with chocolate sauce!")
}
else S.notice("I like "+flavor.is+" too!")
}
}
And our display looks like:
Cancel
Finish
The Boolean sauce field defaults to creating a checkbox rather than an text field.
We can also do cross-field validation:
object AskAboutIceCream4 extends LiftScreen {
val flavor = field(S ? "What's your favorite Ice cream flavor", "",
trim, valMinLen(2,S ? "Name too short"),
valMaxLen(40,S ? "That's a long name"))
val sauce = field(S ? "Like chocalate sauce?", false)
override def validations = notTooMuchChocolate _ :: super.validations
def notTooMuchChocolate(): Errors = {
if (sauce && flavor.toLowerCase.contains("chocolate")) "That's a lot of chocolate"
else Nil
}
def finish() {
if (sauce) {
S.notice(flavor.is+" tastes especially good with chocolate sauce!")
}
else S.notice("I like "+flavor.is+" too!")
}
}
So, you you change the chocolate box and enter a flavor that contains chocolate, you get an error indicating that there's just too much chocolate.
Working with Mapper and Record instances
Turns out that LiftScreen works just ducky with Mapper and Record:
object PersonScreen extends LiftScreen {
object person extends ScreenVar(Person.create)
override def screenTop =
A single screen with some input validation
_register(() => person.is)
val shouldSave = field("Save ?", false)
val likeCats = builder("Do you like cats?", "") ^/
(s => if (Helpers.toBoolean(s)) Nil else "You have to like cats") make
def finish() {
S.notice("Thank you for adding "+person.is)
if (shouldSave.is) {
person.is.save
S.notice(person.is.toString+" Saved in the database")
}
}
}
Note the _register(() => person.is) line. It registers all for fields in the instance of Person that's created in the ScreenVar. A ScreenVar is a screen-local variable.
But, what about testing?
Before we get to the nuts and bolts, let's look at a Wizard test (we're jumping ahead a bit, but one of the goals of LiftScreen and Wizard is to allow definition and use outside the scope of an HTTP request). Here's our Wizard:
val MyWizard = new Wizard {
object completeInfo extends WizardVar(false)
def finish() {
S.notice("Thank you for registering your pet")
completeInfo.set(true)
}
val nameAndAge = new Screen {
val name = field(S ? "First Name", "",
valMinLen(2, S ?? "Name Too Short"))
val age = field(S ? "Age", 0,
minVal(5, S ?? "Too young"),
maxVal(120, S ?? "You should be dead"))
override def nextScreen = if (age.is < 18) parentName else favoritePet
}
val parentName = new Screen {
val parentName = field(S ? "Mom or Dad's name", "",
valMinLen(2, S ?? "Name Too Short"),
valMaxLen(40, S ?? "Name Too Long"))
}
val favoritePet = new Screen {
val petName = field(S ? "Pet's name", "",
valMinLen(2, S ?? "Name Too Short"),
valMaxLen(40, S ?? "Name Too Long"))
}
And here's the test:
MyWizard.currentScreen.open_! must_== MyWizard.nameAndAge
// validate that we don't go forward unless we've got a name and age
MyWizard.nextScreen
MyWizard.currentScreen.open_! must_== MyWizard.nameAndAge
MyWizard.nameAndAge.name.set("David")
MyWizard.nameAndAge.age.set(14)
MyWizard.nextScreen
// we get to the parentName field because the age is < 18
MyWizard.currentScreen.open_! must_== MyWizard.parentName
// go back and change age
MyWizard.prevScreen
MyWizard.currentScreen.open_! must_== MyWizard.nameAndAge
MyWizard.nameAndAge.age.set(45)
MyWizard.nextScreen
// 45 year olds get right to the favorite pet page
MyWizard.currentScreen.open_! must_== MyWizard.favoritePet
S.clearCurrentNotices
MyWizard.favoritePet.petName.set("Elwood")
MyWizard.nextScreen
MyWizard.currentScreen must_== Empty
MyWizard.completeInfo.is must_== true
So, we're able to walk the wizard back and forth simulating what the user enters and insuring that we're getting to the expected states.
The nuts & bolts
Let's walk through how all of this works.
First, a LiftScreen is a Lift Snippet. This means that it you can refer to the LiftScreen just by its name, e.g.
Each of the fields in the Screen are statically typed variables. We can define them with the field[T](name: => String, default: T, stuff: FilterOrValidate[T]*). Lift determines the type of the field based on the default value for the field. The FilterOrValidate varg allows you to specify the filter (transformations of input, e.g., toLowerCase) and validation functions.
LiftScreen, by default, uses to type of the field to vend an HTML form that corresponds to the type (e.g., an for a String and a checkbox for a Boolean). You can set up global Type -> Form vendors in LiftRules.vendForm for application-scope form vending.
You can also manually create a form field by creating an instance of Field:
trait Field extends BaseField {
def default: ValueType
def is: ValueType
/**
* Set to true if this field is part of a multi-part mime upload
*/
override def uploadField_? = false
def set(v: ValueType): Unit
implicit def manifest: Manifest[ValueType]
override def helpAsHtml: Box[NodeSeq] = Empty
/**
* Is the field editable
*/
def editable_? = true
def toForm: Box[NodeSeq]
def validate: List[FieldError]
def validations: List[ValueType => List[FieldError]] = Nil
def setFilter: List[ValueType => ValueType] = Nil
}
Creating the HTML
So, you've gotten to the point where you get how to define screens. But how does the screen get turned into HTML?
By default, the /templates-hidden/wizard-all.html template is chosen. You can override the template in ScreenRules (global) or by defining the template path in your LiftScreen. You just supply the bind points (the tags) and LiftScreen (and Wizard) will bind correctly to the form.
Page of
But wait...
So, we've covered the basics of LiftScreen. All the LiftScreen concepts carry over to Wizard. In a few days, I'll be walking you through creating multi-page input Wizards.
If you've got questions or comments, please post them to the Lift Google Group: http://groups.google.com/group/liftweb?hl=en
Thanks!
David
Permalink
[Less]
|
Posted
over 13 years
ago
A Chat Application in Lift by David Pollak and Steve Vinoski Last year, Debasish Ghosh and Steve Vinoski gave an overview of the Scala language, highlighting some of the features of Scala using the Lift Web framework in their article, “Scala and
... [More]
Lift—Functional Recipes for the Web.” We pick up where they left off in this column by taking a deeper dive into Lift, a Web framework in the vein of Seaside and WebObjects. http://www.computer.org/portal/web/computingnow/0610/whatsnew/internetcomputing
Permalink
[Less]
|