Return-Path:
This tutorial is based on the first Sling Gems on dev.day.com: The Sling gems: a blog in 46 lines of code
+. It has slightly been adapted to fit here. In this tutorial, the SlingPostServlet and the sling.js library are brought
+together using 46 (no kidding: fourty-six) lines of code to create a
+simple blog (or let's say bloggish) application. I used this example in my http://us.apachecon.com/c/acus2009/sessions/284
+ presentation at ApacheCon US 09 in Oakland (slides will be available
+soon), and I think it's a good testimony to the power and simplicity of
+Sling. Although this is a simple sample, it requires some custom settings to work.
+If you're just starting with Sling, SLINGxSITE:Discover Sling in 15 minutes
+ might be a better choice. See SLINGxSITE:Getting and Building Sling
+ for how to start Sling. Start it on port 8888 for the below links to work. For this sample we need the optional
+org.apache.sling.samples.path-based.rtp bundle, if it's not present in
+the OSGi console
+, install and start it. That bundle is not released yet so you might need
+to build it yourself, from its [source|http://svn.apache.org/repos/asf/sling/trunk/samples/path-based-rtp]
+. The bundle must then appear in the [OSGI console's list of bundles|http://localhost:8888/system/console/bundles]
+, with name = org.apache.sling.samples.path-based.rtp and status =
+Active. Then, login using http://localhost:8888/?sling:authRequestLogin=1 which
+should prompt you for a username and password, use admin and admin.
+Once that's done, http://localhost:8888/index.html should say You are
+currently logged in as user admin to workspace default. The easiest way to create content in Sling is to use an HTTP POST request,
+let's use a simple HTML form: {code:html}
+
+ Audience
+Step 0: Start, configure and login to Sling
+Step 1: Creating content
+Sling microblog
<div> + <form method="POST"> +Title:<br/> +<input type="text" name="title" style="width:100%"/> +<br/>Text:<br/> +<textarea style="width:100%" name="text"></textarea> + +<br/> +<input type="submit" value="save"/> +<input type="hidden" name=":redirect" value="*.html"/> + </form> +</div> + +<!-- code of step 2 comes here --> +
+
+That's two input fields, a submit button and a hidden field that tells +
Sling what to do after the POST (in this case: redirect to the html view of +the node that was just created).
+To test the form, start Sling and save the above script as +
{{/apps/blog/blog.esp}} {footnote}ESP is Sling's server-side javascript +language{footnote} in the Sling repository - a WebDAV mount is the easiest +way to do that. Browsing to {{http://localhost:8888/content/blog/*.html}} +{footnote} This assumes your instance of Sling is running on port 8888. If +that's not the case, adjust the example URLs accordingly. {footnote} should +display the above form.
+Input some data (using "foo" for the title, for the sake of our examples +
below), save the form, and Sling + should display the form again, using the URL of the node that was just +created.
+{note:title=AccessDeniedException?} +If you get an error saying _javax.jcr.AccessDeniedException: ...not allowed +
to add or modify item_ it means that you are not logged in as user admin. +See instructions above for logging in. + {note}
+At this point you're probably looking at an empty form with an URL ending +
in foo, if you used that for the title. Or foo_0 or foo_1 if other +_foo_s already existed. Don't worry about not seeing your content, we'll +fix that right away.
+h2. Step 2: Where's my content? + +To verify that our content has been created, we can have a look at the JSON +
data at {{http://localhost:8888/content/blog/foo.tidy.json}}, which should +display our new node's values:
+{code:javascript} +{ + "jcr:primaryType": "nt:unstructured", + "text": "This is the foo text", + "title": "foo" +} +
That's reassuring, but what we really want is for these values to be +displayed on the editing form for our post.
+Thanks to the sling.js client library, we just need to add a +Sling.wizard() call to our form to display those values. Let's first +add a element to our form to load the sling.js library, before +the existing of course:
+{code:html} +
+ + +And add the {{Sling.wizard()}} after the form, where we had the _code of +
step 2 comes here_ comment:
+{code:html} +<!-- code of step 2 comes here --> +<script>Sling.wizard();</script> +
Reloading the form at http://localhost:8888/content/blog/.html* and +creating a new post should now redirect to an editable version of the post, +with the form fields correctly initialized.
+We can now create and edit posts; let's add some navigation, using more of +the sling.js functionality.
+ +The sling.js library provides utilities to access and manipulate content. +For our blog, we'll use the getContent(path) method to list the +siblings of the current node.
+Add the following code to your script, after the Sling.wizard() call +that was added in step 2:
+{code:html} +
The first link to {{/content/blog/*}} brings us back to our content +
creating form, which is nothing else than the editing form reading empty +values and posting to the "magic star" URL.
+The rest of the javascript runs client-side, as it is not embedded in {{<% +
%>}} code markers, calls the {{sling.getContent}} method to get two levels +of node data below {{/content/blog}}, and displays links to nodes that it +finds.
+That's a basic navigation, of course, in a real blog we'd need some paging +
and contextualization to cope with large numbers of posts.
+Nevertheless, with this addition our ESP script allows us to create, edit +
and navigate blog posts - not bad for 46 lines of code, including comments, +whitespace and output formatting.
+h2. Step 4: Data first, structure later + +You might have heard this mantra, which we apply in many areas of Sling. + +In this case, adding a new field to our blog posts could not be easier: +
just add an input field to the form, and Sling will do the rest.
+Adding this inside our script's {{<form>}} element, for example: + +{code:html} +<br/>Author:<br/> +<input type="author" name="author" style="width:100%"/> +
Allows us to add an author name to our blog posts. No need to define +anything at the repository level, as Sling is using it in unstructured mode +in this case, and no need to migrate existing data, the author field of +existing posts will simply be empty.
+ +Now wait...we said we were going to create an ESP script, but our +"application" is just static HTML and some client javascript at this point.
+That's correct - as we are using only Sling client-facing features at this +point (HTTP POST and sling.js), we do not necessarily need to use ESP +code.
+To keep things simple, we'll refrain from adding ESP-based features at this +point, but you can of course use any ESP code in the blog.esp "script".
+ +The 46-line blog is a good example of the power of Sling. It leverages the SlingPostServlet +, which handles POST requests in a form-friendly way, and the +[sling.js|http://svn.apache.org/repos/asf/sling/trunk/bundles/servlets/post/src/main/resources/system/sling.js] +client library, which provides high-level functionality on the client side.
+{display-footnotes}
+ + +The Resource and ResourceResolver interfaces are defined with a +method adaptTo, which adapts the object to other classes. Using this +mechanism the JCR session of the resource resolver calling the adaptTo +method with the javax.jcr.Session class object. Likewise the JCR node +on which a resource is based can be retrieved by calling the +Resource.adaptTo method with the javax.jcr.Node class object.
+To use resources as scripts, the Resource.adaptTo method must support +being called with the org.apache.sling.api.script.SlingScript class +object. But of course, we do not want to integrate the script manager with +the resource resolver. To enable adapting objects to classes which are not +foreseen by the original implementation, a factory mechanism is used. This +way, the script manager can provide an adapter factory to adapt +Resource to SlingScript objects.
+ +The Adaptable interface defines the API to be implemented by a class +providing adaptability to another class. The single method defined by this +interface is
+/** + * Adapts the adaptable to another type. + * <p> + * Please not that it is explicitly left as an implementation detail +
whether
+ * each call to this method with the same type
yields the same
+ * object or a new object on each call.
+ *
+ * Implementations of this method should document their adapted types as
+ * well as their behaviour with respect to returning newly created or not
+ * instance on each call.
+
+ * @param javax.jcr.Node.class
or
+ * java.io.File.class
+ * @return The adapter target or null
if the resource cannot
+ * adapt to the requested type
+
This method is called to get a view of the same object in terms of another +class. Examples of implementations of this method is the Sling +ResourceResolver implementation providing adapting to a JCR session and +the Sling JCR based Resource implementation providing adapting to a JCR +node.
+ +Sometimes an Adaptable implementation cannot foresee future uses and +requirements. To cope with such extensibility requirements two interfaces +and an abstract base class are defined:
+The AdapterFactory interface defines the service interface and API for +factories supporting extensible adapters for SlingAdaptable objects. +The interface has a single method:
+/** + * Adapt the given object to the adaptable type. The adaptable object is + * guaranteed to be an instance of one of the classes listed in the + * {@link #ADAPTABLE_CLASSES} services registration property. The type + * parameter is one of the classes listed in the {@link #ADAPTER_CLASSES} + * service registration properties. + * <p> + * This method may return <code>null</code> if the adaptable object cannot + * be adapted to the adapter (target) type for any reason. In this case, +
the + * implementation should log a message to the log facility noting the cause + * for not being able to adapt. + *
+ * Note that the adaptable
object is not required to implement
+ * the Adaptable
interface, though most of the time this
+method
+ * is called by means of calling the {@link Adaptable#adaptTo(Class)}
+ * method.
+
+ * @param null
if this factory instance
+ * cannot adapt the object.
+
Implementations of this interface are registered as OSGi services providing +two lists: The list of classes wich may be adapted (property named +adaptables) and the list of classes to which the adapted class may be +adapted (property named adapters). A good example of an Class +implementing AdapterFactory is the SlingScriptAdapterFactory.
+AdapterFactory services are gathered by a AdapterManager +implementation for use by consumers. Consumers should not care for +AdapterFactory services.
+ +The AdapterManager is defines the service interface for the genralized +and extensible use of AdapterFactory services. Thus the adapter manager +may be retrieved from the service registry to try to adapt whatever object +that needs to be adapted - provided appropriate adapters exist.
+The AdapterManager interface is defined as follows:
+/** + * Returns an adapter object of the requested <code>AdapterType</code> for + * the given <code>adaptable</code> object. + * <p> + * The <code>adaptable</code> object may be any non-<code>null</code> +
object
+ * and is not required to implement the Adaptable
interface.
+
+ * @param null
if no factory exists to
+ * adapt the adaptable
to the AdapterType
+ * or if the adaptable
cannot be adapted for any other
+ * reason.
+
Any object can theoretically be adapted to any class even if it does not +implement the Adaptable interface, if an AdapterFactory service +delivers a getAdapter() method which adapts an object to another one. +To check if there's any existing AdapterFactory which can adapt a given +object to another one the AdapterManager service with it's +getAdapter() method does the job. So the Adaptable interface merely +is an indicator that the object provides built-in support for beeing +adapted.
+ +The SlingAdaptable class is an implementation of the Adaptable +interface which provides built-in support to call the AdapterManager to +provide an adapter from the Adaptable object to the requested class.
+An example of extending the SlingAdaptable class will be the Sling JCR +based Resource implementation. This way, such a resource may be adapted +to a SlingScript by means of an appropriatley programmed +AdapterFactory (see below).
- + +The Apache Sling Commons Thread Pool bundle provides a thread pool +services. All thread pools are managed by the +{nl:org.apache.sling.commons.threads.ThreadPoolManager}. This service can +be used to get a thread pool.
+Thread pools are managed by name - there is a default thread pool and +custom thread pools can be generated on demand using a unique name.
+The thread pools are actually wrappers around the thread pool support +(executer) from the Java library. The advantage of using this thread pool +service is, that the pools can be configured and managed through OSGi +configurations. In addition the bundle contains a plugin for the Apache +Felix Web Console.
+When using the {nl:ThreadPoolMananger} it is important to release a thread +pool using the manager after it has been used.
- + + +The Community Roles and Processes are put in effect as of 13/May/2009. +Updated 7/December/2009 to reflect Sling being a top level project.
+ +There are different roles with which Sling community members may be +associated: User, Contributor, Committer, and PMC (Project Management +Committee) Member. These roles are assigned and assumed based on merit.
+The User and Contributor roles are acquired by using the software and +participating in the community, but the Committer and PMC member roles can +only be granted by a PMC vote.
+The roles defined here conform to the ASF's definition of roles +.
+ +Users are the people who use any of the products of the Sling project. +People in this role are not contributing code, but they are using the +products, reporting bugs, making feature requests, testing code, and such. +This is by far the most important category of people, since without users +there is no reason for Sling. When a user starts to contribute code or +documentation patches, they become a Contributor.
+ +Contributors are the people who write code or documentation patches or +contribute positively to the project in other ways. A volunteer's +contribution is always recognized.
+ +Contributors who give frequent and valuable contributions to a subproject of Sling can have their status promoted to that of a Committer +. A Committer has write access to Sling's source code repository. +Contributors of documentation are eligible as committers in the same way as +contributors of pure code.
+ +Committers showing continued interest in the project and taking an active part in the evolution of the project may be elected as PMC + members. The PMC (Project Management Committee) is the official managing +body of project and is responsible for setting its overall direction.
+ +There is no requirement for becoming a User or Contributor; these roles are +open to everyone.
+ +In order for a Contributor to become a Committer, a member of the PMC can +nominate that Contributor to the PMC. Once a Contributor is nominated, the +PMC calls a vote on the PMC private mailing list.
+If there are at least three positive votes and no negative votes after +three days (72 hours), the results are posted to the PMC private mailing +list.
+Upon a positive vote result, the Contributor will be emailed by the PMC to +invite him/her to become a Committer. If the invitation is accepted, an +announcement about the new Committer is made to the developer mailing list +and he/she is given write access to the source code repository. A +Contributor will not officially become a Committer member until the +appropriate legal paperwork is submitted.
+ +In order for a Committer to become a member of the PMC, a member of the PMC +can nominate that Committer to the PMC. Once a Committer is nominated, the +PMC calls a vote on the PMC private mailing list.
+If there are at least three positive votes and no negative votes after +three days (72 hours), the results are posted to the PMC private mailing +list.
+To have the Committer being accepted as a PMC member, the ASF Board has +acknowledge the addition to the PMC. The Committer should not be consulted +about his/her desire to become a PMC member before the board +acknowledgement, or be informed that they are being considered, since this +could create hard feelings if the vote does not pass.
+Upon a positive vote result, the PMC member will be emailed by the PMC to +invite him/her to become a PMC member. If the invitation is accepted, an +announcement about the new PMC member is made to the developer mailing +list.
+ +NOTE: This documentation is work in progress!
+ +The Apache Sling Event Support bundle provides interesting services for +advanced event handling and job processing. While this bundle leverages the +OSGi EventAdmin, it provides a very powerful support for so called jobs: a +job is a task which has to be performed by a component - the Sling job +handling ensures that exactly one component performs this task.
+To get some hands on code, you can refer to the following tutorials: + How to Manage Events in Sling + Scheduler Service (commons scheduler)
+The Sling Event Supports adds the following services: + Jobs + Distributed Events +* Scheduled Events
+ +In general, the eventing mechanism (OSGi EventAdmin) has no knowledge about +the contents of an event. Therefore, it can't decide if an event is +important and should be processed by someone. As the event mechanism is a +"fire event and forget about it" algorithm, there is no way for an event +admin to tell if someone has really processed the event. Processing of an +event could fail, the server or bundle could be stopped etc.
+On the other hand, there are use cases where the guarantee of processing a +job is a must and usually this comes with the requirement of processing +this job exactly once. Typical examples are sending notification emails (or +sms) or post processing of content (like thumbnail generation of images or +documents).
+The Sling Event Support adds the notion of a job to the OSGi EventAdmin. A +job is a special OSGi event that someone has to process (do the job). The +job event has a special topic org/apache/sling/event/job to indicate that +the event contains a job. These job events are consumed by the Sling Job +Handler - it ensures that someone does the job! To support different jobs +and different processors of such jobs, the real topic of the event is +stored in the event.job.topic property of the original event. When a job +event (event with the topic org/apache/sling/event/job) is received, a +new event with the topic from the property event.job.topic is fired +(Firing this event comes of course with a set of rules and constraints +explained below).
+In order to distinguish a job which occured twice and a job which is +generated "at the same time" on several nodes, each job can be uniquely +identified by its topic (property event.job.topic) and an optional job +name, the event.job.id property. It is up to the client creating the +event to ensure that the event.job.id property is unqiue and identical +on all application nodes. If the job name is not provided for the job, then +it is up to the client to ensure that the job event is only fired once. +Usually for jobs generated based on user interaction, a unique job name is +not required as the job is only created through the user interaction.
+ +A job processor is a service consuming and processing a job. It listens for +OSGi events with the job topic. The OSGi EventAdmin usually comes with a +timeout for event handlers. An event handler must consume an OSGi event as +fast as possible otherwise the handler might get a timeout and get +blacklisted. Therefore a job processor should never directly process the +job in the event handler method, but do this async.
+In addition the Sling Job Handler needs to get notified if someone is +processing a job and when someone has finished processing this job.
+To make implementing such a job processor easier, there is the JobUtil +utility class along with the JobProcessor interface. The JobUtil class +has a helper method for this: processJob(Event, JobProcessor). The job +processor must implement the JobProcessor interface which consists of a +single process(Event) method. When the event handler receives a job event +through the OSGi EventAdmin, it calls JobUtil.processJob(event, this) and +returns. This utility method takes care to notify the Sling Job Handler +that someone is processing the job. Then the process(Event) method of the +job processor is called in the background and when it returns, the Sling +Job Handler is notified that the job is completed (or processing failed).
+If the job processor wants to do the background processing by itself or +does not need background processing at all, it must signal starting and +completition of the job by call JobUtil.acknowledgeJob(Event), +_JobUtil.finishedJob(event) or _JobUtil.rescheduleJob(Event).
+ +Incoming jobs are first persisted in the repository (for failover etc.) and +then a job is put into a processing queue. There are different types of +queues defining how the jobs are processed (one after the other, in +parallel etc.).
+For managing queues, the Sling Job Handler uses the OSGi ConfigAdmin - it +is possible to configure one or more queue configurations through the +ConfigAdmin. One way of creating and configuring such configurations is the +Apache Felix WebConsole.
+ +A queue configuration can have the following properties:
+*Property Name* | *Description* |
_queue.name_ | The name of the queue. If matching is used for +topics, the value \{0\} can be used for replacing the matched part. |
_queue.type_ | The type of the queue: ORDERED, UNORDERED, +TOPIC_ROUND_ROBIN, IGNORE, DROP |
_queue.topics_ | A list of topics processed by this queue. Either +the concrete topic is specified or the topic string ends with /* or /. If a +star is at the end all topics and sub topics match, with a dot only direct +sub topics match. |
_queue.maxparallel_ | How many jobs can be processed in parallel? +-1 for number of processors. |
_queue.retries_ | How often should the job be retried. -1 for +endless retries. |
_queue.retrydelay_ | The waiting time in milliseconds between job +retries. |
_queue.priority_ | The thread priority: NORM, MIN, or MAX |
_queue.runlocal_ | Should the jobs only be processed on the cluster +node they have been created? |
_queue.applicationids_ | Optional list of application (cluster +node) ids. If configured, these jobs are only processed on this application +node. |
_service.ranking_ | A ranking for this configuration. |
The configurations are processed in order of their service ranking. The +first matching queue configuration is used for the job.
+ +An ordered queue processes one job after the other.
+ +Unordered queues process jobs in parallel.
+ +The jobs are processed in parallel. Scheduling of the jobs is based on the +topic of the jobs. These are started by doing round-robin on the available +topics.
+ +A queue of type ignoring ignores this job. The job is persisted but not +processed. This can be used to delay processing of some jobs. With a +changed configuration and a restart of the Sling Job Handler the ignored +jobs can be processed at a later time.
+ +A queue of type drop is dropping a job - which means it is not processed +at all and directly discarded.
+ +The job event handler listens for all job events (all events with the topic +org/apache/sling/event/job) and will as a first step persist those events +in the JCR repository. All job events are stored in a tree under the job +root node /var/eventing/jobs. Persisting the job ensures proper handling +in a clustered environment and allows failover handling after a bundle stop +or server restart. Once a job has been processed by someone, the job will +be removed from the repository.
+When the job event listener tries to write a job into the repository it +will check if the repository already contains a job with the given topic +event.job.topic and job name (property event.job.id). If the event has +already been written by some other application node, it's not written +again.
+Each job is stored as a separate node with the following properties: +
Property Name | Description |
event:topic | The topic of the job |
event:id | The unique identifier of this job (optional). + |
event:created | The date and time when the event has been created +(stored in the repository) + |
event:application | The identifier of the node where the job was +created |
event:properties | Serialized properties |
event:finished | The date and time when the job has been finished |
event:processor | The identifier of the node which processed the job +(after successful processing) |
The failover of an application node is accomplished by locking. If a job is +locked in the repository a session scoped lock is used. If this application +node dies, the lock dies as well. Each application node observes the JCR +locking properties and therefore gets aware of unlocked event nodes with +the active flag set to true. If an application node finds such a node, it +locks it, updates the event:application information and processes it +accordingly. In this case the event gets the additional property +org/apache/sling/job/retry.
+Each application is periodically removing old jobs from the repository +(using the scheduler).
+ +A job event is an event like any other. Therefore it is up to the client +generating the event to decide if the event should be distributed. If the +event is distributed, it will be distributed with a set event.application +on the remote nodes. If the job event handler receives a job with the +event.application property set, it will not try to write it into the +repository. It will just broadcast this event asynchronously as a ~FYI +event.
+If a job event is created simultanously on all application nodes, the event +will not be distributed. The application node that actually has the lock on +the stored job in the repository will clear the event.application when +sending the event locally. All other application nodes will use the +event.application stored in the repository when broadcasting the event +locally.
+ +Based on some usage patterns, we discuss the functionality of the eventing +mechanism.
+ +If a user action results in an event, the event is only created on one +single node in the cluster. The event object is generated and delivered to +the OSGi event admin. If the event.distribute is not explicitly set, the +event is only distributed localled.
+If the event.distribute is the, the cluster event handler will write the +event into the repository. All nodes in the cluster observe the repository +area where all events are stored. If a new event is written into that area, +each application node will get notified. It will create the event based on +the information in the repository, clear the event.distribute and publish +the event.
+The flow can be described as follows: +1. Client code generates event using OSGi API, if the event.distribute +should be set, it is using the ~EventUtil. +1. Client code sends the event to the (local) event admin. +1. Event admin delivers the event locally. +1. Clustering event handler receives the event if event.distribute is +present +1. # Event handler adds event.application and writes the event to the +repository +1. # Remote repository observers get notified through JCR observation about +the new event. They distribute the event locally with the +event.application (from the node where the event occured first) and +cleared event.distribute.
+ +JCR events are environment generated events and therefore are sent by the +repository to each node in the cluster. In general, it is advisable to not +built the application on the low level repository events but to use +application events. Therefore the observer of the JCR event should create +an OSGi event based on the changes in the repository. A decision has to be +made if the event should be a job or a plain event.
+The flow can be described as follows: +1. Client registers for JCR observation +1. JCR notifies the client for changes +1. Client generates OSGi event based on the JCR events (the +event.distribute will not be set), it decides if it sends this event as a +job. +1. Client code sends the event to the (local) event admin +1. Event admin publishes the event locally +1. The distribution event handler does not set see the event as the +event.distribute is not set. +1. The job event handler gets the event if it has the job topic +1. # The job event handler adds the event.application property and tries to +write the job to the repository +1. ## If no job with the topic and id property is in the repository, the +event will be written and locked. +1. ## If an event with the topic and id property is in the repository then: +1. ### If the event.application equals the current application node, the +event is set to active (event:active) in the repository again and locked +1. ### If the event.application does not equal the current application +node, the event is not distributed locally. +1. ## If the job could be locked in the repository, the job event handler +delivers the job locally and synchronously and it unlocks the job and sets +event:active to false afterwards.
+ +Scheduled events are OSGi events that have been created by the environemnt. +They are generated on each application node of the cluster through an own +scheduler instance. Sending these events works the same as sending events +based on JCR events (see above).
+In most use cases a scheduler will send job events to ensure that exactly +one application node is processing the event.
+ +If you want to receive OSGi events, you can just follow the specification: +receive it via a custom event handler which is registered on bundle start - +a filter can be specified as a configuration property of the handler.
+As we follow the principle of distributing each event to every registered +handler, the handler has to decide if it will process the event. In order +to avoid multiple processing of this event in a clustered environment, the +event handler should check the event.application property. If it is not +set, it's a local event and the handler should process the event. If the +event.application is set, it's a remote event and the handler should not +process the event. This is a general rule of thumb - however, it's up to +the handler to make its decision either on event.application or any other +information.
+It is advisable to perform the local event check even in a non clustered +environment as it makes the migration to a cluster later on much easier and +there is nearly no performance overhead caused by the check.
+The ~EventUtil class provides an utility method isLocalEvent(Event) which +checks the existance of the event.application property and returns true +if it is absend.
+ +In addition to the job handling, the Sling Event support adds handling for +distributed events. A distributed event is an OSGi event which is sent +across JVM boundaries to a different VM. A potential use case is to +broadcast information in a clustered environment.
+ +When it comes to application based on Sling, there is a variety of sources +from which OSGi events can be send: + JCR observation events + Application generated events + Events from messaging systems (~JMS) + "External events"
+The events can eiter be generated inside a current user context, e.g. when +the user performs an action through the UI, or they can be out of a user +context, e.g. for schedulded events. This leads to different weights of +events.
+ +We can distinguish two different weights of events, depending how they are +distributed in a clustered environment:
+External events, like incoming JMS events etc. might fall either into the +first or the second category. The receiver of such events must have the +knowledge about the weight of the event.
+ +The foundation of the distributed event mechanism is to distribute each +event to every node in a clustered environment. The event distribution +mechanism has no knowledge about the intent of the event and therefore is +not able to make delivery decisions by itself. It is up to the sender to +decide what should happen, however the sender must explicitly declare an +event to be distributed. There are exceptions to "distributing everything +to everywhere" as for example framework related events (bundle stopped, +installed etc.) should not be distributed.
+The event mechanism will provide additional functionality making it easier +for event receivers to decide if they should process an event. The event +receiver can determine if the event is a local event or comming from a +remote application node. Therefore a general rule of thumb is to process +events only if they're local and just regard remote events as a FYI.
+The event mechanism is an event mechanism which should not be confused +with a messaging mechanism. Events are received by the event mechanism +and distributed to registered listeners. Concepts like durable listeners, +guarantee of processing etc. are not part of the event mechanism itself. +However, there is additional support for such things, like job handling.
+The application should try to use application events instead of low level +JCR events whereever possible. Therefore a bridging between JCR events and +the event mechanism is required. However, a general "automatic" mapping +will not be provided. It is up to the application to develop such a mapping +on a per use case base. There might be some support to make the mapping +easier.
+The event handling should be made as transparent to the developer as +possible. Therefore the additional code for a developer to make the +eventing working in a clustered environment etc. should be kept to a +minimum (which will hopefully reduce possible user errors).
+ +For distributed events two properties are defined (check the EventUtil +class): + event.distribute - this flag is set by the sender of an event to give a +hint if the event should be distributed across instances. For example JCR +observation based events are already distributed on all instances, so there +is no further need to distribute them. If the flag is present, the event +will be distributed. The value has currently no meaning, however the +EventUtil method should be used to add this property. If the flag is absent +the event is distributed locally only. event.application - An identifier for the current application node in +the cluster. This information will be used to detect if an event has been +created on different nodes. If the event has been created on the same node, +the event.application is missing, if it is a remote event, the +event.application contains the ID of the node, the event has been +initially created. Use the EventUtil.isLocal(Event) method to detect if +the event is a local or a distributed event.
+While the event.distribute must be set by the sender of an event (if the +event should be distributed), the event.application property is +maintained by the event mechanism. Therefore a client sending an event +should never set this information by itself. This will confuse the local +event handlers and result in unexpected behaviour. On remote events the +event.application is set by the event distribution mechanism.
+ +The (local) event admin is the service distributing events locally. The +Sling Distributing Event Handler is a registered event handler that is +listening for events to be distributed. It distributes the events to remote +application notes, the JCR repository is used for distribution. The +distributing event handler writes the events into the repository, the +distributing event handlers on other application nodes get notified through +observation and then distribute the read events locally.
+As mentioned above, the client sending an event has to mark an event to be +distributed in a cluster by setting the event.distribute in the event +properties (through EventUtil). This distribution mechanism has the +advantage that the application nodes do not need to know each other and the +distribution mechanism is independent from the used event admin +implementation.
+ +Distributable events are stored in the repository, the repository will have +a specific area (path) where all events are stored.
+Each event is stored as a separate node with the following properties: +
Property Name | Description |
event:topic | The topic of the event |
event:application | The identifier of the application node where the +event was created |
event:created | The date and time when the event has been created +(stored in the repository) + |
event:properties | Serialized properties (except the +event.distribute, but including the event.application) |
Each application is periodically removing old events from the repository +(using the scheduler).
+ +Scheduled events are OSGi events that have been created by the environemnt. +They are generated on each application node of the cluster through an own +scheduler instance. Sending these events works the same as sending events +based on JCR events (see above).
+In most use cases a scheduler will send job events to ensure that exactly +one application node is processing the event.
+ +If you want to receive OSGi events, you can just follow the specification: +receive it via a custom event handler which is registered on bundle start - +a filter can be specified as a configuration property of the handler.
+As we follow the principle of distributing each event to every registered +handler, the handler has to decide if it will process the event. In order +to avoid multiple processing of this event in a clustered environment, the +event handler should check the event.application property. If it is not +set, it's a local event and the handler should process the event. If the +event.application is set, it's a remote event and the handler should not +process the event. This is a general rule of thumb - however, it's up to +the handler to make its decision either on event.application or any other +information.
+It is advisable to perform the local event check even in a non clustered +environment as it makes the migration to a cluster later on much easier and +there is nearly no performance overhead caused by the check.
+The ~EventUtil class provides an utility method isLocalEvent(Event) which +checks the existance of the event.application property and returns true +if it is absend.
+ +Each Sling based application will contain a scheduler service (which is +based on the Quartz open source project).
+ +A typical example for post processing (or running a business process) is +sending an email or creating thumbnails and extracting meta data from the +content (like we do in DAM), which we will discuss here.
+An appropriate JCR observer will be registered. This observer detects when +new content is put into the repository or when content is changed. In these +cases it creates appropriate CONTENT_ADDED, CONTENT_UPDATED OSGi events +from the JCR events. In order to ensure that these actions get processed +accordingly, the event is send as a job (with the special job topic, the +topic and id property).
+The event admin now delivers these jobs to the registered handlers. The job +event handler gets notified and (simplified version) sends the contained +event synchronously. One of the handlers for these events is the post +processing service in DAM. The job mechanism ensures that exactly one +application node is post processing and that the process has to be finished +even if the application node dies during execution.
+ +The scheduler is a service which uses the open source Quartz library. The +scheduler has methods to start jobs periodically or with a cron definition. +In addition, a service either implementing java.lang.Runnable or +org.quartz.job is started through the whiteboard pattern if it either +contains a configuration property scheduler.expression or +scheduler.period. The job is started with the ~PID of the service - if +the service has no PID, the configuration property scheduler.name must be +set.
- + + +{tm}Apache Sling{tm} is an innovative web framework that is intended to +bring back the fun to web development.
+Discussions about Sling happen on our mailing lists, see the Project Information + page for more info.
+ +Apache Sling is a web framework that uses a Java Content Repository +, such as [Apache Jackrabbit|http://jackrabbit.apache.org/] +, to store and manage content.
+Sling applications use either scripts or Java servlets, selected based on +simple name conventions, to process HTTP requests in a RESTful way.
+The embedded Apache Felix + OSGi framework and console provide a dynamic runtime environment, where +code and content bundles can be loaded, unloaded and reconfigured at +runtime.
+As the first web framework dedicated to JSR-170 + Java Content Repositories, Sling makes it very simple to implement simple +applications, while providing an enterprise-level framework for more +complex applications.
+ +{excerpt-include:News|nopanel=true} +* all news...
+ +Sling started as an internal project at Day Software +, and entered the Apache Incubator in September 2007. As of June, 17th, +2009 Apache Sling is a top level project of the Apache Software Foundation.
+The name "Sling" has been proposed by Roy Fielding who explained it like +this:
+{quote} +[The name is](the-name-is.html) + Biblical in nature. The story of David: the weapon he uses to slay the +giant Goliath is a sling. Hence, our David's [David Nuescheler, CTO of +Day Software] favorite weapon.
+It is also the simplest device for delivering content very fast. +{quote}
+ +See Who is using Sling + on our public wiki.
+ +If you prefer doing rather than reading, please proceed to Discover Sling in 15 minutes + or read through the recommended links in the [Getting Started] + section, where you can quickly get started on your own instance of Sling.
+ +Day built a Wiki system on Sling. Each Wiki page is a node (with optional +child nodes) in the repository. As a page is requested, the respective node +is accessed and through the applying Component is rendered.
+Thanks to the JCR Mapping and the resolution of the Component from the +mapped Content, the system does not care for what actual node is addressed +as long as there is a Content mapping and a Component capable of handling +the Content.
+Thus in the tradition of REST, the attachement of a Wiki page, which +happens to be in a node nested below the wiki page node is easily accessed +using the URL of the wiki page attaching the relative path of the +attachement ode. The system resolves the URL to the attachement Content +and just calls the attachement's Component to spool the attachement.
+ +Day has implemented a Digital Asset Management (DAM) Application based on +Sling. Thanks to the flexibility of the Content/Component combo as well as +the service registration/access functionality offered by OSGi, extending +DAM for new content type is merely a matter of implementing one or two +interfaces and registering the respective service(s).
+Again, the managed assets may be easily spooled by directly accessing them.
+ +Last but not least, Sling offers itself very well to implementing a Web +Content Management system. Thanks to the flexibility of rendering the +output - remember: the system does not care what to render, as long as the +URL resolves to a Content object for which a Component exists, which is +called to render the Content - providing support for Web Content authors +(not PHP programmers but users out in the field) to build pages to their +likings can easily be done.
+ +The main purpose of Sling is to develop a content-centric Web Application +framework for Java Content Repository (JCR) based data stores. Sling is +implemented - with the notable exception of JCR Node Type management - +purely in terms of the JCR API and as such may use any JCR compliant +repository. The default implementation for Apache Jackrabbit + is provided out of the box.
+ +Sling is implemented as a series of OSGi + Bundles and makes extensive use of the OSGi functionality, such as +lifecycle management and the service layer. In addition, Sling requires +several OSGi compendium services to be available, such as the Log Service, +Http Service, Configuration Admin Service, Metatype Service, and +Declarative Services.
+ +While Sling does not require a specific OSGi framework implementation to +run in, Sling is being developed using Apache Felix + as the OSGi framework implementation. It has not been tested yet, but it +is expected that Sling also operates perfectly inside other OSGi frameworks +such as [Equinox|http://www.eclipse.org/equinox] + and [Knopflerfish|http://www.knopflerfish.org] +.