| Apache Sling > FAQ |
This page lists a series of common questions and answers. It is of course work in progress ...
Using the userManager:
curl \
-F"oldPwd=admin" \
-F"newPwd=Fritz" \
-F"newPwdConfirm=Fritz" \
http://admin:admin@localhost:8080/system/userManager/user/admin.changePassword.html
You will also have to set that password in the Felix Web Management Console (/system/console/configMgr) under "Apache Sling Embedded JCR Repository." This is used by Sling to create an admin JCR session (using SlingRepository.loginAdministrative()) for components that need to have full access to the repository.
Note: Only after restarting the framework the old password will become invalid (as of 09-11-10).
Note: depending on the login module used in Jackrabbit, the password might not be checked at all (SimpleLoginModule, standard in Jackrabbit <= 1.4). Since Jackrabbit 1.5, the DefaultLoginModule provides full user support.
At the moment, you cannot do this. (Soon to change as per SLING-1172!) Instead, each value must be a field in the request POST. For example, suppose you have the json document:
{
"greetings":"Hello, World!",
"multi" : ["first","second"],
"translations" : { "en": "Hello", "zh", "你好" }
}
You would do a post such as:
curl -F"greetings=Hello, World!" -F"mult=first" -F"multi=second" -F"translations/en=Hello" -F"translations/zh=你好" http://admin:admin@localhost:8080/content/../../..
'apps' is reserved for matching scripts evaluated by sling.
The "*" url is used for POSTing to a child node.
By default, if a resource cannot be found from the root url, sling will try appending "content". For example, if you request the following non-existent resource:
http://localhost:8080/blog/first_post
Sling will look in:
http://localhost:8080/content/blog/first_post
Before returning a 404.
Let's start by creating a resource:
curl -F"greetings=Hello, World" -F"translations/en=Hello" -F"translations/es=hola" http://admin:admin@localhost:8080/content/greet
We can now view the resource with:
curl http://admin:admin@localhost:8080/content/greet.json {"greetings":"Hello, World","jcr:created":"Fri Nov 06 2009 16:26:23 GMT-0800","jcr:primaryType":"sling:Folder"}
Notice that the "greet" resource is a sling:Folder. Also notice that it's a little hard to read the result. Let's tidy it up:
curl http://admin:admin@localhost:8080/content/greet.tidy.json { "greetings": "Hello, World", "jcr:created": "Fri Nov 06 2009 16:26:23 GMT-0800", "jcr:primaryType": "sling:Folder" }
But where did our translations go? To get them, we have to request 2 nodes down into the tree:
curl http://admin:admin@localhost:8080/content/greet.tidy.2.json { "greetings": "Hello, World", "jcr:created": "Fri Nov 06 2009 16:26:23 GMT-0800", "jcr:primaryType": "sling:Folder", "translations": { "en": "Hello", "jcr:created": "Fri Nov 06 2009 16:26:23 GMT-0800", "es": "hola", "jcr:primaryType": "sling:Folder" } }
However, if we try and get the resource without an extension, we get nothing found. This is because we're requesting a folder, so sling tries to find either an index.html or return a directory list, just like a normal directory on a webserver.
To fix this, we can use a script and a sling resource type. Let's update our greeting document:
curl F"sling:resourceType=greeting" -F"greetings=Hello, World" -F"translations/en=Hello" -F"translations/es=hola" http://admin:admin@localhost:8080/content/greet
Then we'll post a simple esp script to apps:
<html>
<head><title></title></head>
<body>
<h1><%= currentNode.greetings %></h1>
</body>
</html>
curl -X MKCOL http://admin:admin@gandalf.local:8080/apps/greeting curl -T GET.esp http://admin:admin@localhost:8080/apps/greeting/GET.esp
Now you should be able to see an HTML version of the resource at http://localhost.local:8080/content/greet. This script matches the sling:resourceType we set and the HTTP method we used. Note that resourceType matches must be exact.
Assuming a versionable node at /content/versioned, with sling:resourceType=foo, here's the /apps/foo/html.esp script that handles the /content/versioned.html request:
<html> <% // assume we have a versionable node var iter = currentNode.getVersionHistory().getAllVersions(); %> <body> <h1>Versions of node <%= currentNode.getPath() %></h1> <% while(iter.hasNext()) { var v = iter.nextVersion(); // use UUID of version node to build a link, and add a .version // selector to have another esp script process that request var uuid = v["jcr:uuid"]; var vPath = currentNode.getPath() + ".version." + uuid + ".html"; // Use Version creation date as the link text var vDate = v.getCreated().getTime(); %> <a href="<%= vPath %>"><%= vDate %></a><br/> <% } %> </body> </html>
The links to the individual versions look like: /content/versioned.version.313016e1.html where the first .version. selector causes a different esp script to be called to display the version data, and the 313016e1 selector is the UUID of the versioned node (real UUIDs are longer).
That request is handled by this second script, /apps/foo/version/html.esp (name will change soon, SLING-387):
<html> <% // Get version node UUID, which is the second selector var uuid = null; var sel = request.getRequestPathInfo().getSelectors(); if(sel.length >= 2) { uuid = sel[1]; } else { response.sendError(400, "Version node UUID must be given as second selector"); } // Get version node var v = currentNode.getSession().getNodeByUUID(uuid); var frozen = v.getNode("jcr:frozenNode"); var title = frozen.title; %> <body> <h1>Version of node <%= currentNode.getPath() %></h1> Name: <b><%= v.getName() %></b><br/> UUID: <b><%= uuid %></b><br/> Path: <b><%= v.getPath() %></b><br/> Frozen node path: <b><%= frozen.getPath() %></b><br/> <% if(title) { %> Frozen node title: <b><%= frozen.getProperty("title") %></b><br/> <% } else { %> Frozen node does not have a title property <% } %> </body> </html>
Which uses the UUID selector to retrieve the versioned node.
The second trick here is that the versioned data is saved as a "jcr:frozenNode" node under the Version node. This is explained for example at http://www.onjava.com/lpt/a/6784 .
See SLING-580, the SlingServletResolver class logs detailed information (at the DEBUG level) to indicate in which order the candidate scripts and servlets are considered for processing a request.
"*" resources do not have a sling:resourceType which can cause confusion when you're trying to render a specific script. Consider:
Suppose we have content such as:
content --gradapp ----application --------app1 --------app2 ------------tabs ----------------tab1 ----------------tab2 apps --gradapp ----application --------edit.esp --------html.esp --------list.esp ----tab --------edit.esp
In this case, http://localhost:8888/gradapp/application/app1.edit.html will provide an edit page for the app1 resource using the script from apps/gradapp/application/edit.esp. However, http://localhost:8888/gradapp/application/*.edit.html will not use that edit.esp script.
By default the "star resource" does not have a resource type, so you get the default rendering. To give it a specific resource type based on its path, you can install and start the samples/path-based-rtp bundle.
Another suggestion is to register a generic node creation form script, e.g. at /apps/sling/servlet/default/create.esp. You should be able to invoke that script by browsing to /gradapp/application/*.create. If you want the create.esp script to be able to render different forms
(e.g. one for applications, one for tabs) you could do so by checking on a query parameter.
So requesting:
/gradapp/application/*.create?typeToCreate=application
could give a form for creating application nodes, while:
/gradapp/application/*.create?typeToCreate=tab
could give the tab form.
See: http://markmail.org/message/htl6r3uctuzb6l5q and http://mail-archives.apache.org/mod_mbox/sling-users/200911.mbox/%3c8A802DC6-7472-4040-807A-D55524F30D3E@gmail.com%3e
The JSON rendering is done by the DefaultGetServlet, which is hardwired to use the JsonRendererServlet for .json extensions.
If a servlet or script is registered for the sling/servlet/default resource type, but with a specific sling.servlet.extensions property (set using the @scr.property annotation), it will take over and process GET requests which have a .json extension and no specific servlet or script.
As scripts and servlets are equivalent in Sling, the simplest way to do this to create a script at apps/sling/servlet/default/json.esp, for example.
The same logic applies to other extensions (html, txt, ...) handled by the DefaultGetServlet.
The following servlet (inspired from the Sakai ScriptRunner) executes scripts directly when called with the script URL and a .runscript selector (for example /foo/myscript.esp.runscript.html).
Note that this can be insecure: if users are allowed to upload scripts, they can execute any code supported by Sling, so use that only if you know what you're doing.
/* @scr.component * immediate="true" label="ScriptRunner" * description="Runs scripts using the .runscript selector" * * @scr.service * interface="javax.servlet.Servlet" * @scr.property * name="sling.servlet.resourceTypes" * value="sling/servlet/default" * @scr.property * name="sling.servlet.selectors" * value="runscript" * @scr.property * name="sling.servlet.methods" * value="GET" */ public class ScriptRunnerServlet extends SlingAllMethodsServlet { protected void doGet( SlingHttpServletRequest req, SlingHttpServletResponse resp) throws ServletException, IOException { Servlet s = req.getResource().adaptTo(Servlet.class); if(s == null) { throw new ServletException("Resource " + req.getResource() + " does not adapt to a Servlet"); } s.service(req, resp); } }
As I write this, we don't have documentation on how to create more script engines, but that's not too hard to do if you take one of the simple existing engines as an example.
The JRuby engine for example, implemented in the scripting/ruby module, is built out of two simple classes, one that inherits from AbstractSlingScriptEngine, and one that inherits from AbstractScriptEngineFactory. The code is very simple, it's basically only a wrapper around the JRuby engine, that adapts it for Sling.
If creating a script engine, don't forget the META-INF/services/javax.script.ScriptEngineFactory file, which lets scripting subsystem know about the factory class, so that the engine is activated when the bundle that contains it is loaded.
Once the script engine is created, loading its bundle into Sling should be enough to activate scripts having the extension defined by the engine. If several scripts are found with the same name but different script extensions, the priority in selecting them is currently unspecified.
The javascript and freemarker engines source code also shows how to add automated tests to a script engine, including making a JCR repository available to the tests.
To go further, the javascript and jsp script engines are the most interesting ones to study.
The javascript engine provides wrappers to make it easier to access JCR and Sling objects from server-side javascript, and also uses a clever EspReader (sorry it's not that ESP) to convert .esp scripts to plain javascript code.
The jsp engine is actually a compiler, so it can be an interesting example if your language needs or can benefit from compiling.
The Sling Maven Plugin provides an install goal which is able to install or update a bundle in a running Sling application (if the Sling web console is deployed). If the plugin properties are configured accordingly you can just mvn clean package org.apache.sling:maven-sling-plugin:install and the bundle is uploaded.
You can use the settings.xml to set the url to your Sling application. See the Sling Maven Plugin for more information.
Mostly when using the Sling Web Application, that is running Sling inside a web application deployed into some servlet container, you might want to share classes between the servlet container and Sling. Some examples of such sharing are:
For such cases the OSGi Core Specification provides a functionality to declare such class sharing. The functionality is defined in terms of two Framework properties org.osgi.framework.system.packages and org.osgi.framework.bootdelegation:
The problem with the org.osgi.framework.bootdelegation property is, that it completely bypasses any bundle import wirings and just asks the parent classloader. Such situations are not easily recognizable. Therefore the Sling Console will be enhanced to mark any package import which matchs an entry in the org.osgi.framework.bootdelegation appropriately (SLING-148).
Also note, that any package listed as an import in a bundle must be resolveable for the bundle resolve. The import resolution process does not take the org.osgi.framework.bootdelegation configuration into account. This means, that regardless of whether a package is listed in the org.osgi.framework.bootdelegation property or not, if the package is listed as a required import in the Import-Package header, it must be exported by some other bundle.
Sling uses the sling.bootdelegation.class property name prefix to define lists of classes that must be added to the org.osgi.framework.bootdelegation property. In case you want to have a closer look, this is implemented in the org.apache.sling.launcher.app.Sling.resolve() method.
If a Sling property name starts with the sling.bootdelegation.class. prefix, the list of packages defined as the property value is appended to the org.osgi.framework.bootdelegation property, but only if the fully qualified class taken from the rest of the property name exists in the parent class loader.
Here's an example, from the jcr-client.properties file:
sling.bootdelegation.class.javax.jcr.Repository = \ javax.jcr, \ javax.jcr.lock, \ javax.jcr.nodetype, \ javax.jcr.observation, \ javax.jcr.query, \ javax.jcr.util, \ javax.jcr.version
This means that, if the javax.jcr.Repository class is available in the parent class loader, all packages listed will be added to the org.osgi.framework.bootdelegation, making the corresponding classes available to OSGi bundles.
If the property name does not start with this sling.bootdelegation.class. property, the list of packages is just appended to the org.osgi.framework.bootdelegation property.
Currently extending the org.osgi.framework.system.packages property in a Sling configuration file is only possibly by setting the org.apache.sling.launcher.system.packages property. The value of this property, which must start with a comma, is just appended to the org.osgi.framewrok.system.packages property.
A more elaborate support as is supported for the org.osgi.framework.bootdelegation Property has been prepared (SLING-147).
So, what mechanism should be used ? The answer is, that it depends.
Most of the time, you will want to use the org.osgi.framework.system.packages property. Because this property ensures that you will allways benefit from the normal class resolution mechanism through package imports and exports.
This allows creating the bundles normally by having the package import lists being built according to the packages used by the bundle classes. For example you may use the Apache Felix Maven Bundle Plugin to build your OSGi bundles and the imports are automatically calculated (by default).
The drawback of this method is, that there may be bundles in your system, which export packages also listed in the org.osgi.framework.system.packages property. Depending on the export version, the wrong package may be bound. So to prevent such collisions you should not install such bundles.
An example of such a declaration is the Servlet API packages (javax.servlet, javax.servlet.http and javax.servlet.resources). These packages are imported into the OSGi framework by the SlingServlet of the launcher/webapp project as part of the org.osgi.framework.system.packages property. To have this work correctly, no bundle should export the respective packages. In the case of Sling, this means, the org.apache.felix.commons.sling-api bundle must not be installed.
If on the other hand you cannot prevent the installation of such bundles and hence the export of the respective packages, you might want to set the org.osgi.framework.bootdelegation property conditionally as described above in the answer to how this property is supported in Sling. This ensures the property is only set, if the classes are actually available. This should be used as a fall back only, if the org.osgi.framework.system.packages method does not work.