blob: 4a3b60ecfc467f8fbefe178ba127622f928033b3 [file] [log] [blame]
Richard S. Hall677f5892007-07-10 15:29:11 +00001<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
Karl Pauls63100252008-04-21 22:17:48 +00002<html><head><title>Apache Felix - Apache Felix Shell Service</title>
Richard S. Hall677f5892007-07-10 15:29:11 +00003
4
5
6
7 <link rel="stylesheet" href="apache-felix-shell-service_files/site.css" type="text/css" media="all">
Karl Pauls63100252008-04-21 22:17:48 +00008 <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"></head><body>
9 <div class="title"><div class="logo"><a href="http://felix.apache.org/site/index.html"><img alt="Apache Felix" src="apache-felix-shell-service_files/logo.png" border="0"></a></div><div class="header"><a href="http://www.apache.org/"><img alt="Apache" src="apache-felix-shell-service_files/apache.png" border="0"></a></div></div>
Richard S. Hall677f5892007-07-10 15:29:11 +000010 <div class="menu">
Karl Pauls63100252008-04-21 22:17:48 +000011<ul>
12 <li><a href="http://felix.apache.org/site/news.html" title="news">news</a></li>
13 <li><a href="http://felix.apache.org/site/license.html" title="license">license</a></li>
14 <li><span class="nobr"><a href="http://felix.apache.org/site/downloads.cgi" title="Visit page outside Confluence" rel="nofollow">downloads<sup><img class="rendericon" src="apache-felix-shell-service_files/linkext7.gif" alt="" align="absmiddle" border="0" height="7" width="7"></sup></a></span></li>
15 <li><a href="http://felix.apache.org/site/documentation.html" title="documentation">documentation</a></li>
16 <li><a href="http://felix.apache.org/site/mailinglists.html" title="mailinglists">mailing lists</a></li>
17 <li><a href="http://felix.apache.org/site/contributing.html" title="Contributing">contributing</a></li>
18 <li><span class="nobr"><a href="http://www.apache.org/" title="Visit page outside Confluence" rel="nofollow">asf<sup><img class="rendericon" src="apache-felix-shell-service_files/linkext7.gif" alt="" align="absmiddle" border="0" height="7" width="7"></sup></a></span></li>
19 <li><span class="nobr"><a href="http://www.apache.org/foundation/sponsorship.html" title="Visit page outside Confluence" rel="nofollow">sponsorship<sup><img class="rendericon" src="apache-felix-shell-service_files/linkext7.gif" alt="" align="absmiddle" border="0" height="7" width="7"></sup></a></span></li>
20 <li><span class="nobr"><a href="http://www.apache.org/foundation/thanks.html" title="Visit page outside Confluence" rel="nofollow">sponsors<sup><img class="rendericon" src="apache-felix-shell-service_files/linkext7.gif" alt="" align="absmiddle" border="0" height="7" width="7"></sup></a></span>
21<!-- ApacheCon Ad -->
22<iframe src="apache-felix-shell-service_files/button.html" style="border-width: 0pt; float: left;" frameborder="0" height="135" scrolling="no" width="135"></iframe>
23<p style="height: 100px;">
24<!-- ApacheCon Ad -->
25</p></li></ul> </div>
Richard S. Hall677f5892007-07-10 15:29:11 +000026 <div class="main">
27<h1><a name="ApacheFelixShellService-ApacheFelixShellService"></a>Apache Felix Shell Service</h1>
28
29<ul>
30 <li><a href="#ApacheFelixShellService-overview" title="overview on Apache Felix Shell Service">Overview</a></li>
31 <li><a href="#ApacheFelixShellService-service" title="service on Apache Felix Shell Service">How the Shell Service Works</a></li>
32 <li><a href="#ApacheFelixShellService-commands" title="commands on Apache Felix Shell Service">How Commands Work</a></li>
33 <li><a href="#ApacheFelixShellService-creating" title="creating on Apache Felix Shell Service">Creating a Command</a></li>
34 <li><a href="#ApacheFelixShellService-security" title="security on Apache Felix Shell Service">Security and the Shell Service</a></li>
35 <li><a href="#ApacheFelixShellService-feedback" title="feedback on Apache Felix Shell Service">Feedback</a></li>
36</ul>
37
38
39<p><a name="ApacheFelixShellService-overview"></a></p>
40
41<h2><a name="ApacheFelixShellService-Overview"></a>Overview</h2>
42
43<p>In order to interact with Felix it is necessary to have some sort of
44interactive shell that allows you to issue commands to the framework
45and to obtain information from it. The OSGi specification does not
46define how an OSGi framework should provide this interactivity. Felix
47defines a shell service for creating and executing arbitrary commands.
48The shell service does not define a user interface, only a service API.</p>
49
50<p>The benefit of the Felix shell service approach is that it is possible to:</p>
51
52<ul>
53 <li>have multiple shell user interfaces (e.g., textual and graphical),</li>
54 <li>add custom commands to the shell (i.e., bundles can make commands available via the shell service), and</li>
55 <li>use the shell service from other bundles/services.</li>
56</ul>
57
58
59<p>The remainder of this document describes how the shell service works
60and how to create custom commands for it. This document does not
61describe how to use the command shell, nor does it describe the
62text-based or GUI-based user interfaces that are available for the
63shell.</p>
64
65<p><a name="ApacheFelixShellService-service"></a></p>
66
67<h2><a name="ApacheFelixShellService-HowtheShellServiceWorks"></a>How the Shell Service Works</h2>
68
69<p>The Felix shell service is intended to be a simple, but extensible
70shell service that can have multiple user interface implementations,
71all of which are independent from the Felix framework. The shell
72service is currently not intended to be sophisticated, rather it is
73just a mechanism to execute commands. The shell service maintains a
74list of command services, each of which have a unique command name. The
75shell service is defined by the following service interface:</p>
76
77<div class="code"><div class="codeContent">
78<pre class="code-java"><span class="code-keyword">package</span> org.apache.felix.shell;
79
80<span class="code-keyword">public</span> <span class="code-keyword">interface</span> ShellService
81{
82 <span class="code-keyword">public</span> <span class="code-object">String</span>[] getCommands();
83 <span class="code-keyword">public</span> <span class="code-object">String</span> getCommandUsage(<span class="code-object">String</span> name);
84 <span class="code-keyword">public</span> <span class="code-object">String</span> getCommandDescription(<span class="code-object">String</span> name);
85 <span class="code-keyword">public</span> ServiceReference getCommandReference(<span class="code-object">String</span> name);
86 <span class="code-keyword">public</span> void executeCommand(
87 <span class="code-object">String</span> commandLine, PrintStream out, PrintStream err)
88 <span class="code-keyword">throws</span> Exception;
89}</pre>
90</div></div>
91
92<p>Using the shell service interface, it is possible to access and
93execute available commands. The shell service methods perform the
94following functions:</p>
95
96<ul>
97 <li><tt>getCommands()</tt> - returns an array of strings that correspond to the names of the installed shell commands.</li>
98 <li><tt>getCommandUsage()</tt> - returns the command usage string for a particular command name</li>
99 <li><tt>getCommandDescription()</tt> - returns a short description for a particular command name.</li>
100 <li><tt>getCommandReference()</tt> - returns the service reference for a particular command name.</li>
101 <li><tt>executeCommand()</tt> - executes a particular command using the specified command line and print streams.</li>
102</ul>
103
104
105<p>Most of the shell service methods require no explanation except for
106the executeCommand() method. Even though this method is the most
107complex, it is still fairly simplistic. The assumption of the shell
108service is that a command line will be typed by the user (or perhaps
109constructed by a GUI) and passed into it for execution. The shell
110service interprets the command line in a very simplistic fashion; it
111takes the leading string of characters terminated by a space character
112(not including it) and assumes that this leading token is the command
113name. Consider the following command line:</p>
114
115<div class="preformatted"><div class="preformattedContent">
Karl Pauls63100252008-04-21 22:17:48 +0000116<pre>update 3 http://www.foo.com/bar.jar
Richard S. Hall677f5892007-07-10 15:29:11 +0000117</pre>
118</div></div>
119
120<p>The shell service interprets this as an <tt>update</tt> command and
121will search for a command service with the same name. If a
122corresponding command service is not found, then it will print an error
123message to the error print stream. If a corresponding command service
124is found, then it will pass the entire command line string and the
125print streams into the <tt>executeCommand()</tt> method of the command service (for a more detailed description of command services, see the next section).</p>
126
127<p>Notice that there is no method to add commands to the shell service
128interface. This is because commands are implemented as OSGi services
129and the shell service listens for service events and when a command
130service registers/unregisters it automatically updates its list of
131commands accordingly.</p>
132
133<p><a name="ApacheFelixShellService-commands"></a></p>
134
135<h2><a name="ApacheFelixShellService-HowCommandsWork"></a>How Commands Work</h2>
136
137<p>All commands available in the shell service are implemented as OSGi
138services. The advantage of this approach is two-fold: the shell service
139can leverage OSGi service events to maintain its list of available
140commands and the set available commands is dynamically extendable by
141installed bundles. The command service interface is defined as follows:</p>
142
143<div class="code"><div class="codeContent">
144<pre class="code-java"><span class="code-keyword">package</span> org.ungoverned.osgi.service.shell;
145
146<span class="code-keyword">public</span> <span class="code-keyword">interface</span> Command
147{
148 <span class="code-keyword">public</span> <span class="code-object">String</span> getName();
149 <span class="code-keyword">public</span> <span class="code-object">String</span> getUsage();
150 <span class="code-keyword">public</span> <span class="code-object">String</span> getShortDescription();
151 <span class="code-keyword">public</span> void execute(<span class="code-object">String</span> line, PrintStream out, PrintStream err);
152}</pre>
153</div></div>
154
155<p>The semantics of the command service methods are:</p>
156
157<ul>
158 <li><tt>getName()</tt> - returns the name of the command; this must not contain whitespace and must be unique.</li>
159 <li><tt>getUsage()</tt>
160- returns the usage string of the command; this should be one line and
161as short as possible (this is used for generating the help command
162output).</li>
163 <li><tt>getShortDescription()</tt> - returns a short
164description of the command; this should be one line and as short as
165possible (this is used for generating the help command output).</li>
166 <li><tt>execute()</tt> - executes the command's functionality using supplied command line and print streams.</li>
167</ul>
168
169
170<p><a name="ApacheFelixShellService-creating"></a></p>
171
172<h2><a name="ApacheFelixShellService-CreatingaCommand"></a>Creating a Command</h2>
173
174<p>The following example creates a simple version of the <tt>start</tt> command.</p>
175
176<div class="code"><div class="codeContent">
177<pre class="code-java"><span class="code-keyword">package</span> test;
178
179<span class="code-keyword">import</span> java.io.PrintStream;
180<span class="code-keyword">import</span> java.net.URL;
181<span class="code-keyword">import</span> java.net.MalformedURLException;
182<span class="code-keyword">import</span> java.util.StringTokenizer;
183
184<span class="code-keyword">import</span> org.osgi.framework.*;
185<span class="code-keyword">import</span> org.apache.felix.shell.ShellService;
186<span class="code-keyword">import</span> org.apache.felix.shell.Command;
187
188<span class="code-keyword">public</span> class MyStartCommandImpl <span class="code-keyword">implements</span> Command
189{
190 <span class="code-keyword">private</span> BundleContext m_context = <span class="code-keyword">null</span>;
191
192 <span class="code-keyword">public</span> MyStartCommandImpl(BundleContext context)
193 {
194 m_context = context;
195 }
196
197 <span class="code-keyword">public</span> <span class="code-object">String</span> getName()
198 {
199 <span class="code-keyword">return</span> <span class="code-quote">"mystart"</span>;
200 }
201
202 <span class="code-keyword">public</span> <span class="code-object">String</span> getUsage()
203 {
204 <span class="code-keyword">return</span> <span class="code-quote">"mystart &lt;id&gt; [&lt;id&gt; ...]"</span>;
205 }
206
207 <span class="code-keyword">public</span> <span class="code-object">String</span> getShortDescription()
208 {
209 <span class="code-keyword">return</span> <span class="code-quote">"start bundle(s)."</span>;
210 }
211
212 <span class="code-keyword">public</span> void execute(<span class="code-object">String</span> s, PrintStream out, PrintStream err)
213 {
214 StringTokenizer st = <span class="code-keyword">new</span> StringTokenizer(s, <span class="code-quote">" "</span>);
215
216 <span class="code-comment">// Ignore the command name.
217</span> st.nextToken();
218
219 <span class="code-comment">// There should be at least one bundle id.
220</span> <span class="code-keyword">if</span> (st.countTokens() &gt;= 1)
221 {
222 <span class="code-keyword">while</span> (st.hasMoreTokens())
223 {
224 <span class="code-object">String</span> id = st.nextToken().trim();
225
226 <span class="code-keyword">try</span> {
227 <span class="code-object">long</span> l = <span class="code-object">Long</span>.valueOf(id).longValue();
228 Bundle bundle = m_context.getBundle(l);
229 <span class="code-keyword">if</span> (bundle != <span class="code-keyword">null</span>)
230 {
231 bundle.start();
232 }
233 <span class="code-keyword">else</span>
234 {
235 err.println(<span class="code-quote">"Bundle ID "</span> + id + <span class="code-quote">" is invalid."</span>);
236 }
237 } <span class="code-keyword">catch</span> (NumberFormatException ex) {
238 err.println(<span class="code-quote">"Unable to parse id '"</span> + id + <span class="code-quote">"'."</span>);
239 } <span class="code-keyword">catch</span> (BundleException ex) {
240 <span class="code-keyword">if</span> (ex.getNestedException() != <span class="code-keyword">null</span>)
241 err.println(ex.getNestedException().toString());
242 <span class="code-keyword">else</span>
243 err.println(ex.toString());
244 } <span class="code-keyword">catch</span> (Exception ex) {
245 err.println(ex.toString());
246 }
247 }
248 }
249 <span class="code-keyword">else</span>
250 {
251 err.println(<span class="code-quote">"Incorrect number of arguments"</span>);
252 }
253 }
254}</pre>
255</div></div>
256
257<p>A bundle activator class is needed for packaging the command servce; the bundle activator registers the command service in its <tt>start()</tt> method. Note: You do not need one activator per command, a single activator can register any number of commands.</p>
258
259<div class="code"><div class="codeContent">
260<pre class="code-java"><span class="code-keyword">package</span> test;
261
262<span class="code-keyword">import</span> org.osgi.framework.BundleActivator;
263<span class="code-keyword">import</span> org.osgi.framework.BundleContext;
264
265<span class="code-keyword">public</span> class MyStartActivator <span class="code-keyword">implements</span> BundleActivator
266{
267 <span class="code-keyword">private</span> <span class="code-keyword">transient</span> BundleContext m_context = <span class="code-keyword">null</span>;
268
269 <span class="code-keyword">public</span> void start(BundleContext context)
270 {
271 m_context = context;
272
273 <span class="code-comment">// Register the command service.
274</span> context.registerService(
275 org.apache.felix.shell.Command.class.getName(),
276 <span class="code-keyword">new</span> MyStartCommandImpl(m_context), <span class="code-keyword">null</span>);
277 }
278
279 <span class="code-keyword">public</span> void stop(BundleContext context)
280 {
281 <span class="code-comment">// Services are automatically unregistered so
282</span> <span class="code-comment">// we don't have to unregister the factory here.
283</span> }
284}</pre>
285</div></div>
286
287<p>To compile these classes you will need to have the <tt>framework.jar</tt> file on your class path. Compile all of the source files using a command like:</p>
288
289<div class="preformatted"><div class="preformattedContent">
290<pre>java -d c:\classes *.java
291</pre>
292</div></div>
293
294<p>This command compiles all of the source files and outputs the generated class files into a subdirectory of the <tt>c:\classes</tt> directory, called test, named after the package of the source files; for the above command to work, the <tt>c:\classes</tt>
295directory must exist. Once you have compiled all of the above classes,
296you need to create a bundle JAR file of the generated package
297directory. The bundle JAR file needs a manifest, so create a file
298called <tt>manifest.mf</tt> with the following contents:</p>
299
300<div class="code"><div class="codeContent">
301<pre class="code-java">Bundle-Name: My Start Command
302Bundle-Description: A 'start' command <span class="code-keyword">for</span> the shell service.
303Bundle-Activator: test.MyStartActivator
304Bundle-ClassPath: .
305Import-Package: org.apache.felix.shell</pre>
306</div></div>
307
308<p>To create the bundle JAR file, issue the command:</p>
309
310<div class="preformatted"><div class="preformattedContent">
311<pre>jar cfm mystart.jar manifest.mf -C c:\classes test
312</pre>
313</div></div>
314
315<p>This command creates a JAR file using the manifest you created and
316includes all of the classes in the test directory inside of the <tt>c:\classes</tt>
317directory. Once the bundle JAR file is created, you are ready to add
318the command service to the shell service; simply start Felix and
319install and start the bundle created by the above command. By doing so,
320the new <tt>mystart</tt> command is made available via the shell service.</p>
321
322<p><a name="ApacheFelixShellService-security"></a></p>
323
324<h2><a name="ApacheFelixShellService-SecurityandtheShellService"></a>Security and the Shell Service</h2>
325
326<p>The shell service security handling is quite simple, all security is
327handled by the standard OSGi framework mechanisms. For example, if a
328bundle should not be able to register a shell service, then it should
329not be given the corresponding service permission. Security handling
330may change in future release after some experience is gained through
331usage.</p>
332
333<p><a name="ApacheFelixShellService-feedback"></a></p>
334
335<h2><a name="ApacheFelixShellService-Feedback"></a>Feedback</h2>
336
337<p>Subscribe to the Felix users mailing list by sending a message to <span class="nobr"><a href="mailto:users-subscribe@felix.apache.org" title="Send mail to users-subscribe@felix.apache.org" rel="nofollow">users-subscribe@felix.apache.org<sup><img class="rendericon" src="apache-felix-shell-service_files/mail_small.gif" alt="" align="absmiddle" border="0" height="12" width="13"></sup></a></span>; after subscribing, email questions or feedback to <span class="nobr"><a href="mailto:users@felix.apache.org" title="Send mail to users@felix.apache.org" rel="nofollow">users@felix.apache.org<sup><img class="rendericon" src="apache-felix-shell-service_files/mail_small.gif" alt="" align="absmiddle" border="0" height="12" width="13"></sup></a></span>.</p>
338 </div>
339
Karl Pauls63100252008-04-21 22:17:48 +0000340</body></html>