Richard S. Hall | 677f589 | 2007-07-10 15:29:11 +0000 | [diff] [blame] | 1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
Karl Pauls | b26e3f4 | 2008-04-21 22:17:48 +0000 | [diff] [blame^] | 2 | <html><head><title>Apache Felix - Apache Felix Shell Service</title> |
Richard S. Hall | 677f589 | 2007-07-10 15:29:11 +0000 | [diff] [blame] | 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | <link rel="stylesheet" href="apache-felix-shell-service_files/site.css" type="text/css" media="all"> |
Karl Pauls | b26e3f4 | 2008-04-21 22:17:48 +0000 | [diff] [blame^] | 8 | <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. Hall | 677f589 | 2007-07-10 15:29:11 +0000 | [diff] [blame] | 10 | <div class="menu"> |
Karl Pauls | b26e3f4 | 2008-04-21 22:17:48 +0000 | [diff] [blame^] | 11 | <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. Hall | 677f589 | 2007-07-10 15:29:11 +0000 | [diff] [blame] | 26 | <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 |
| 44 | interactive shell that allows you to issue commands to the framework |
| 45 | and to obtain information from it. The OSGi specification does not |
| 46 | define how an OSGi framework should provide this interactivity. Felix |
| 47 | defines a shell service for creating and executing arbitrary commands. |
| 48 | The 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 |
| 60 | and how to create custom commands for it. This document does not |
| 61 | describe how to use the command shell, nor does it describe the |
| 62 | text-based or GUI-based user interfaces that are available for the |
| 63 | shell.</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 |
| 70 | shell service that can have multiple user interface implementations, |
| 71 | all of which are independent from the Felix framework. The shell |
| 72 | service is currently not intended to be sophisticated, rather it is |
| 73 | just a mechanism to execute commands. The shell service maintains a |
| 74 | list of command services, each of which have a unique command name. The |
| 75 | shell 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 |
| 93 | execute available commands. The shell service methods perform the |
| 94 | following 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 |
| 106 | the executeCommand() method. Even though this method is the most |
| 107 | complex, it is still fairly simplistic. The assumption of the shell |
| 108 | service is that a command line will be typed by the user (or perhaps |
| 109 | constructed by a GUI) and passed into it for execution. The shell |
| 110 | service interprets the command line in a very simplistic fashion; it |
| 111 | takes the leading string of characters terminated by a space character |
| 112 | (not including it) and assumes that this leading token is the command |
| 113 | name. Consider the following command line:</p> |
| 114 | |
| 115 | <div class="preformatted"><div class="preformattedContent"> |
Karl Pauls | b26e3f4 | 2008-04-21 22:17:48 +0000 | [diff] [blame^] | 116 | <pre>update 3 http://www.foo.com/bar.jar |
Richard S. Hall | 677f589 | 2007-07-10 15:29:11 +0000 | [diff] [blame] | 117 | </pre> |
| 118 | </div></div> |
| 119 | |
| 120 | <p>The shell service interprets this as an <tt>update</tt> command and |
| 121 | will search for a command service with the same name. If a |
| 122 | corresponding command service is not found, then it will print an error |
| 123 | message to the error print stream. If a corresponding command service |
| 124 | is found, then it will pass the entire command line string and the |
| 125 | print 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 |
| 128 | interface. This is because commands are implemented as OSGi services |
| 129 | and the shell service listens for service events and when a command |
| 130 | service registers/unregisters it automatically updates its list of |
| 131 | commands 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 |
| 138 | services. The advantage of this approach is two-fold: the shell service |
| 139 | can leverage OSGi service events to maintain its list of available |
| 140 | commands and the set available commands is dynamically extendable by |
| 141 | installed 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 |
| 161 | as short as possible (this is used for generating the help command |
| 162 | output).</li> |
| 163 | <li><tt>getShortDescription()</tt> - returns a short |
| 164 | description of the command; this should be one line and as short as |
| 165 | possible (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 <id> [<id> ...]"</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() >= 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> |
| 295 | directory must exist. Once you have compiled all of the above classes, |
| 296 | you need to create a bundle JAR file of the generated package |
| 297 | directory. The bundle JAR file needs a manifest, so create a file |
| 298 | called <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 |
| 302 | Bundle-Description: A 'start' command <span class="code-keyword">for</span> the shell service. |
| 303 | Bundle-Activator: test.MyStartActivator |
| 304 | Bundle-ClassPath: . |
| 305 | Import-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 |
| 316 | includes all of the classes in the test directory inside of the <tt>c:\classes</tt> |
| 317 | directory. Once the bundle JAR file is created, you are ready to add |
| 318 | the command service to the shell service; simply start Felix and |
| 319 | install and start the bundle created by the above command. By doing so, |
| 320 | the 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 |
| 327 | handled by the standard OSGi framework mechanisms. For example, if a |
| 328 | bundle should not be able to register a shell service, then it should |
| 329 | not be given the corresponding service permission. Security handling |
| 330 | may change in future release after some experience is gained through |
| 331 | usage.</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 Pauls | b26e3f4 | 2008-04-21 22:17:48 +0000 | [diff] [blame^] | 340 | </body></html> |