tag:blogger.com,1999:blog-92105665780970475762024-03-13T10:40:34.309-07:00<coderthoughts />David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.comBlogger43125tag:blogger.com,1999:blog-9210566578097047576.post-27230357859431327882018-01-29T14:10:00.000-08:002018-01-30T13:20:37.046-08:00Blockchain Smart Contracts are the new Serverless!<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="text-align: right;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://1.bp.blogspot.com/-AqbgUS9H8WY/Wm-OGt3a9UI/AAAAAAAAD_k/9HoNVNQsAVADMnuBZ3N0AYyuo5eXD9jRACEwYBhgL/s1600/Michael_Bacina_how-smart-contracts-work.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="215" data-original-width="481" height="89" src="https://1.bp.blogspot.com/-AqbgUS9H8WY/Wm-OGt3a9UI/AAAAAAAAD_k/9HoNVNQsAVADMnuBZ3N0AYyuo5eXD9jRACEwYBhgL/s200/Michael_Bacina_how-smart-contracts-work.png" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Smart Contracts (thanks to Michael Bacina)</td></tr>
</tbody></table>
Over the recent past I've been experimenting with Smart Contracts for block chain implementations. Smart Contracts are essentially programs running on the block chain infrastructure. For example Ethereum supports Smart Contracts written in Solidity. EOS is another example of a blockchain that will support smart contracts. I've been looking at EOS an its smart contracts in more detail, here the smart contracts can be written in C/C++ so you don't have to learn a new language for it.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-8TtRjuAJaww/WnDhfzAXmBI/AAAAAAAAEAE/0DLIr0S5amsCsQYbuciOWcuCmczNj3a0wCLcBGAs/s1600/Ethereum.png_1512566634.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="225" data-original-width="225" height="200" src="https://3.bp.blogspot.com/-8TtRjuAJaww/WnDhfzAXmBI/AAAAAAAAEAE/0DLIr0S5amsCsQYbuciOWcuCmczNj3a0wCLcBGAs/s200/Ethereum.png_1512566634.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A CryptoKitty</td></tr>
</tbody></table>
So what can you do with a smart contract? Smart contracts are designed to provide some sort of computation and store the result (immutably) on the blockchain. The computation is a custom program that applies to your domain, so for example you could be renting out holiday accommodation and your website might store the holiday home rental contract including the price, optional extras, insurance etc for a given period of time, after having computed it, on the blockchain. In most cases smart contracts can also handle the payment, so let's say the rental home costs 500 Euro per week, then the equivalent in Ether (ETH) or whatever the current blockchain/crypto is, can be transferred to the owner as part of the smart contract execution.<br />
<div style="text-align: left;">
</div>
Or, more creatively as has been done on the ethereum network, your contract could compute a unique CryptoKitty for you that is a cute looking creature created just for you to look at and store the result on the blockchain.<br />
<br />
You can even take this a little bit further. As shown with the CryptoKitty the smart contract does not need to have anything to do with transferring money from <i>a</i> to <i>b</i> or writing some sort of financial contract. In theory you could use the smart contract to do anything you might be able to use asynchronous computing power for.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
After playing briefly with Solidity, the smart contract language for Ethereum I moved to play a bit more with EOS smart contracts. Why EOS? EOS as a blockchain is still in it's early phase and under heavy development although they do have a test network up and running at this stage. However I find EOS interesting because it has a couple of interesting aspects:<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://eos.io/images/dawn_2.0-logo.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="800" data-original-width="534" height="200" src="https://eos.io/images/dawn_2.0-logo.png" width="133" /></a></div>
<ul>
<li>First of all it's quite easy to run a test EOS node on your own machine which allows you as a developer to play with it and understand it in a sandbox type environment.</li>
<li>EOS aims to provide much higher transaction rates than the current major block chains can provide. It promises up to 50000 transactions per second which all of a sudden is big enough to handle amounts of payments similar to major credit card companies like Visa and Mastercard.</li>
<li>Smart contracts on EOS can be written in C/C++ which is really nice, as you don't need to learn a new programming language for it.</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
So let's take a look at how I got my example EOS contract deployed to my own test EOS node.<br />
<br />
<h3>
Build the EOS code</h3>
I tried this on Ubuntu 16.04. Compile the EOS code base:<br />
<br />
First, clone the EOS code<br />
<span style="font-family: "courier new" , "courier" , monospace;"> ~$ git clone https://github.com/eosio/eos ~/eos<br /> </span><span style="font-family: "courier new" , "courier" , monospace;">~$ </span><span style="font-family: "courier new" , "courier" , monospace;">cd eos</span></div>
<div>
<br /></div>
<div>
Then build the whole lot:</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> ~/eos$ ./build.sh ubuntu full</span><br />
This takes a while but once it's finished you should be able to run your EOS node:<br />
<span style="font-family: "courier new" , "courier" , monospace;"> ~/eos/build/programs/eosiod$ ./</span><span style="font-family: "courier new" , "courier" , monospace;">eosiod</span><br />
<br />
It now exits with an error. You need to set up <span style="font-family: "courier new" , "courier" , monospace;">data-dir/config.ini</span> as described in the EOS docs: <a href="https://github.com/EOSIO/eos">https://github.com/EOSIO/eos</a><br />
<br />
At this point your EOS node should be happily up and running. Which is really neat. You've got a EOS block chain node running for development purposes on your local machine!<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">~/eos/build/programs/eosiod $ ./eosiod </span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-5ozj96C3noA/Wm-Q9Yn2tpI/AAAAAAAAD_0/rx2h9zXzJMkqNdqew15Hj28OxelKm7OBwCEwYBhgL/s1600/eosnode.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="282" data-original-width="866" height="130" src="https://4.bp.blogspot.com/-5ozj96C3noA/Wm-Q9Yn2tpI/AAAAAAAAD_0/rx2h9zXzJMkqNdqew15Hj28OxelKm7OBwCEwYBhgL/s400/eosnode.jpg" width="400" /></a></div>
<br />
<br />
Before you can use your EOS node you need to create a wallet and an account. Since our smart contract will be computing the Fibonacci sequence, I'm going to call the account <b>fibonacci</b>. The following commands do that for you. They use a demo account <b>inita</b> that is created in the <span style="font-family: "courier new" , "courier" , monospace;">config.ini</span> file when its set up as above.<br />
<br />
Here we use the <span style="font-family: "courier new" , "courier" , monospace;">eosioc</span> program which is a client to the EOS network:<br />
<span style="font-family: "courier new" , "courier" , monospace;">~/eos/build/programs/eosioc $ ./eosioc wallet create</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">~/eos/build/programs/eosioc $ ./eosioc wallet open</span><br />
<br />
Import the inita demo key:<br />
<span style="font-family: "courier new" , "courier" , monospace;">~/eos/build/programs/eosioc $ ./eosioc wallet import 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3</span></div>
<div>
<br /></div>
<div>
Create two private keys for the fibonacci account:<br />
<div>
<span style="font-family: "courier new" , "courier" , monospace;">~/eos/build/programs/eosioc $ ./eosioc create key</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">Private key:###</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">Public key: ###</span></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">~/eos/build/programs/eosioc $ ./eosioc create key</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">Private key:###</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">Public key: ###</span></div>
</div>
<div>
<br /></div>
Create the fibonacci account:<br />
<span style="font-family: "courier new" , "courier" , monospace;">~/eos/build/programs/eosioc $ ./eosioc create account inita fibonacci {private key1} {private key2}</span><br />
<br />
<h3>
Create the smart contract</h3>
I created a little testproject called <i>fibonacci</i> which computes the fibonacci sequence to a certain iteration in the EOS smart contract and stores the result in the EOS database.<br />
<br />
The code can be found in github here: <a href="https://github.com/coderthoughts/fibonacci">https://github.com/coderthoughts/fibonacci</a><br />
<br />
It exists of two components: an external contract, in the <a href="https://github.com/coderthoughts/fibonacci/blob/master/fibonacci.abi">fibonacci.abi file</a> which defines how the application communicates with the outside world. The actual communication typically happens in JSON:</div>
<div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "structs": [{</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "name": "compute",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "base": "",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "fields": {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "iterations": "uint64"</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> },{</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">"name": "result",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">"base": "",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">"fields": {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">"id": "name",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">"value": "uint64"</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }],</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "actions": [{</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "action_name": "compute",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "type": "compute"</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }],</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "tables": [{</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "table_name": "results",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "type": "result",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "index_type": "i64",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "key_names": ["id"],</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "key_types": ["name"]</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }]</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span></div>
<br />
And there is the <a href="https://github.com/coderthoughts/fibonacci/blob/master/fibonacci.cpp">fibonacci.cpp file</a> that contains the source code in C++ of the contract. The main bit of the C++ contract is just the apply() method that gets invoked when the EOS Smart Contract receives a message:</div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> uint64_t fibonacci(uint64_t iterations) {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> uint64_t first = 0;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> uint64_t second = 1;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (iterations == 0)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> return 0;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> eosio::print("1 ");</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> for (uint64_t i=1; i < iterations; i++) {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> uint64_t res = first + second;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> first = second;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> second = res;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> eosio::print(res, " ");</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> return second;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> /// The apply method implements the dispatch of events to this contract</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> void apply(uint64_t code, uint64_t action) {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (action == N(compute)) {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> auto message = eosio::current_message<compute>();</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> eosio::print("Calling fibonacci\n");</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> uint64_t num = fibonacci(message.iterations);</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> result res(eosio::name(code), num);</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Results::store(res, res.id);</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> eosio::print("Stored result in database\n");</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span></div>
</div>
<div>
<br /></div>
<div>
There are also specific EOS libraries available, the documentation is here: <a href="https://eosio.github.io/eos/group__contractdev.html">https://eosio.github.io/eos/group__contractdev.html</a></div>
<div>
<br />
<h3>
Deploy your own smart contract</h3>
An interesting part of the EOS smart contract development lifecycle is that these contract don't get compiled in regular machine language, what C++ compilers normally do, but it gets compiles into a WebAssembly .wast file. This file is some sort of assembler language but then platform independent and this is what EOS uses at runtime. <br />
<br />
Once deployed, you can execute your contract by sending a message to it. The contract executes and can write its output to a database location for later retrieval by a client.<br />
<br />
The easiest way to compile the fibonacci source is to put the files with the other example smart contracts in the EOS codebase, in the <i>contracts </i>directory. Then you've got everything in the path as the compiler expects it:<br />
<br />
Compile the project:<br />
<span style="font-family: "courier new" , "courier" , monospace;">~/eos/contracts$ ../build/tools/eoscpp -o fibonacci/fibonacci.wast fibonacci/fibonacci.cpp</span><br />
Upload the fibonacci smart contract to the EOS node:<br />
<span style="font-family: "courier new" , "courier" , monospace;">~/eos/contracts$ eosioc set contract fibonacci fibonacci/fibonacci.wast fibonacci/fibonacci.abi</span><br />
Now we can start executing it. Lets run the fibonacci compute for 8 iterations and store the result:<br />
<span style="font-family: "courier new" , "courier" , monospace;">~/eos/contracts$ eosioc push message fibonacci compute '{"iterations":8}' -S fibonacci</span><br />
<br />
On the EOS demon console you can see some debug output from the smart contract<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">> Calling fibonacci<br />> 1 1 2 3 5 8 13 21 Stored result</span><br />
However a real user will obviously never be able to see this. So to obtain the result of the computation, we'll look it up in the EOS database:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">/build/programs/eosioc$ ./eosioc get table fibonacci fibonacci </span><span style="font-family: "courier new" , "courier" , monospace;">results </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"></span><br />
<div>
<span style="font-family: "courier new" , "courier" , monospace;">{</span></div>
<span style="font-family: "courier new" , "courier" , monospace;">
<div>
"rows": [{</div>
<div>
"id": "fibonacci",</div>
<div>
"value": 21</div>
<div>
}</div>
<div>
],</div>
<div>
"more": false</div>
<div>
}</div>
<div>
<br /></div>
</span><br />
So the result is 21. We've executed our smart contract and obtained the result on the EOS blockchain!</div>
<div>
<br />
<h3>
Conclusion</h3>
I'm pretty excited by the possibilities of smart contracts with regard to the possibilities that these, once matured, can provide. It becomes similar to what today is labeled 'serverless' computing. Things that are at the moment possible through large providers such as Amazon Lambda and Microsoft Azure Functions will also be provided via block chain networks. One difference is that the computation is not done by a single cloud entity, but rather by a collection of nodes that are run by individuals who have mining machines for that crypto. In my eyes it's still early days and certain things in the contracts can certainly be improved, e.g. the available APIs usable from within the smart contracts are still fairly limited, but that will probably improve over time. The fun thing is: it's pretty easy to get started experimenting and writing smart contracts, even from a simple Linux box, so you can learn and develop your smart contracts while the blockchain teams are working on maturing the infrastructure.<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Times; -webkit-text-stroke: #000000}
p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Times; -webkit-text-stroke: #000000; min-height: 14.0px}
span.s1 {font-kerning: none}
</style></div>
</div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com0tag:blogger.com,1999:blog-9210566578097047576.post-59344697121706870102016-03-01T00:35:00.002-08:002016-03-01T00:45:24.392-08:00Adding Aspects to OSGi Services with no Bytecode Weaving<div class="separator" style="clear: both; text-align: center;">
</div>
In <a href="https://coderthoughts.blogspot.com/2016/02/branch-by-abstraction-and-osgi.html" target="_blank">my previous post</a> I looked at how easy it is to apply the 'branch by abstraction' pattern to OSGi services, where the Service API is the abstraction layer and you can branch at runtime without even taking down the service client. <br />
Another part of the story here is that it can be useful to add aspects to an implementation. This can also be done using the branch by abstraction pattern and in OSGi you can add these aspects to existing services without the need to modify them, and also without the need for bytecode manipulation. In OSGi you can proxy services by hiding the original and placing a proxy service in the service registry that consumers will use instead. This makes it possible to add a chain of aspects to the service invocations without the need to either change the clients nor the services themselves. <br />
<br />
To illustrate, I started a little project called <a href="https://github.com/coderthoughts/service-spector" target="_blank">service-spector</a> that allows you to add service aspects to any kind of service. The aspects themselves are also implemented as services. Here's what a <a href="https://github.com/coderthoughts/service-spector/blob/master/src/main/java/org/coderthoughts/service/spector/ServiceAspect.java" target="_blank">ServiceAspect</a> looks like: <br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>public interface ServiceAspect { </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> String announce(); </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> void preServiceInvoke(ServiceReference service, Method method, Object[] args); </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> void postServiceInvoke(ServiceReference service, Method method, Object[] args, Object result); </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> String report(); </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>} </b></span><br />
<br />
Aspects are called before and after a service invocation is done via <b><span style="font-family: "courier new" , "courier" , monospace;">preServiceInvoke()</span></b>/<b><span style="font-family: "courier new" , "courier" , monospace;">postServiceInvoke()</span></b>. They are called with a reference to the service being invoked and with the parameters that are provided to the service. The post call is also provided with the result of the invocation. The aspect API allows you to do things like obtaining metrics for services, add logging to existing service calls or anything else really that an aspect could do. Additionally it contains <span style="font-family: "courier new" , "courier" , monospace;"><b>annouce()</b></span> and <b><span style="font-family: "courier new" , "courier" , monospace;">report()</span></b> to present information to the user.<br />
<br />
The service-spector is configured using <a href="https://osgi.org/javadoc/r6/cmpn/org/osgi/service/cm/package-summary.html" target="_blank">Configuration Admin</a> with a list of filters for services that need have the aspects applied in the <b><span style="font-family: "courier new" , "courier" , monospace;">service.filters</span></b> property. There is an additional configuration item that states whether the original services need to be hidden. The proxies are automatically registered with a higher service ranking than the original service. If the consumer of the original only uses a single service the original does not need to be hidden as the one with the higher ranking will be selected. However, if a consumer uses all the services of a certain type you do want to hide the originals. In general it's safer to hide the original service, but if you want to run without the <a href="https://github.com/coderthoughts/service-spector/blob/master/src/main/java/org/coderthoughts/service/spector/impl/HidingHook.java" target="_blank">HidingHooks</a> you can switch this off.<br />
<br />
I am using the <a href="http://felix.apache.org/documentation/subprojects/apache-felix-file-install.html" target="_blank">Felix File Install</a> <link></link> to provide the configuration and have it stored in a file in <b><span style="font-family: "courier new" , "courier" , monospace;">load/org.coderthoughts.service.spector.impl.ServiceSpector.config</span></b> A little known feature of File Install is that is supports typed configuration data in .config files, so the following file contains a Boolean <b><span style="font-family: "courier new" , "courier" , monospace;">hide.services</span></b> property and a String[] <b><span style="font-family: "courier new" , "courier" , monospace;">service.filters</span></b> property. <br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>hide.services=B"true" </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>service.filters=["(objectClass\=org.coderthoughts.primes.service.PrimeNumberService)",</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> "(objectClass\=org.apache.felix.bundlerepository.RepositoryAdmin)"] </b></span><br />
<br />
In the <a href="https://github.com/coderthoughts/service-spector/blob/master/src/main/java/org/coderthoughts/service/spector/impl/ServiceSpector.java" target="_blank">ServiceSpector main component</a> I can easily consume this configuration using the new Declarative Services annotation based approach:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>@Component(immediate=true) </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>public class ServiceSpector implements ServiceListener { </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> // This API is populated by Config Admin with configuration information </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> @interface Config { </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> String [] service_filters(); </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> boolean hide_services() default false; </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> } </b></span><br />
<span style="font-size: small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>... </b></span><br />
<span style="font-size: small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> @Activate </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> private synchronized void activate(BundleContext bc, Config cfg) { </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> ... </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> System.out.println("Service filters: " +</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> Arrays.toString(cfg.service_filters())); </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> System.out.println("Hide services: " + cfg.hide_services());</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b> } </b></span><br />
<br />
The <span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>Config</b></span> annotation is automatically populated with the configuration information by matching the property keys with the annotation methods, which I can easily read out using its typed API (the dots in configuration keys are mangled to underscores).<br />
<br />
Service-spector has a <a href="https://osgi.org/javadoc/r6/core/org/osgi/framework/ServiceListener.html" target="_blank">Service Listener</a> that looks for services appearing and checks if they need to have aspects applied. If so they will be proxied. Then, when the proxied service is invoked, all the ServiceAspect services are called to let them do their thing. For example the <a href="https://github.com/coderthoughts/service-spector/blob/master/src/main/java/org/coderthoughts/service/spector/impl/CountingServiceAspect.java" target="_blank">CountingServiceAspect</a> simply counts how often a certain API is invoked:<string longadder=""><string longadder=""> </string></string><br />
<br />
<string longadder=""><string longadder=""><b><span style="font-family: "courier new" , "courier" , monospace;">@Component(scope=ServiceScope.PROTOTYPE)<br />public class CountingServiceAspect implements ServiceAspect {<br /> Map<String, LongAdder> invocationCounts = new ConcurrentHashMap<>();<br /><br /> @Override<br /> public String announce() {<br /> return "Invocation counting started.\n";<br /> }<br /><br /> @Override<br /> public void preServiceInvoke(ServiceReference service, Method method, Object[] args) {<br /> Class declaringClass = method.getDeclaringClass();<br /> String key = declaringClass.getSimpleName() + "#" + method.getName();<br /> invocationCounts.computeIfAbsent(key, k -> new LongAdder()).increment();<br /> }<br /><br /> @Override<br /> public void postServiceInvoke(ServiceReference service, Method method, Object[] args, Object result){<br /> // nothing to do<br /> }<br /><br /> @Override<br /> public String report() {<br /> StringBuilder sb = new StringBuilder();<br /> sb.append("Invocation counts\n");<br /> sb.append("=================\n");<br /> for (Map.Entry<String, LongAdder> entry : invocationCounts.entrySet()) {<br /> sb.append(entry.getKey() + ": " + entry.getValue() + "\n");<br /> }<br /> return sb.toString();<br /> }<br />}</span></b> </string></string><br />
<br />
<string longadder=""><string longadder="">The CountingServiceAspect is included in the Service Spector bundle. Lets add this and the <a href="http://www-us.apache.org/dist//felix/org.apache.felix.fileinstall-3.5.2.jar" target="_blank">File Install</a> and <a href="http://www-us.apache.org/dist//felix/org.apache.felix.configadmin-1.8.8.jar" target="_blank">Configuration Admin</a> bundles to the framework setup used in <a href="https://coderthoughts.blogspot.com/2016/02/branch-by-abstraction-and-osgi.html" target="_blank">the previous post</a> that computes primes. I now have the following bundles installed. I removed bundle 9, the incorrect prime number generator that returns only 1's for now. <br /><br /><span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>g! lb <br />START LEVEL 1 <br /> ID|State |Level|Name <br /> 0|Active | 0|System Bundle (5.4.0)|5.4.0 <br /> 1|Active | 1|Apache Felix Bundle Repository (2.0.6)|2.0.6 <br /> 2|Active | 1|Apache Felix Gogo Command (0.16.0)|0.16.0 <br /> 3|Active | 1|Apache Felix Gogo Runtime (0.16.2)|0.16.2 <br /> 4|Active | 1|Apache Felix Gogo Shell (0.10.0)|0.10.0 <br /> 5|Active | 1|Apache Felix Declarative Services (2.0.2)|2.0.2 <br /> 6|Active | 1|service (1.0.0.SNAPSHOT)|1.0.0.SNAPSHOT <br /> 7|Active | 1|impl (1.0.1.SNAPSHOT)|1.0.1.SNAPSHOT <br /> 8|Installed | 1|client (1.0.0.SNAPSHOT)|1.0.0.SNAPSHOT <br /> 10|Active | 1|Apache Felix Configuration Admin Service (1.8.8)|1.8.8 <br /> 11|Active | 1|Apache Felix File Install (3.5.2)|3.5.2 <br /> 12|Active | 1|service-spector (1.0.0.SNAPSHOT)|1.0.0.SNAPSHOT </b></span></string></string><br />
<br />
<string longadder=""><string longadder="">Additionally I have the configuration file from above in the <span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>./load/org.coderthoughts.service.spector.impl.ServiceSpector.config</b></span> location. This is the default location where the Felix File Install looks. When I start the service-spector bundle it reports the active aspects and its configuration: <br /><br /><span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>g! start 12<br />Invocation counting started. <br /><br />Service Spector <br />=============== <br />Service filters: [(objectClass=org.coderthoughts.primes.service.PrimeNumberService), (objectClass=org.apache.felix.bundlerepository.RepositoryAdmin)] <br />Hide services: true </b></span></string></string><br />
<br />
<string longadder=""><string longadder="">Ok so let's start our client: <br /><span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>g! start 8 <br />Services now: $Proxy6(PrimeNumbers) <br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 <br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 <br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 <br />g! stop 8</b></span></string></string><br />
<br />
<string longadder=""><string longadder="">The version of primes on master has a PrimeServiceReporter <link></link> that reports the name and the classname of the service. Here we can see that the service calls itself "PrimeNumbers" but that the actual service instance is a Proxy. <br /><br />As our filters state that we are also proxying the Repository Admin which comes with the Felix implementation we can also exercise that, for example via the shell commands: <br /> </string></string><br />
<string longadder=""><string longadder=""><b><span style="font-family: "courier new" , "courier" , monospace;">g! obr:repos list<br />... some output ...<br />g! obr:list<br />... some output</span></b></string></string><string longadder=""><string longadder=""> </string></string><br />
<br />
<string longadder=""><string longadder="">Ok so we've now used both of the services that are being proxied. Let's see what APIs have been utilized. The service spector calls <span style="font-family: "courier new" , "courier" , monospace;"><b>report()</b></span> on all the ServiceAspects when the bundle is being stopped:<br /><span style="font-size: small;"><br /></span></string></string><span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>g! stop 12</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>Invocation counts</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>=================</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>PrimeNumberService#getServiceName: 1</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>PrimeNumberService#nextPrime: 30</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>RepositoryAdmin#listRepositories: 1</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><b>RepositoryAdmin#discoverResources: 2</b></span><br />
<br />
<h3>
<string longadder=""><string longadder="">Contributing more aspects</string></string></h3>
Aspects are contributed as services themselves so they don't need to be part of the service-spector bundle itself. The <a href="https://github.com/coderthoughts/service-spector-method-timer" target="_blank">service-spector-method-timer</a> bundle is a separate bundle that contributes a ServiceAspect that provides average method invocations times.<br />
<br />
<h3>
<string longadder=""><string longadder="">Get the binaries</string></string></h3>
<string longadder=""><string longadder="">The code in this blog post can easily be built using maven, but if you just want the binaries, you can get them from here:</string></string><br />
<string longadder=""><string longadder="">service-spector: <a href="https://github.com/coderthoughts/service-spector/releases">https://github.com/coderthoughts/service-spector/releases</a></string></string><br />
method-timer: <a href="https://github.com/coderthoughts/service-spector-method-timer/releases">https://github.com/coderthoughts/service-spector-method-timer/releases</a>David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com2tag:blogger.com,1999:blog-9210566578097047576.post-83272516943477317382016-02-08T02:02:00.000-08:002016-02-08T02:02:01.587-08:00Branch by Abstraction and OSGiInspired by my friend Philipp Suter who pointed me at this wired article <a href="http://www.wired.com/2016/02/rebuilding-modern-software-is-like-rebuilding-the-bay-bridge">http://www.wired.com/2016/02/rebuilding-modern-software-is-like-rebuilding-the-bay-bridge</a> which relates to Martin Fowler's <a href="http://martinfowler.com/bliki/BranchByAbstraction.html" target="_blank">Branch by Abstraction</a> I was thinking: how would this work in an OSGi context?<br />
<br />
Leaving aside the remote nature of the problem for the moment, let's focus on the pure API aspect here. Whether remote or not really orthogonal... I'll work through this with example code that can be found here: <a href="https://github.com/coderthoughts/primes">https://github.com/coderthoughts/primes</a><br />
<br />
Let's say you have an implementation to compute prime numbers:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>public class PrimeNumbers {<br /> public int nextPrime(int n) {<br /> // computes next prime after n - see </b></span><span style="font-family: "courier new" , "courier" , monospace;"><b>https://github.com/coderthoughts/primes </b></span><b style="font-family: 'courier new', courier, monospace;">details</b><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> return p;<br /> }<br />}</b></span></blockquote>
And a client program that regularly uses the prime number generator. I have chosen a client that runs in a loop to reflect a long-running program, similar to a long-running process communicating with a microservice:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b> public class PrimeClient {<br /> private PrimeNumbers primeGenerator = new PrimeNumbers();<br /> private void start() {<br /> new Thread(() -> {<br /> while (true) {<br /> System.out.print("First 10 primes: ");<br /> for (int i=0, p=1; i<10; i++) {<br /> if (i > 0) System.out.print(", ");<br /> p = primeGenerator.nextPrime(p);<br /> System.out.print(p);<br /> }<br /> System.out.println();<br /> try { Thread.sleep(1000); } catch (InterruptedException ie) {}<br /> }<br /> }).start();<br /> }</b></span> </blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b> public static void main(String[] args) {<br /> new PrimeClient().start();<br /> }<br />}</b></span></blockquote>
If you have the source code cloned or forked using git, you can run this example easily by checking out the <b>stage1</b> branch and using Maven:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>.../primes> git checkout stage1<br />.../primes> mvn clean install<br />... maven output<br />[INFO] ------------------------------------------------<br />[INFO] BUILD SUCCESS<br />[INFO] ------------------------------------------------</b></span></blockquote>
<div>
Then run it from the <b style="font-family: 'Courier New', Courier, monospace; font-size: small;">client</b> submodule:</div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>.../primes/client> mvn exec:java -Dexec.mainClass=\<br />org.coderthoughts.primes.client.PrimeClient<br />... maven output<br /> First 10 primes: 3, 5, 7, 11, 13, 17, 19, 23, 29, 31<br />First 10 primes: 3, 5, 7, 11, 13, 17, 19, 23, 29, 31<br />First 10 primes: 3, 5, 7, 11, 13, 17, 19, 23, 29, 31<br />... and so on ...</b></span></blockquote>
<div>
<div>
Ok so our system works. It keeps printing out prime numbers, but as you can see there is a bug in the output. We also want to replace it in the future with another implementation. This is what the <a href="http://martinfowler.com/bliki/BranchByAbstraction.html" target="_blank">Branch by Abstraction Pattern</a> is about.<br />
<br />
In this post I will look at how to do this with OSGi Services. OSGi Services are just POJOs registered in the OSGi Service Registry. OSGi Services are dynamic, they can come and go, and OSGi Service Consumers dynamically react to these changes, as well see. In the following few steps will change the implementation to an OSGi Service. Then we'll update the service at runtime to fix the bug above, without even stopping the service consumer. Finally we can replace the service implementation with a completely different implementation, also without even stopping the client.<br />
<br />
<h3>
Turn the application into OSGi bundles</h3>
We'll start by turning the program into an OSGi program that contains 2 bundles: the client bundle and the impl bundle. We'll use the Apache Felix OSGi Framework and also use OSGi Declarative Services which provides a nice dependency injection model to work with OSGi Services.<br />
<br />
You can see all this on the git branch called <b>stage2</b>:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>.../primes> git checkout stage2<br />.../primes> mvn clean install</b></span></blockquote>
The Client code is quite similar to the original client, except that it now contains some annotations to instruct DS to start and stop it. Also the PrimeNumbers class is now injected instead of directly constructed via the @Reference annotation. The <i>greedy</i> policyOption instructs the injector to re-inject if a better match becomes available:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b> @Component<br />public class PrimeClient {<br /> @Reference(policyOption=ReferencePolicyOption.GREEDY)<br /> private PrimeNumbers primeGenerator;<br /> private volatile boolean keepRunning = false;</b></span> </blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b> @Activate<br /> private void start() {<br /> keepRunning = true;<br /> new Thread(() -> {<br /> while (keepRunning) {<br /> System.out.print("First 10 primes: ");<br /> for (int i=0, p=1; i<10; i++) {<br /> if (i > 0) System.out.print(", ");<br /> p = primeGenerator.nextPrime(p);<br /> System.out.print(p);<br /> }<br /> System.out.println();<br /> try { Thread.sleep(1000); } catch (InterruptedException ie) {}<br /> }<br /> }).start();<br /> }</b></span> </blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b> @Deactivate<br /> private void stop() {<br /> keepRunning = false;<br /> }<br />}</b></span></blockquote>
<div>
The prime generator implementation code is the same except for an added annotation. We register the implementation class in the Service Registry so that it can be injected into the client:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>@Component(service=PrimeNumbers.class)<br />public class PrimeNumbers {<br /> public int nextPrime(int n) {<br /> // computes next prime after n<br /> return p;<br /> }<br />}</b></span></blockquote>
As its now an OSGi application, we run it in an OSGi Framework. I'm using the <a href="http://felix.apache.org/downloads.cgi" target="_blank">Apache Felix Framework</a> version 5.4.0, but any other OSGi R6 compliant framework will do.<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>> java -jar bin/felix.jar<br />g! start http://www.eu.apache.org/dist/felix/org.apache.felix.scr-2.0.2.jar<br />g! start file:/.../clones/primes/impl/target/impl-0.1.0-SNAPSHOT.jar<br />g! install file:/.../clones/primes/client/target/client-0.1.0-SNAPSHOT.jar</b></span></blockquote>
<span id="goog_1579733752"></span>Now you should have everything installed that you need:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>g! lb<br />START LEVEL 1<br /> ID|State |Level|Name<br /> 0|Active | 0|System Bundle (5.4.0)|5.4.0<br /> 1|Active | 1|Apache Felix Bundle Repository (2.0.6)|2.0.6<br /> 2|Active | 1|Apache Felix Gogo Command (0.16.0)|0.16.0<br /> 3|Active | 1|Apache Felix Gogo Runtime (0.16.2)|0.16.2<br /> 4|Active | 1|Apache Felix Gogo Shell (0.10.0)|0.10.0<br /> 5|Active | 1|Apache Felix Declarative Services (2.0.2)|2.0.2<br /> 6|Active | 1|impl (0.1.0.SNAPSHOT)|0.1.0.SNAPSHOT<br /> 7|Installed | 1|client (0.1.0.SNAPSHOT)|0.1.0.SNAPSHOT</b></span></blockquote>
We can start the client bundle:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>g! start 7<br />First 10 primes: 3, 5, 7, 11, 13, 17, 19, 23, 29, 31<br />First 10 primes: 3, 5, 7, 11, 13, 17, 19, 23, 29, 31<br />... and so on ..</b></span></blockquote>
You can now also stop the client:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>g! stop 7</b></span></blockquote>
Great - our OSGi bundles work :)<br />
Now we'll do what Martin Fowler calls creating <i>the abstraction layer</i>.<br />
<br />
<h3>
Introduce the Abstraction Layer: the OSGi Service</h3>
<div>
Go to the branch <b>stage3</b> for the code:</div>
<div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>.../primes> git checkout stage3<br />.../primes> mvn clean install</b></span></blockquote>
</div>
<div>
The abstraction layer for the Branch by Abstraction pattern is provided by an interface that we'll use as a service interface. This interface is in a new maven module that creates the <b>service</b> OSGi bundle.</div>
<div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b><span class="s1">public</span> <span class="s1">interface</span> PrimeNumberService {<br /> <span class="s1">int</span> nextPrime(<span class="s1">int</span> <span class="s2">n</span>);<br />}</b></span></blockquote>
</div>
<div>
Well turn our Prime Number generator into an OSGi Service. The only difference here is that our PrimeNumbers implementation now implements the PrimeNumberService interface. Also the @Component annotation does not need to declare the service in this case as the component implements an interface it will automatically be registered as a service under that interface:</div>
<div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>@Component<br /><span class="s1">public</span> <span class="s1">class</span> PrimeNumbers <span class="s1">implements</span> PrimeNumberService {<br /> public int nextPrime(int n) {<br /> // computes next prime after n<br /> return p;<br /> }<br />}</b></span></blockquote>
</div>
<div>
Run everything in the OSGi framework. The result is still the same but now the client is using the OSGi Service:</div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>g! lb<br />START LEVEL 1<br /> ID|State |Level|Name<br /> 0|Active | 0|System Bundle (5.4.0)|5.4.0<br /> 1|Active | 1|Apache Felix Bundle Repository (2.0.6)|2.0.6<br /> 2|Active | 1|Apache Felix Gogo Command (0.16.0)|0.16.0<br /> 3|Active | 1|Apache Felix Gogo Runtime (0.16.2)|0.16.2<br /> 4|Active | 1|Apache Felix Gogo Shell (0.10.0)|0.10.0<br /> 5|Active | 1|Apache Felix Declarative Services (2.0.2)|2.0.2<br /> 6|Active | 1|service (1.0.0.SNAPSHOT)|1.0.0.SNAPSHOT<br /> 7|Active | 1|impl (1.0.0.SNAPSHOT)|1.0.0.SNAPSHOT<br /> 8|Resolved | 1|client (1.0.0.SNAPSHOT)|1.0.0.SNAPSHOT<br />g! start 8<br />First 10 primes: 3, 5, 7, 11, 13, 17, 19, 23, 29, 31</b></span></blockquote>
You can introspect your bundles too and see that the client is indeed wired to the service provided by the service implementation:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>g! inspect cap * 7<br />org.coderthoughts.primes.impl [7] provides:<br />-------------------------------------------<br />...<br />service; org.coderthoughts.primes.service.PrimeNumberService with properties:<br /> component.id = 0<br /> component.name = org.coderthoughts.primes.impl.PrimeNumbers<br /> service.bundleid = 7<br /> service.id = 22<br /> service.scope = bundle<br /> Used by:<br /> org.coderthoughts.primes.client [8]</b></span></blockquote>
Great - now we can finally fix that annoying bug in the service implementation: that it missed 2 as a prime! While we're doing this we'll just keep the bundles in the framework running...<br />
<br />
<h3>
Fix the bug in the implementation whitout stopping the client</h3>
The prime number generator is fixed in the code in <b>stage4</b>:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>.../primes> git checkout stage4<br />.../primes> mvn clean install</b></span></blockquote>
<div>
It's a small change to the <b>impl</b> bundle. The service interface and the client remain unchanged. Let's update our running application with the fixed bundle:</div>
<div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>First 10 primes: 3, 5, 7, 11, 13, 17, 19, 23, 29, 31<br />First 10 primes: 3, 5, 7, 11, 13, 17, 19, 23, 29, 31<br />First 10 primes: 3, 5, 7, 11, 13, 17, 19, 23, 29, 31<br />g! update 7 file:/.../clones/primes/impl/target/impl-1.0.1-SNAPSHOT.jar<br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29<br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29<br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29</b></span></blockquote>
</div>
<div>
Great - finally our service is fixed! And notice that the client did not need to be restarted! The DS injection, via the @Reference annotation, handles all of the dynamics for us! The client code simply uses the service as a POJO.</div>
<div>
<br /></div>
<h3>
The branch: change to an entirely different service implementation without client restart</h3>
<div>
Being able to fix a service without even restarting its users is already immensely useful, but we can go even further. I can write an entirely new and different service implementation and migrate the client to use that without restarting the client, using the same mechanism. </div>
<div>
<br /></div>
<div>
This code is on the branch <b>stage5</b> and contains a new bundle <b>impl2</b> that provides an implementation of the PrimeNumberService that always returns 1. </div>
<div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>.../primes> git checkout stage5<br />.../primes> mvn clean install</b></span></blockquote>
</div>
<div>
While the impl2 implementation obviously does not produce correct prime numbers, it does show how you can completely change the implementation. In the real world a totally different implementation could be working with a different back-end, a new algorithm, a service migrated from a different department etc...<br />
<br />
Or alternatively you could do a façade service implementation that round-robins across a number of back-end services or selects a backing service based on the features that the client should be getting.<br />
In the end the solution will always end up being an alternative Service in the service registry that the client can dynamically switch to.<br />
<br />
So let's start that new service implementation:</div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29<br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29<br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29<br />g! start file:/.../clones/primes/impl2/target/impl2-1.0.0-SNAPSHOT.jar<br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29<br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29<br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29<br />g! stop 7<br />First 10 primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29<br />First 10 primes: 1, 1, 1, 1, 1, 1, 1, 1, 1, 1<br />First 10 primes: 1, 1, 1, 1, 1, 1, 1, 1, 1, 1</b></span></blockquote>
<div>
Above you can see that when you install and start the new bundle initially nothing will happen. At this point both services are installed at the same time. The client is still bound to the original service as its still there and there is no reason to rebind, the new service is no better match than the original. But when the bundle that provides the initial service is stopped (bundle 7) the client switches over to the implementation that always returns 1. This switchover could happen at any point, even halfway thought the production of the list, so you might even be lucky enough to see something like:</div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><b>First 10 primes: 2, 3, 5, 7, 11, 13, 1, 1, 1, 1</b></span></blockquote>
<div>
I hope I have shown that OSGi services provide an excellent mechanism to implement the Branch by Abstraction pattern and even provide the possibility to do the switching between suppliers without stopping the client!</div>
<div>
<br /></div>
<div>
In the next post I'll show how we can add aspects to our services, still without modifying or even restarting the client. These can be useful for debugging, tracking or measuring how a service is used.</div>
<br />
PS - Oh, and on the remote thing, this will work just as well locally or Remote. Use OSGi Remote Services to turn your local service into a remote one... For available Remote Services implementations see <a href="https://en.wikipedia.org/wiki/OSGi_Specification_Implementations#100:_Remote_Services">https://en.wikipedia.org/wiki/OSGi_Specification_Implementations#100:_Remote_Services</a><br />
<br />
<i>With thanks to Carsten Ziegeler for reviewing and providing additional ideas.</i></div>
</div>
</div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com2tag:blogger.com,1999:blog-9210566578097047576.post-37352189938504285862015-05-19T08:59:00.001-07:002015-05-21T06:11:33.102-07:00My favourite free Mac OSX appsI have been a Windows guy for many, many years, starting with Windows 2.x in the late 1980's all the way up to Windows 7. Then, about 4 years ago I gave up. Not because Windows was so bad (I actually quite liked Windows 7) but because at the time I could not find any decent laptop for it! So I got a Mac in 2011. And, over the years I got to like it, especially the hardware. I was always a big fan of opensource and freeware/shareware which was always widely available for Windows. Over the past years a collected some good legal free software for the Mac and I thought I'd share with everyone what I like. The interesting bit here is that most of this stuff is not actually distributed via the Apple Appstore...<br />
<br />
Here's my list in no particular order, starting with a bundle of freely available utilities...<br />
<br />
<h3>
Handbrake</h3>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-GKCKNpXELxI/VVUGhvjIxQI/AAAAAAAABaU/g9CgcH_Db38/s1600/handbrake2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="458" src="http://1.bp.blogspot.com/-GKCKNpXELxI/VVUGhvjIxQI/AAAAAAAABaU/g9CgcH_Db38/s640/handbrake2.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<br /></div>
<div>
Great video converter tool. Convert any videos that you might have into any other format. BTW if you're looking for good settings that give you videos that play/stream on most devices and are relatively modest in size, I use the following settings (everything not mentioned is left at default):</div>
<div>
<ul>
<li><span style="font-size: x-small;">Format: MP4, Web Optimized</span></li>
<li><span style="font-size: x-small;">Picture: Size: source = destination, Cropping: automatic, Anamorphic: none, Filters: all off</span></li>
<li><span style="font-size: x-small;">Video: Codec: H.264, FPS: Same as source-variable, Quality: RF 22, Use Advanced Options</span></li>
<li><span style="font-size: x-small;">Audio: AAC Stereo, Samplerate auto, Bitrate 128</span></li>
<li><span style="font-size: x-small;">Advanced: Reference Frames: 6, CABAC Entropy Encoding: yes, 8x8 transform: yes, Weighted P-Frames: yes, Subpixel ME & Mode: 2 SATD qpel, Trellis: off, No DCT Decimation: off.</span></li>
</ul>
</div>
<div>
Download from <a href="http://handbrake.fr/">http://handbrake.fr</a>.<br />
<br />
<h3>
VLC</h3>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-gVMlk3NZX7k/VVUMB3UqsvI/AAAAAAAABas/b_hM7crdvOc/s1600/vlc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="442" src="http://3.bp.blogspot.com/-gVMlk3NZX7k/VVUMB3UqsvI/AAAAAAAABas/b_hM7crdvOc/s640/vlc.png" width="640" /></a></div>
<div>
The video player that plays everything. If you install the right drivers it can even play Blu Ray discs, something the native DVD player on OSX cannot do. Download from <a href="http://www.videolan.org/vlc">http://www.videolan.org/vlc</a>.<br />
<br />
<h3>
Kodi</h3>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-k_9Fiam5dNo/VVUMuGS9jsI/AAAAAAAABa0/9ZUAzg20Q1Q/s1600/kodi.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="330" src="http://2.bp.blogspot.com/-k_9Fiam5dNo/VVUMuGS9jsI/AAAAAAAABa0/9ZUAzg20Q1Q/s640/kodi.jpg" width="640" /></a></div>
<div>
<br /></div>
<div>
Previously called XBMC. Another media player. This one is really good for streaming over the network and has tons of plugins. You can also create a catalog that runs on a central server and stream that to all the devices in your house, for example. I wrote some details on how I use my Raspberry PI as such a media server here: <a href="http://coderthoughts.blogspot.com/2014/01/how-i-learned-to-stop-worrying-about.html">http://coderthoughts.blogspot.com/2014/01/how-i-learned-to-stop-worrying-about.html</a>. Download from <a href="http://kodi.tv/">http://kodi.tv</a>.</div>
<div>
<br /></div>
<h3>
BetterTouchTool</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-Uy0uaRL6sB0/VVUOf7pI3zI/AAAAAAAABbA/bZmq3mk2Aqo/s1600/Screen%2BShot%2B2015-05-14%2Bat%2B22.06.48.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="484" src="http://2.bp.blogspot.com/-Uy0uaRL6sB0/VVUOf7pI3zI/AAAAAAAABbA/bZmq3mk2Aqo/s640/Screen%2BShot%2B2015-05-14%2Bat%2B22.06.48.png" width="640" /></a></div>
<div>
This tool has a lot of features for the touchpad, but the main reason why I use it is <i>not</i> on the touchpad. I don't like the click feature of the touchpad, the fact that you have to press the touchpad itself. I prefer pressing a button to act as a mouseclick. On my UK keyboard there is a key that I never use, just above the TAB key there is a key with the § sign. The above screenshot shows how I used the BetterTouchTool to remap that key to be the mouseclick. I still use the trackpad to position my mouse, but no need to press it down, I do that with my other hand by pressing that mapped key. Download from here: <a href="http://blog.boastr.net/">http://blog.boastr.net</a>. </div>
<div>
BetterTouchTool also has Windows 7-like window snapping functionality, which is really handy too. Although I personally use <a href="http://autumnapps.com/breeze/" target="_blank">Breeze</a> for that, which has more features in this area. It's not a free tool so it doesn't belong in this list, but its very cheap so worth checking out :)<br />
<br /></div>
<h3>
Flycut</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-qbJWMcy_brs/VVURPa5risI/AAAAAAAABbM/ELS0K06_gi4/s1600/Screen%2BShot%2B2015-05-14%2Bat%2B22.17.09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="http://3.bp.blogspot.com/-qbJWMcy_brs/VVURPa5risI/AAAAAAAABbM/ELS0K06_gi4/s400/Screen%2BShot%2B2015-05-14%2Bat%2B22.17.09.png" width="400" /></a></div>
<div>
This is a very handy little tool, that simply extends the clipboard with a history of up to 25 entries. Instead of cmd-V, use shift-cmd-V to cycle through all your past clipboard contents. Extremely useful. This one is free on the apple appstore: <a href="https://itunes.apple.com/ie/app/flycut-clipboard-manager/id442160987?mt=12">https://itunes.apple.com/ie/app/flycut-clipboard-manager/id442160987?mt=12</a>.</div>
<div>
<br /></div>
<h3>
Zipeg</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-YZl95xmi8JY/VVUSTrUYQeI/AAAAAAAABbY/iulcjKIZXuY/s1600/Screen%2BShot%2B2015-05-14%2Bat%2B22.22.43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="472" src="http://2.bp.blogspot.com/-YZl95xmi8JY/VVUSTrUYQeI/AAAAAAAABbY/iulcjKIZXuY/s640/Screen%2BShot%2B2015-05-14%2Bat%2B22.22.43.png" width="640" /></a></div>
<div>
This is a free WinZip-like tool for the Mac. It doesn't have as many features as WinZip, but I tend to find it very good for quickly peeking in a .zip or .jar file. And the program icon is just really cute! Download from: <a href="http://www.zipeg.com/">http://www.zipeg.com</a>.</div>
<div>
<br /></div>
<div>
<h3>
MacVim</h3>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-uvAe0FmVVHA/VVUT7WB7D6I/AAAAAAAABbk/oNXwD1D8xLk/s1600/Screen%2BShot%2B2015-05-14%2Bat%2B22.26.23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="500" src="http://2.bp.blogspot.com/-uvAe0FmVVHA/VVUT7WB7D6I/AAAAAAAABbk/oNXwD1D8xLk/s640/Screen%2BShot%2B2015-05-14%2Bat%2B22.26.23.png" width="640" /></a></div>
<div>
I'm a VIM guy and this Mac version is really good! Download from <a href="https://code.google.com/p/macvim">https://code.google.com/p/macvim</a>.<br />
<br /></div>
<div>
<h3>
KeePassX</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-2wrDYIrl4Ic/VVW6va79-uI/AAAAAAAABb4/3h896FszsOo/s1600/Screen%2BShot%2B2015-05-15%2Bat%2B10.21.45.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="494" src="http://4.bp.blogspot.com/-2wrDYIrl4Ic/VVW6va79-uI/AAAAAAAABb4/3h896FszsOo/s640/Screen%2BShot%2B2015-05-15%2Bat%2B10.21.45.png" width="640" /></a></div>
I'm relatively new to password management software but the recent vulnerabilities and attacks made me move to it. I'm a bit weary of storing my passwords in a cloud-based solution. I'd rather have a local file that's properly encrypted, so that it doesn't matter who actually sees that raw file. I also want something that automatically works with web browsers so that passwords are automatically entered. KeePass is a great Windows-based app that does all that. There's an Android version (KeePassDroid) that even works on my BlackBerry Z10. And there are Chrome (<a href="https://chrome.google.com/webstore/detail/chromeipass/ompiailgknfdndiefoaoiligalphfdae?hl=en" target="_blank">chromeIPass</a>) and Firefox (<a href="https://addons.mozilla.org/en-us/firefox/addon/passifox/" target="_blank">PassIFox</a>) integrations with the <a href="https://github.com/pfn/keepasshttp/" target="_blank">KeePass HTTP server</a> that seamlessly enter your passwords in the appropriate websites if you approve this action.<br />
There are a number of opensource projects to get this working on the Mac too. However, they seem to be in various stages of incompleteness. There is the <a href="https://www.keepassx.org/" target="_blank">KeePassX project</a>, but it doesn't have support for the HTTP server (yet) that you need to integrate with the browser. There is a new project called <a href="https://github.com/mstarke/MacPass" target="_blank">MacPass</a> which is another KeePass clone, but it also doesn't support the HTTP server (yet). Some people use Mono to run the Windows KeePass on OSX, but I found that not very satisfying and at some point it just started crashing continuously. In the end I found <a href="https://github.com/keithbennett/keepassx/releases" target="_blank">Keith Bennett's fork of KeePassX 2.0Alpha5</a> that has HTTP server support. The UI is a bit buggy, but it's the best I could find in the opensource arena for password management on OSX, so this is what I'm using right now. Looking forward to having a more stable version of KeePass on the Mac with HTTP server support.<br />
<br />
<h3>
PDF Split and Merge</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-oDTWhgCNotE/VVtRzrFzYlI/AAAAAAAABcg/JBMfC1PaKBA/s1600/Screen%2BShot%2B2015-05-19%2Bat%2B16.06.34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="474" src="http://1.bp.blogspot.com/-oDTWhgCNotE/VVtRzrFzYlI/AAAAAAAABcg/JBMfC1PaKBA/s640/Screen%2BShot%2B2015-05-19%2Bat%2B16.06.34.png" width="640" /></a></div>
<div>
I love PDFs :) (<i>hey</i> I work for Adobe). And there are tons of tools available for creating PDFs both from <a href="https://www.adobe.com/ie/creativecloud/catalog/desktop.html" target="_blank">Adobe</a> and from other vendors. PDFSam is a really nice little tool that helps you work with PDFs once they are created. Rearrange pages, split chapters off, merge multiple PDFs together, that kind of thing. I find myself using this tool pretty often. You can get it from here: <a href="http://www.pdfsam.org/">http://www.pdfsam.org</a>.</div>
<br />
<h3>
OSX Screen Saver runner</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-44CYw-uo7Zk/VVW7RSvNjnI/AAAAAAAABcA/EGkYO9jFf1M/s1600/screensaverengine-icon.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-44CYw-uo7Zk/VVW7RSvNjnI/AAAAAAAABcA/EGkYO9jFf1M/s1600/screensaverengine-icon.png" /></a></div>
Ok, should maybe not be in this list, because it's not freeware or opensource, as it's shipped as part of OSX itself but it's free in that everyone who has OSX already has it, but you may not be aware of its existence. This is just a really simple way to lock your screen, if you need to walk away from your computer, for example. You simply find it on your local disk at <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">/System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app</span><br />
<div>
and I normally have it locked to my dock, so I can just click it to lock my screen.</div>
<div>
<br /></div>
<h2>
Applications</h2>
There are also a few larger applications where I think there are some great opensource products available that I use on a daily basis.<br />
<br />
<h3>
Openoffice / Libreoffice</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-GOr2KmxG804/VVW8cjhVDoI/AAAAAAAABcI/2QB6mdaoAqs/s1600/Screen%2BShot%2B2015-05-15%2Bat%2B10.28.47.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="384" src="http://1.bp.blogspot.com/-GOr2KmxG804/VVW8cjhVDoI/AAAAAAAABcI/2QB6mdaoAqs/s640/Screen%2BShot%2B2015-05-15%2Bat%2B10.28.47.png" width="640" /></a></div>
Personally I prefer this office suite to the commercial ones available. Great features, great usability and it works on all the platforms that I use outside of OSX, including Linux. Download from <a href="https://www.openoffice.org/">https://www.openoffice.org</a>.<br />
<br />
<h3>
Thunderbird and Firefox</h3>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-AHg__qu9aEc/VVtWuH8Hp9I/AAAAAAAABcw/yoFm-gHeweY/s1600/ff.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="183" src="http://2.bp.blogspot.com/-AHg__qu9aEc/VVtWuH8Hp9I/AAAAAAAABcw/yoFm-gHeweY/s320/ff.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-DQERSZcmdlE/VVtWvrkPf1I/AAAAAAAABc4/Rx_ij1IBL9Y/s1600/tb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="213" src="http://2.bp.blogspot.com/-DQERSZcmdlE/VVtWvrkPf1I/AAAAAAAABc4/Rx_ij1IBL9Y/s320/tb.png" width="320" /></a></div>
<div>
These two need no further introduction. Just an excellent webbrowser and a great email client. Get them at <a href="https://www.mozilla.org/">https://www.mozilla.org</a>.</div>
<div>
<br /></div>
<h3>
Eclipse</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-ClHQ7IlZZNU/VVtYtU5etVI/AAAAAAAABdE/sNNw1jVV7z0/s1600/Screen%2BShot%2B2015-05-19%2Bat%2B16.37.18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="450" src="http://4.bp.blogspot.com/-ClHQ7IlZZNU/VVtYtU5etVI/AAAAAAAABdE/sNNw1jVV7z0/s640/Screen%2BShot%2B2015-05-19%2Bat%2B16.37.18.png" width="640" /></a></div>
<div>
Still the best IDE around for Java and other programming languages. Available from <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</div>
<div>
<br /></div>
<div>
That's it - these are my favourite opensource / freeware / shareware apps that I use all the time. Enjoy :)</div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com2tag:blogger.com,1999:blog-9210566578097047576.post-34244527240173792862014-05-28T00:23:00.000-07:002014-05-28T00:29:10.092-07:00Running bndtools OSGi builds with Maven - bnd-maven-plugin now released!The <a href="http://bndtools.org/" target="_blank">bndtools.org</a> project provides some really great tooling for building OSGi bundles. This includes help for generating the bundle manifest, templates for working with Declarative Services, testing support and more. There's tooling for resolving deployments against repositories, and last but certainly not least - automatic semantic versioning. The tooling for semantic versioning will tell you whether the version of your exported packages is correct, by comparing the content of your APIs with the previous release of the bundle.<br />
<div>
I won't go into the full feature set of bndtools here, have a look at the <a href="http://bndtools.org/" target="_blank">bndtools website</a> for all the goodness. Another thing to say about bndtools is that it has <a href="https://github.com/bndtools" target="_blank">a really active community</a>, which is always a good thing to know when you're deciding on using an opensource project.</div>
<div>
<br /></div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://bndtools.org/images/tutorial/07.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://bndtools.org/images/tutorial/07.png" height="282" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Example IDE screen, taken from <a href="http://bndtools.org/">bndtools.org</a></td></tr>
</tbody></table>
<div>
The bndtools project itself focuses on providing an Eclipse-based graphical IDE for developing OSGi bundles. However you normally also want to be able to build your bundles in a headless build. For example as part of a Jenkins/Hudson process or simply to build it from the command-line yourself.</div>
<div>
The bndtools project provides headless build options using <a href="http://ant.apache.org/" target="_blank">Ant</a> and <a href="http://www.gradle.org/" target="_blank">Gradle</a>, but for many people being able to use <a href="http://maven.apache.org/" target="_blank">Maven</a> for builds is essential.</div>
<div>
<br /></div>
<div>
Most OSGi development in Maven is done these days using the <a href="http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html" target="_blank">maven-bundle-plugin</a> or using <a href="http://www.eclipse.org/tycho/" target="_blank">Eclipse Tycho</a>. Tycho focuses on building OSGi bundles using a manifest-first approach which is supported by the Eclipse PDE tooling. </div>
<div>
The maven-bundle-plugin uses the pom.xml file as the source of information for the bundle and while it uses bnd under the covers, just like the bndtools project, maven-bundle-plugin is a little different in that it uses the pom.xml as the source of information for the build. The bndtools IDE uses bnd.bnd files for this.</div>
<div>
<br /></div>
<div>
This is where the <a href="https://github.com/bndtools/bnd/tree/master/bnd-maven-plugin-parent" target="_blank">bnd-maven-plugin</a> comes in. Toni Menzel started it a while ago, Peter Kriens worked on it, and I have been putting bits and pieces in recently. The bnd-maven-plugin basically builds a bndtools project <i>exactly</i> like the bndtools IDE would build it, but then from Maven. As of today the first version of the bnd-maven-plugin (called 1.0.2 - mea culpa ;) has been released to Maven Central, so you can easily use it in your Maven builds.</div>
<div>
<br /></div>
<div>
Precise instructions on how to use it can be found in the <a href="https://github.com/bndtools/bnd/blob/master/bnd-maven-plugin-parent/README.md" target="_blank">project readme</a>, but you can easily try it out by looking at the <a href="https://github.com/bndtools/bnd/tree/master/bnd-maven-plugin-parent/samples/sample-projects" target="_blank">bnd-maven-plugin samples in github</a>. They show what your pom should look like. The samples contain the following test projects:</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">TestBundle</span> - this is a simple default-layout bndtools project that can be built with Maven.</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">TestBundle2</span> - a bndtools project that follows Maven conventions for directory locations of source and output files. It can be built using Maven and the bndtools IDE. This project also contains an example unit test.</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">TestBundle3</span> - this project contains an OSGi Framework integration test, which can be run both from Maven and Bndtools.</div>
<div>
Additionally, the sample structure contains</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">ParentPom</span> - a module that contains a parent pom used by all the other submodules.</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">MBPBundle</span> - a module that builds a bundle using the maven-bundle-plugin, showing that it can be used alongside the bnd-maven-plugin, although within a single module you need to choose one or the other.</div>
<div>
<br /></div>
<div>
To run it with Maven you can simply go <a href="https://github.com/bndtools/bnd/tree/master/bnd-maven-plugin-parent/samples/sample-projects" target="_blank">into the parent directory</a> and execute <span style="font-family: Courier New, Courier, monospace;">mvn install</span>:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">$ .../samples/sample-projects $ mvn install</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">... lots of maven output ...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">[INFO] --- maven-surefire-plugin:2.17:test (default-test) @ TestBundle2 ---</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">[INFO] Surefire report directory: /Users/David/clones/bnd/bnd-maven-plugin-parent/samples/sample-projects/TestBundle2/target/surefire-reports</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">-------------------------------------------------------</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> T E S T S</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">-------------------------------------------------------</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Running org.foo.bar.UnitTest</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.003 sec - in org.foo.bar.UnitTest</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">Results :</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">Tests run: 1, Failures: 0, Errors: 0, Skipped: 0</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">[INFO] --- bnd-maven-plugin:1.0.2:integration-test (default-integration-test) @ TestBundle3 ---</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">[INFO] Running the Bnd OSGi integration tests</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">Tests run : 1</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Passed : 1</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Errors : 0</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Failures : 0</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">No Errors</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">[INFO] Finished running the Bnd OSGi integration tests</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] ----------------------------------------------------------------------</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] Reactor Summary:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO]</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] ParentPom ......................................... SUCCESS [0.185s]</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] TestBundle ........................................ SUCCESS [4.134s]</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] TestBundle2 ....................................... SUCCESS [0.361s]</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] TestBundle3 ....................................... SUCCESS [1.041s]</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] MBPBundle ......................................... SUCCESS [0.842s]</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] sample-projects ................................... SUCCESS [0.041s]</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] ----------------------------------------------------------------------</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] BUILD SUCCESS</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] ----------------------------------------------------------------------</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">[INFO] Total time: 11.707s</span></div>
</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
The above has built all modules, ran the unit tests in <span style="font-family: Courier New, Courier, monospace;">TestBundle2</span> and the integration tests in <span style="font-family: Courier New, Courier, monospace;">TestBundle3</span>.<br />
<br /></div>
<div>
<h3>
Use them with the bndtools IDE!</h3>
</div>
<div>
As the <span style="font-family: Courier New, Courier, monospace;">TestBundle</span>, <span style="font-family: Courier New, Courier, monospace;">TestBundle2</span> and <span style="font-family: Courier New, Courier, monospace;">TestBundle3</span> projects are bndtools Eclipse projects, they can be imported via File -> Import -> Existing Projects into Workspace (along with the bnd <span style="font-family: Courier New, Courier, monospace;">cnf</span> project):<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-DqWYx6u4MfY/U4WQMd1ZHmI/AAAAAAAABRY/bs0UOm5tl6Y/s1600/wizard.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-DqWYx6u4MfY/U4WQMd1ZHmI/AAAAAAAABRY/bs0UOm5tl6Y/s1600/wizard.png" height="331" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Then you can use the IDE for all your dev needs, e.g. to execute the OSGi integration tests:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-9-mNWDC-R_A/U4Tkf4TuD9I/AAAAAAAABQ8/f57g0SDnxxA/s1600/bndtest.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-9-mNWDC-R_A/U4Tkf4TuD9I/AAAAAAAABQ8/f57g0SDnxxA/s1600/bndtest.png" height="546" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
This is only the first release, but should be good enough to start playing with it. See if it works for you. Give us feedback! Help developing it further! You can fork, file issues and patches in the <a href="https://github.com/bndtools/bnd/issues?labels=bnd-maven-plugin" target="_blank">bndtools/bnd project</a> on github. Enjoy :)</div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com4tag:blogger.com,1999:blog-9210566578097047576.post-29078973004367792942014-03-25T01:24:00.004-07:002014-03-25T01:24:31.144-07:00Apache Felix Framework 4.4.0 provides full OSGi Core R5 supportFelix Framework 4.4.0 was released today (<a href="http://felix.apache.org/news.html">http://felix.apache.org/news.html</a>) and now provides for OSGi Core R5 support. From a features point of view, it means that the following R5 features are now fully supported:<div>
<div>
<ul>
<li>New <span style="font-family: Courier New, Courier, monospace;">org.osgi.framework.UnfilteredServiceListener</span> interface</li>
<li>New <span style="font-family: Courier New, Courier, monospace;">org.osgi.framework.VersionRange</span> class</li>
<li>The Resource API and it's use in the Wiring API</li>
<li>New osgi.identity namespace</li>
<li>New value and new default for <span style="font-family: Courier New, Courier, monospace;">org.osgi.framework.bsnversion</span> framework property</li>
<li>support for static valueOf methods in the Filter</li>
<li>Enhanced <span style="font-family: Courier New, Courier, monospace;">Bundle.adapt()</span> to support AccessControlContext</li>
<li>Updates to the Bundle Hook Specification (Bundle Collision Hook)</li>
</ul>
</div>
</div>
<div>
But the main benefit as I see it that we can now run OSGi Subsystems, such as the Apache Aries implementation, on Apache Felix! For more details on that see this blog <a href="http://coderthoughts.blogspot.com/2014/01/osgi-subsytems-on-apache-felix.html" target="_blank">post: http://coderthoughts.blogspot.com/2014/01/osgi-subsytems-on-apache-felix.html</a></div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com0tag:blogger.com,1999:blog-9210566578097047576.post-40198103764710358532014-01-28T02:15:00.000-08:002014-01-28T02:15:07.996-08:00How I learned to stop worrying (about power cuts) and love my Raspberry PI<div class="separator" style="clear: both; text-align: left;">
As many other hobbyists I got myself a Raspberry PI a few months ago. It's very cool that such a functional piece of hardware can be so cheap. I'm using mine as a backup server for my photos, documents, videos etc, it is my Samba Server, FTP server and, most importantly an XBMC network server.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="" style="clear: both; text-align: left;">
I'll outline some of the things that I did to make mine working the way I wanted to, but first I'd like to address the biggest issue that I experienced with my Raspberry PI: the fact that it doesn't deal well with power cuts. The PI itself isn't really to blame for that, its a general issue with Linux and other unix-type operating systems: sudden power losses can make their boot/os disk unreadable. With Linux running on a laptop you don't really have that issue, as your laptop has a built-in UPS, but my PI doesn't have that. Which means that I often happened to find my device in a state that it wouldn't boot up again. Apparently we had a power blip.</div>
<div class="" style="clear: both; text-align: left;">
While some companies are starting to sell UPS solutions for the PI, one of the main issues with that is that the UPS often costs more than the PI itself. If you don't want to spend that extra money, I have used a different setup that gave me the capability to survive PI boot disk corruptions without too much work or cost.<br />
<br /></div>
<h3>
Backup your PI boot SD card</h3>
<div class="" style="clear: both; text-align: left;">
<a href="http://1.bp.blogspot.com/-zWWe4C7BFuw/UuaPRTHH58I/AAAAAAAAA-4/N99OvauQDWU/s1600/IMG_00000150.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-zWWe4C7BFuw/UuaPRTHH58I/AAAAAAAAA-4/N99OvauQDWU/s1600/IMG_00000150.jpg" height="240" width="320" /></a>Some people suggested that the best way to prevent your PI from corrupted boot disks is to make the disk readonly. While certainly a good idea, this doesn't really work for me as I often experiment with my PI and change its installed packages regularly. So I didn't want to restrict myself that much.<br />
What I did instead was use <a href="http://learn.adafruit.com/adafruit-raspberry-pi-lesson-1-preparing-and-sd-card-for-your-raspberry-pi/make-a-backup-image" target="_blank">Billw's cloning script</a> to make a backup copy of the main SD card on a second one. I used an old SD card that I still had lying around and got a USB SD card adapter, which sells on ebay for about £1 including shipping. Using that script I make regular backups of my main SD card onto its clone. When experiencing one of those regular power blips and the PI doesn't want to start up, I simply swap the SD cards - the one from the USB goes into the main SD card port and vice-versa! That will allow me to start up the PI again with all my configuration as I had it. Then simply run Billw's script again to clone the SD card that's still working back on the corrupted one at which point I'll have two working SD cards again!</div>
<div class="" style="clear: both; text-align: left;">
I've used this mechanism for a few months now and it works pretty well. Certainly much cheaper than a UPS and although a little bit of work (you need to ensure that the backup SD-card stays up-to-date) I found that it worked great. You can also automate the cloning so that it happens automatically every night or so...<br />
<br /></div>
<div class="" style="clear: both; text-align: left;">
In addition to the backup script there are a few other things that I tweaked for my PI. They might be a little different than what you're used to on Linux, so I'm outlining them here.<br />
<br /></div>
<h3>
Get a Hard Disk with power-save</h3>
<div>
If you're planning to use your PI as a file server, like I do, get a USB harddrive that has a power-save feature. This means that you can have your system running 24/7 but that there won't be any spinning parts if nobody's using it, and very low power consumption too in that case. I got this<span style="text-align: center;"> </span><a href="http://www.seagate.com/gb/en/external-hard-drives/desktop-hard-drives/expansion-hard-drive/?sku=STBV2000200" style="text-align: center;" target="_blank">Seagate 2TB one</a><span style="text-align: center;"> for about £60 which works great! The only thing you need to do is enable the power save feature once, which you have to do on a Windows PC using the program that comes on the drive. Once power-save enabled, you can connect it to the PI and it works perfectly. <i>Note that you do need to</i><span style="font-family: Courier New, Courier, monospace;"> sudo apt-get install ntfs-3g </span><i>to use most pre-formatted USB drives</i>.</span><br />
<span style="text-align: center;"><br /></span></div>
<h3>
<span style="text-align: center;">Use UUIDs to mount your drives</span></h3>
<div>
<span style="text-align: center;">Another issue that I was experiencing was that my USB drives didn't always get the same disk identifier after a PI reboot. One disk would sometimes be sdb and other times it would be sdc, for example. This is problematic if you use the standard </span></div>
<div style="text-align: left;">
<span style="font-family: Courier New, Courier, monospace;"> /dev/sda1 /disks/x1 auto noatime 0 0</span></div>
<div style="text-align: left;">
<span style="text-align: center;">in your <span style="font-family: Courier New, Courier, monospace;">/etc/fstab</span> to mount your drives to a directory, as your mounted directory would sometimes be mounted to one disk and sometimes to the other...</span></div>
<div style="text-align: left;">
<span style="text-align: center;">A good solution to that is to mount your drives by UUID instead. If you look in </span><span style="font-family: Courier New, Courier, monospace;">/dev/disk/by-uuid/</span> you can see the UUIDs of all the available disks. Find the right one and use that to mount your disk. The following makes sure that always the same disk is mounted to<span style="font-family: Courier New, Courier, monospace;"> /disks/x1</span>:</div>
<div style="text-align: left;">
<span style="font-family: Courier New, Courier, monospace;"> /dev/disk/by-uuid/5A22DB2D2DB0CBF /disks/x1 auto noatime 0 0</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<h3>
Rotate the logs</h3>
<div class="" style="clear: both; text-align: left;">
If you're doing a lot of file transfer to-from your PI, the Samba/FTP log files can get pretty big. If you, like me, use a crappy old 4GB SD card to run off, it might make sense to keep the size of the log files down. Logrotate allows you to do this, and the standard Raspbian distribution comes with this installed. I just tweaked it to keep the log files smaller, by only remembering 1 week of logs. To do this, you tweak <span style="font-family: Courier New, Courier, monospace;">/etc/logrotate.conf</span>, I'm using these settings:</div>
<span style="font-family: Courier New, Courier, monospace;"> # rotate log files weekly</span><br />
<span style="font-family: Courier New, Courier, monospace;"> weekly</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # keep 1 weeks worth of backlogs</span><br />
<span style="font-family: Courier New, Courier, monospace;"> rotate 1</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # create new (empty) log files after rotating old ones</span><br />
<span style="font-family: Courier New, Courier, monospace;"> create</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> # compress log files</span><br />
<span style="font-family: Courier New, Courier, monospace;"> compress</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<br />
<h3>
Serve XBMC throughout the house</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-Wg_klNeH_EU/UubHWu6AItI/AAAAAAAAA_c/-vBwM1KzCE4/s1600/xbmc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-Wg_klNeH_EU/UubHWu6AItI/AAAAAAAAA_c/-vBwM1KzCE4/s1600/xbmc.png" height="282" width="400" /></a></div>
<div>
Many people use the PI to run <a href="http://xbmc.org/" target="_blank">XBMC</a> directly. XBMC is a wonderful media playback application that gives you a really nice interface to your media collection. And a PI connected to an audio/video system can certainly be used for running it. However, I was looking for something different. I wanted to use my video library on any device in the house. Maybe a laptop, maybe our multimedia pc which is connected to the tv, maybe even on an Android smartphone. XBMC has features that allow you to do this. To get this working, <i>you don't actually need XBMC on the Raspberry PI at all!</i> You need the video files on it, a network file access protocol, such as Samba, and an installation of MySQL which is what the XBMC installations on the client computers communicate with to show your video library etc. The whole setup is really nice as it allows you to watch your videos on any device in the house and it even remembers where you left off if you stopped halfway, so that you can easily continue watching on another computer. Once you've installed Samba and <a href="http://wiki.xbmc.org/index.php?title=HOW-TO:Share_libraries_using_MySQL" target="_blank">MySQL for XBMC</a> on the PI, you need to install XBMC on all the client devices that you want to use. There are builds <a href="http://xbmc.org/download/" target="_blank">available for most platforms</a>. Next you need to configure XBMC to use MySQL. I actually use different profiles to organize my video files, for example you may have holiday videos in one and educational videos in another profile (or any other separation of your choice ;). You can do this by putting the <span style="font-family: Courier New, Courier, monospace;">advancedsettings.xml</span> file in the <span style="font-family: Courier New, Courier, monospace;">.../userdata/profile/<Profile Name></span> directory and name the database in it to separate the content, like this:</div>
<div>
<a href="http://4.bp.blogspot.com/-EOk0VRS0_2Y/Uua1bJn3bcI/AAAAAAAAA_M/5bbKUThQ5s8/s1600/XBMC-12-Media-Center-Beta-2.jpeg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://4.bp.blogspot.com/-EOk0VRS0_2Y/Uua1bJn3bcI/AAAAAAAAA_M/5bbKUThQ5s8/s1600/XBMC-12-Media-Center-Beta-2.jpeg" height="242" style="cursor: move;" width="320" /></a><span style="font-family: Courier New, Courier, monospace;"> <advancedsettings></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <videodatabase></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <type>mysql</type></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <host>my_pi_host</host></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <port>3306</port></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <user>mysql_username</user></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <pass>mysql_password</pass></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <name>my_holiday_movies</name></span><br />
<span style="font-family: Courier New, Courier, monospace;"> </videodatabase></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </advancedsettings></span></div>
<div>
The name tag in the configuration is used to connect to separate MySQL databases running in the same database server. That allows you to serve different media depending on the XBMC profile you're in. In the <i>Holidays</i> profile it lists my holiday videos, in the <i>Education</i> profile it has my educational ones.</div>
<div>
<br />
Finally, when you add videos to your database, make sure to select add the directory on the PI using a network protocol that works on all your devices. For example a Samba URL: </div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> smb://my_pi_host/share/holiday/videos/</span></div>
<div>
And voilà , you will get all your movies on all your XBMC devices.</div>
<div>
<br /></div>
<div>
Ok, so this was a random collection of bits and pieces that I did to get my Raspberry PI to do what I wanted. If you have a nugget of PI goodness, leave a comment to share :)</div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com4tag:blogger.com,1999:blog-9210566578097047576.post-62079070841261949742014-01-13T08:15:00.003-08:002015-03-30T10:09:06.068-07:00OSGi Subsytems on Apache Felix<span style="font-size: large;"><b><i>Note</i></b>: more up-to-date information on getting started with Apache Aries Subsystems can be found here: <a href="http://aries.apache.org/modules/subsystems.html">http://aries.apache.org/modules/subsystems.html</a></span><br />
<br />
Last year I blogged about running the OSGi Subsystems implementation from Apache Aries. At the time Equinox was the only OSGi framework that had the full Core R5 implementation supported, which is needed in order to run OSGi Subsystems.<br />
<div>
<br /></div>
<div>
With some recent work on the Apache Felix container, it is now very close to supporting the full <a href="http://www.osgi.org/Specifications/HomePage" target="_blank">OSGi R5 specification</a>. One of this first things that I tried with that is to run the Aries Subsystems implementation on it. And it works :)</div>
<div>
<br /></div>
<div>
OSGi Subsystems are a great way to package a number of OSGi bundles together, for example if you want to distribute an OSGi-based application. Subsystems provide a really convenient deployment model, without compromising the modularity of your application. For more background on Subsystems, see my earlier post about <a href="http://coderthoughts.blogspot.com/2013/04/osgi-subsystems.html" target="_blank">how to use OSGi Subsystems to deploy your applications</a>.</div>
<div>
<br /></div>
<div>
What I'm doing here is take the example from that blog post and run it on Apache Felix with Aries Subsystems.</div>
<div>
<br /></div>
<div>
First of all you need the latest and greatest Felix Framework. I always build it as follows:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> svn co http://svn.apache.org/repos/asf/felix/trunk felix</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> cd felix/framework</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> mvn install </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> cd ../main</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> mvn install </span></div>
<div>
<br /></div>
<div>
This will give you a framework runtime in the <span style="font-family: Courier New, Courier, monospace;">felix/main</span> folder.</div>
<div>
Then add the following bundles to the <span style="font-family: Courier New, Courier, monospace;">bundle</span> subdirectory:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/subsystem/org.apache.aries.subsystem.api/1.0.0/org.apache.aries.subsystem.api-1.0.0.jar" target="_blank">org.apache.aries.subsystem.api-1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/subsystem/org.apache.aries.subsystem.core/1.0.0/org.apache.aries.subsystem.core-1.0.0.jar" target="_blank">org.apache.aries.subsystem.core-1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/application/org.apache.aries.application.api/1.0.0/org.apache.aries.application.api-1.0.0.jar">org.apache.aries.application.api-1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/application/org.apache.aries.application.modeller/1.0.0/org.apache.aries.application.modeller-1.0.0.jar" target="_blank">org.apache.aries.application.modeller-1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/application/org.apache.aries.application.utils/1.0.0/org.apache.aries.application.utils-1.0.0.jar" target="_blank">org.apache.aries.application.utils-1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/blueprint/org.apache.aries.blueprint/1.1.0/org.apache.aries.blueprint-1.1.0.jar" target="_blank">org.apache.aries.blueprint-1.1.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/proxy/org.apache.aries.proxy/1.0.1/org.apache.aries.proxy-1.0.1.jar" target="_blank">org.apache.aries.proxy-1.0.1</a></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/org.apache.aries.util/1.1.0/org.apache.aries.util-1.1.0.jar" target="_blank">org.apache.aries.util-1.1.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/apache/felix/org.apache.felix.configadmin/1.8.0/org.apache.felix.configadmin-1.8.0.jar" target="_blank">org.apache.felix.configadmin-1.8.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> org.apache.felix.coordinator-0.0.1-SNAPSHOT</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/apache/felix/org.apache.felix.resolver/1.0.0/org.apache.felix.resolver-1.0.0.jar" target="_blank">org.apache.felix.resolver-1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/eclipse/equinox/org.eclipse.equinox.region/1.1.0.v20120522-1841/org.eclipse.equinox.region-1.1.0.v20120522-1841.jar" target="_blank">org.eclipse.equinox.region-1.1.0.v20120522-1841</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/slf4j/slf4j-api/1.7.5/slf4j-api-1.7.5.jar" target="_blank">slf4j-api-1.7.5</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <a href="http://search.maven.org/remotecontent?filepath=org/slf4j/slf4j-simple/1.7.5/slf4j-simple-1.7.5.jar" target="_blank">slf4j-simple-1.7.5</a></span></div>
</div>
<div>
<br /></div>
<div>
All of the bundles above have links behind them where they can be downloaded, except for the Felix Coordinator implementation, as this one is very new. You can just build it from the <span style="font-family: Courier New, Courier, monospace;">coordinator</span> directory of your Felix checkout. Or, if you prefer to use released components, you can also use the Equinox implementation <a href="http://search.maven.org/remotecontent?filepath=org/eclipse/equinox/org.eclipse.equinox.coordinator/1.1.0.v20120522-1841/org.eclipse.equinox.coordinator-1.1.0.v20120522-1841.jar" target="_blank">org.eclipse.equinox.coordinator_1.1.0.v20120522-1841</a>, but in order to run that on Felix you also need the Equinox supplement bundle <a href="http://download.eclipse.org/equinox/drops/R-KeplerSR1-201309111000/download.php?dropFile=org.eclipse.equinox.supplement_1.5.0.v20130812-2109.jar" target="_blank">org.eclipse.equinox.supplement_1.5.0.v20130812-2109</a>.</div>
<div>
Finally, I'm adding my little gogo command bundle as described in <a href="http://coderthoughts.blogspot.com/2013/04/osgi-subsystems.html" target="_blank">my older post</a>, to add <span style="font-family: Courier New, Courier, monospace;">subsystem:list</span>, <span style="font-family: Courier New, Courier, monospace;">subsystem:install</span>, <span style="font-family: Courier New, Courier, monospace;">subsystem:uninstall</span>, <span style="font-family: Courier New, Courier, monospace;">subsystem:start</span> and <span style="font-family: Courier New, Courier, monospace;">subsystem:stop</span> commands to Gogo. You can also download that as a bundle here: <a href="http://coderthoughts.googlecode.com/files/subsystem-gogo-command-1.0.0.jar" target="_blank">subsystem-gogo-command-1.0.0.jar</a></div>
<div>
<br /></div>
<div>
Ok, let's start up Felix and look what's inside:</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">.../felix/main $ java -jar bin/felix </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">... some log messages appear ...</span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">____________________________</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Welcome to Apache Felix Gogo</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">g! lb</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">START LEVEL 1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ID|State |Level|Name</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 0|Active | 0|System Bundle (4.3.0.SNAPSHOT)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 1|Active | 1|Apache Aries Application API (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 2|Active | 1|Apache Aries Application Modelling (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 3|Active | 1|Apache Aries Application Utils (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 4|Active | 1|Apache Aries Blueprint Bundle (1.1.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 5|Active | 1|Apache Aries Proxy Bundle (1.0.1)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 6|Active | 1|Apache Aries Subsystem API (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 7|Active | 1|Apache Aries Subsystem Core (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 8|Active | 1|Apache Aries Util (1.1.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 9|Active | 1|Apache Felix Bundle Repository (1.6.6)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 10|Active | 1|Apache Felix Configuration Admin Service (1.8.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 11|Active | 1|Apache Felix Coordinator Service (0.0.1.SNAPSHOT)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 12|Active | 1|Apache Felix Gogo Command (0.12.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 13|Active | 1|Apache Felix Gogo Runtime (0.10.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 14|Active | 1|Apache Felix Gogo Shell (0.10.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 15|Active | 1|Apache Felix Resolver (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 16|Active | 1|Region Digraph (1.1.0.v20120522-1841)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 17|Active | 1|slf4j-api (1.7.5)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 18|Resolved | 1|slf4j-simple (1.7.5)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 19|Active | 1|Subsystem Gogo Command (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 20|Active | 1|org.osgi.service.subsystem.region.context.0 (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">g! subsystem:list</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">0<span class="Apple-tab-span" style="white-space: pre;"> </span>ACTIVE<span class="Apple-tab-span" style="white-space: pre;"> </span>org.osgi.service.subsystem.root</span></div>
</div>
<div>
<br /></div>
<div>
Looking at the bundles, you can see the Gogo command line ones that come with Felix, as well as its Bundle Repo. You can also see bundle 20, which is a synthesised bundle that the Subsystems implementation added and which represents the root subsystem. The <span style="font-family: Courier New, Courier, monospace;">subsystem:list</span> command reports that there is one subsystem: the root one.</div>
<div>
<br /></div>
<div>
Now let's look at my example applications again. I have the following two subsystems:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-qYyKKg8SaTI/UXD0ymHTKLI/AAAAAAAAA0E/0_huupOO2vw/s1600/BundleVennDiagram.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-qYyKKg8SaTI/UXD0ymHTKLI/AAAAAAAAA0E/0_huupOO2vw/s1600/BundleVennDiagram.png" height="188" width="400" /></a></div>
<div>
<br /></div>
<div>
Subsystems are a great way to distribute multi-bundle applications, and my two subsystems each contain 2 specific bundles as well as a shared bundle.</div>
<div>
<br /></div>
<div>
Let's install then and see what happens:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">g! subsystem:install http://coderthoughts.googlecode.com/files/subsystem1.esa</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Installing subsystem: http://coderthoughts.googlecode.com/files/subsystem1.esa</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Subsystem successfully installed: subsystem1; id: 1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">g! subsystem:start 1</span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">g! lb</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">START LEVEL 1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ID|State |Level|Name</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">...</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 21|Active | 1|SharedBundle (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 22|Active | 1|BundleA (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 23|Active | 1|BundleB (1.0.0)</span></div>
</div>
</div>
<div>
The three bundles needed by subsystem1 are installed and all started with the <span style="font-family: Courier New, Courier, monospace;">subsystem:start</span> command.</div>
<div>
<br /></div>
<div>
And let's add the second subsystem...</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">g! subsystem:install http://coderthoughts.googlecode.com/files/subsystem2.esa</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Installing subsystem: http://coderthoughts.googlecode.com/files/subsystem2.esa</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Subsystem successfully installed: subsystem2; id: 2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">g! subsystem:start 2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">g! lb</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">START LEVEL 1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ID|State |Level|Name</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">...</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 21|Active | 1|SharedBundle (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 22|Active | 1|BundleA (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 23|Active | 1|BundleB (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 24|Active | 1|BundleC (1.0.0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 25|Active | 1|BundleD (1.0.0)</span></div>
<div>
<br /></div>
<div>
As expected, the two new bundles for subsystem2 are now also installed. And, because we're talking about a <i>feature</i> subsystem here, where everything is shared, the SharedBundle (21) is not installed a second time, but rather reused from subsystem1. </div>
<div>
<br /></div>
<div>
The topic of subsystems is much bigger. Subsystems can provide a certain level of isolation, they can work with Repositories for provisioning and you have a lot of options wrt to what you can put inside an .esa file: you can put all the bundles in there that your application needs, or you can just have a textual descriptor that declares your main bundles and let the OSGi Resolver and Repository find the dependencies and deploy these for you. Details about these various options can be found in the OSGi Subsystem spec (chapter 134 in the <a href="http://www.osgi.org/Download/Release5" target="_blank">OSGi R5 Enterprise spec</a>). </div>
<div>
<br /></div>
<div>
All in all good news - Subsystems now works in Apache Felix as well. Right now you need to build the latest snapshot, but hopefully we'll have a Felix Framework release for this soon!</div>
<div>
</div>
</div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com7tag:blogger.com,1999:blog-9210566578097047576.post-7516341237601060652013-10-22T00:43:00.000-07:002013-10-22T00:43:46.929-07:00Role-based access control for Karaf shell commands and OSGi servicesIn a <a href="http://coderthoughts.blogspot.com/2013/10/jmx-role-based-access-control-for-karaf.html" target="_blank">previous post</a> I outlined how role-based access control was added to JMX in <a href="http://karaf.apache.org/" target="_blank">Apache Karaf</a>. While JMX is one way to remotely manage a Karaf instance, another management mechanism is provided via the <a href="http://karaf.apache.org/manual/latest/users-guide/remote-console.html" target="_blank">Karaf Console/Shell</a>. Up until now security for console commands was very coarse-grained. Once in the console you had access to all the commands. For example, it was not possible to give certain users access to merely changing their own configuration without also giving them access to shutting down the whole karaf instance.<br />
<br />
With commit <a href="http://svn.apache.org/viewvc?view=revision&revision=1534467" target="_blank">r1534467</a> this has now changed (thanks again to JB Onofré for reviewing and applying my pull request). You can now define roles required for each shell command and even have different roles depending on the arguments used with a certain command. This is achieved by using a relatively advanced feature of OSGi: Service Registry Hooks. These hooks give you a lot of control on how the OSGi service registry behaves. <a href="http://coderthoughts.blogspot.com/2009/11/altering-osgi-service-lookups-with.html" target="_blank">I blogged about them before</a>. They enable you to:<br />
<ul>
<li>see what service consumers are looking for, so you can register these services on-the-fly. This is often used to import remote services from discovery, but only if there is actually a client for them.</li>
<li>hide services from certain service consumers</li>
<li>change the service properties the client sees for a service by providing an alternative registration</li>
<li>proxy the original service</li>
</ul>
<div>
Every Karaf command is in reality an Apache <a href="http://felix.apache.org/site/apache-felix-gogo.html" target="_blank">Felix Gogo</a> command, registered as an OSGi service. Every command has two service registration properties: <span style="font-family: Courier New, Courier, monospace;">osgi.command.scope</span> and <span style="font-family: Courier New, Courier, monospace;">osgi.command.function</span>. These properties define the name of the command and its scope. With the use of the OSGi Service Registry hooks I can replace the original service with a proxy that adds the role-based security. </div>
<div>
<br /></div>
When I originally floated this idea on the Karaf mailing list, Christian Schneider said: "why don't we enable this for all services?" Good idea! So that's how I ended up implementing it. I first added a mechanism to add role-based access control to OSGi services in general and then applied this mechanism to get role-based access control for the Karaf commands.<br />
<div>
<br /></div>
<h3>
<span style="font-size: 19px;"><b>Under the hood</b></span></h3>
<div>
<div style="text-align: right;">
</div>
<div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-qMMwBMxeF9A/UmDt8-kNJRI/AAAAAAAAA7I/a67KLs-INg4/s1600/Karaf_ServiceGuard.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="165" src="http://4.bp.blogspot.com/-qMMwBMxeF9A/UmDt8-kNJRI/AAAAAAAAA7I/a67KLs-INg4/s400/Karaf_ServiceGuard.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">the original service is hidden by OSGi Service Registry Hooks</td></tr>
</tbody></table>
The theory is quite simple. As mentioned above you can use OSGi Service Registry hooks to hide a service from certain consuming bundles and effectively replace it with another. In my case the replacement is a proxy of the original service with the same service registration properties (and some extra ones, see below). It will delegate an invocation to the original service, but before it does so it will check the ACL for the service being invoked to find out what the permitted roles are. Then it checks the roles of the current user by looking at the <a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/security/auth/Subject.html#getSubject(java.security.AccessControlContext)" target="_blank">Subject in the current AccessControlContext</a>. If the user doesn't have any of the permitted roles the service invocation is aborted with a <span style="font-family: Courier New, Courier, monospace;">SecurityException</span>.</div>
<div>
<br /></div>
<h3>
How do I configure ACLs for OSGi services?</h3>
<div>
ACLs for OSGi services are defined in a way similar to how these <a href="http://coderthoughts.blogspot.com/2013/10/jmx-role-based-access-control-for-karaf.html" target="_blank">are defined for JMX access</a>: through the OSGi Configuration Admin service. The PID needs to start with <span style="font-family: Courier New, Courier, monospace;">org.apache.karaf.service.acl.</span> but the exact PID value isn't important. The service to which the ACL is matched is found through the <span style="font-family: Courier New, Courier, monospace;">service.guard</span> configuration value. Configuration Admin is very flexible wrt to how configuration is stored, but by default in Karaf these configurations are stored as <span style="font-family: Courier New, Courier, monospace;">.cfg</span> files in the <span style="font-family: Courier New, Courier, monospace;">etc/</span> directory. Let's say I have a service in my system that implements the following API and is registered in the OSGi service registry under the <span style="font-family: Courier New, Courier, monospace;">org.acme.MyAPI</span> interface:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> package org.acme;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> public interface MyAPI {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> void doit(String s);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
</div>
<div>
If I want to specify an ACL to say that only clients that have the <i>manager</i> role can invoke this service, I have to do two things:</div>
<div>
<ol>
<li>First I need to enable the role-based access for this service by including it in the filter specified in the <span style="font-family: Courier New, Courier, monospace;">etc/system.properties</span> in the <span style="font-family: Courier New, Courier, monospace;">karaf.secured.services</span> property:<br /><span style="font-family: Courier New, Courier, monospace;"> karaf.secured.services=(|(objectClass=org.acme.MyAPI)(...what was there already...))</span><br />only services matching this property are enabled for role-based access control. Other services are left alone.</li>
<li>Define the ACL for this service as Config Admin configuration. For example by creating a file <span style="font-family: Courier New, Courier, monospace;">etc/org.apache.karaf.service.acl.myapi.cfg</span>:<br /><span style="font-family: Courier New, Courier, monospace;"> service.guard=(objectClass=org.acme.MyAPI)<br /> doit=manager</span><br />So the actual PID of the configuration is not really important, as long as it starts with the prefix. The service it applies to is then selected by matching the filter in the <span style="font-family: Courier New, Courier, monospace;">service.guard</span> property.</li>
</ol>
<div>
There are some additional rules. There is a special role of <span style="font-family: Courier New, Courier, monospace;">*</span> which means that ACLs are disabled for this method. Similar to the JMX ACLs you can also specify function arguments that require specific roles. For more details see the <a href="https://github.com/bosschaert/karaf/commit/5e67cd5f7e975c34d346cc2f4c4d0c0011ce43b7" target="_blank">commit message</a>.</div>
</div>
<div>
<br /></div>
<div>
<h3>
Setting roles for service invocation</h3>
</div>
</div>
<div>
The service proxy checks the roles in the current <a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/security/auth/Subject.html#getSubject(java.security.AccessControlContext)" target="_blank">AccessControlContext</a> against the required ones. So when invoking a service that has role-based access control enabled, you need to set these roles. This is normally done as follows:</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="s1"> import</span> javax.security.auth.Subject;</span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="s1"> import</span> org.apache.karaf.jaas.boot.principal.RolePrincipal;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> // ... </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> Subject s = new Subject();</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> s.getPrincipals().add(new RolePrincipal("manager"));</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> Subject.doAs(s, new PrivilegedAction() {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> public Object run() {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> svc.doit("foo"); // invoke the service</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
This example uses a Karaf built-in role. You can also use your own role implementations by specifying them using the <span style="font-family: Courier New, Courier, monospace;">className:roleName</span> syntax in the ACL.</div>
</div>
<div>
<br />
<b>Note however</b>, that <span style="font-family: 'Courier New', Courier, monospace;">javax.security.auth.Subject</span> is a very powerful API. You should give bundles that import it extra scrutiny to ensure that they don't give access to clients that they shouldn't really have...<br />
<br /></div>
<h3>
Applied to Shell Commands</h3>
<div>
Next step was to apply these concepts to the Karaf shell commands. As all the shell commands are registered with the <span style="font-family: Courier New, Courier, monospace;">osgi.command.function</span> and <span style="font-family: Courier New, Courier, monospace;">osgi.command.scope</span> properties. I enabled them in the default Karaf configuration with the following system property:</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> karaf.secured.services=(&(osgi.command.scope=*)(osgi.command.function=*))</span></div>
<div>
<br /></div>
<div>
The next thing is to configure command ACLs. However that presented a slight usability problem. Most of the services in Karaf are implemented (via OSGi Blueprint) using the <a href="https://github.com/apache/felix/blob/trunk/gogo/runtime/src/main/java/org/apache/felix/service/command/Function.java" target="_blank">Function</a> interface. Which means that the actual method name is always <span style="font-family: Courier New, Courier, monospace;">execute</span>. It also means that you need to create a separate Configuration Admin PID for each command which is quite cumbersome. You really want to configure this stuff on a per-scope level with all the commands for a single scope in a single configuration file. To allow this the command-integration code contains a configuration transformer which creates service ACLs as described above but based on command scope level configuration files.<br />
The command scope configuration file must have a PID that follows this structure: <span style="font-family: Courier New, Courier, monospace;">org.apache.karaf.command.acl.<scope></span> So if you want to create such a file for the <span style="font-family: Courier New, Courier, monospace;">feature</span> scope the config file would be in <span style="font-family: Courier New, Courier, monospace;">etc/org.apache.karaf.command.acl.feature.cfg</span>:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> list = viewer</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> info = viewer</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> install = admin</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> uninstall = admin</span></div>
</div>
<div>
In this example only users with the <i>admin</i> role can do install/uninstall operations while <i>viewers</i> can list features etc... Note that by using groups (as outlined in <a href="http://coderthoughts.blogspot.com/2013/10/jmx-role-based-access-control-for-karaf.html" target="_blank">this previous post</a>) users added to an admin group will also have viewer permissions, so will be able to do everything. For a more complex configuration file, have <a href="https://github.com/bosschaert/karaf/blob/e0767d8f09de59e81ea000e9d0feed0dca9f0856/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.command.acl.config.cfg" target="_blank">a look at this one</a>.</div>
<div>
<br /></div>
<h3>
Can I find out what roles a service requires?</h3>
<div>
It can be useful to know in advance what the roles are that are required to invoke a service. For example the shell supports tab-style command completion. You don't want to show commands to the user that you know are not available to the user's roles. For this purpose an additional service registration property is added to the proxy service registration: <span style="font-family: Courier New, Courier, monospace;">org.apache.karaf.service.guard.roles=[role1,role2]</span>. The value of the property is the <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html" target="_blank">Collection</a> of roles that can possibly invoke a method on the service. Since each command maps to a single service, we can have a Command Processor that only selects the commands applicable to the roles of the current user. This means that commands that this user doesn't have the right roles for are automatically hidden from autocompletion etc. When I'm logged in as an admin I can see all the feature commands (I removed ones not mentioned in the config for brevity):</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> karaf@root()> feature: </span><i><hit TAB></i></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> info install list uninstall</span></div>
</div>
<div>
while Joe, a viewer, only see the feature commands available to viewers:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> joe@root()> feature:</span><span style="font-family: Courier New, Courier, monospace;"> </span><i><hit TAB></i></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> info list</span></div>
</div>
<div>
<br /></div>
<div>
In some cases the commands have roles associated with particular values being passed in. For example the config admin shell commands require admin rights for certain PIDs but not all. So Joe can safely edit his own configuration but is prevented from editing system level configuration:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> joe@root(config)> edit org.acme.foo</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> joe@root(config)> property-set somekey someval</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> joe@root(config)> update</span></div>
So Joe can edit the <span style="font-family: Courier New, Courier, monospace;">org.acme.foo</span> PID, but when he tries to edit the <span style="font-family: Courier New, Courier, monospace;">jmx.acl</span> PID access is denied:<br />
<div>
<span style="font-family: Courier New, Courier, monospace;"> joe@root(config)> edit jmx.acl</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> Error executing command: Insufficient credentials.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
</div>
<h3>
Where are we with this stuff today?</h3>
<div>
The first commits to enable the above have just gone into Karaf trunk and although I wrote lots of unit tests for it, more use is needed to see whether it all works as users would expect. Also the default ACL configuration files may need a bit more attention. What's there now is really a start, the idea is to refine as we go along and have this as a proper feature for Karaf 3.<br />
<br />
<h3>
The power of OSGi services</h3>
One thing that this approach shows is really the power and flexibility of OSGi services. None of the code of the actual commands was changed. The ability to build role-based access on top of them in a non-intrusive way was really enabled by the OSGi service registry design and its capabilities.</div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com0tag:blogger.com,1999:blog-9210566578097047576.post-67331677605721699632013-10-18T06:34:00.002-07:002013-10-18T09:34:45.409-07:00Running pure Jasmine unit tests through MavenI have always really liked writing unit tests. For the simple reason that with those I know that I did all I could to ensure my algorithms worked as planned. Sure, even with high code coverage there is still a chance that you're missing a situation in your tests, but at least once you know this you can fill the gap by adding an additional test. And, of course, you want to run these tests automatically as part of a regular build. No manual testing please :)<br />
<br />
So when I started looking at some projects that use JavaScript I wanted to use the same ideas. Write unit tests that are automatically run during a headless build.<br />
I started using Jasmine, as it seems to be the most popular JavaScript testing framework today. Since the project I was working with was using Maven already I wanted to integrate my Jasmine testing as part of the ordinary Maven test cycle.<br />
Additionally, I wanted the setup of my environment be trivial. I really don't want any developer to install additional software besides what they already have to run Maven. And, I don't want to depend on any platform specific software, if possible.<br />
<br />
This got me looking around on the internet and I found a really good post by <a href="http://blog.code-cop.org/2013/07/jasmine-rhino-ant.html" target="_blank">Code Cop</a> that describes how you can do something like this for Apache Ant. What he did was test JavaScript logic using Jasmine, outside of the browser. So you don't have the browser JavaScript environment present, but you can test all your algorithms. This is precisely what I was looking for too. Another nice thing of his work is that the test results are stored in the same XML format as JUnit uses, so you can inspect these files with any tool that can work with ordinary JUnit output XML files (e.g. you can open them in Eclipse and view them in the JUnit view).<br />
<br />
I started with the <a href="http://hg.code-cop.org/js-katas/src/tip/lib" target="_blank">code by Code Cop</a>, and reduced it to the bare minimum, only supporting Jasmine (Code Cop's work also supports other JS test frameworks). You can find this minimal ant-jasmine test integration at <a href="https://github.com/bosschaert/coderthoughts/tree/master/jasmine-ant" target="_blank">coderthoughts/jasmine-ant</a>. The next step: get it working in Maven.<br />
<br />
There were a couple of things that needed to be changed to be able to do this:<br />
<ol>
<li>I wanted to obtain the Java-level dependencies via Maven: the original Rhino scripting engine (can't use the one in the JRE, because JavaAdapter was removed, <a href="http://download.oracle.com/javase/6/docs/technotes/guides/scripting/programmer_guide/index.html#jsengine" target="_blank">see here</a>) and <span style="font-family: Courier New, Courier, monospace;">js-engine.jar</span> that adds Rhino as the <span style="font-family: Courier New, Courier, monospace;">rhino-nonjdk</span> scripting language.</li>
<li>I want to have the source .js files in <span style="font-family: Courier New, Courier, monospace;">src/main/js</span> and the tests in <span style="font-family: Courier New, Courier, monospace;">test/main/js</span>, the usual locations in Maven.</li>
<li>I needed to make the output directory configurable so that the results are written to <span style="font-family: Courier New, Courier, monospace;">target/surefire-reports</span>, where Maven expects these files.</li>
</ol>
<div>
In the end I got things going. I'm still using Ant inside Maven to actually do the Jasmine test running, using a slightly modified version of Code Cop's Jasmine runner Ant task. But the whole end result fits nicely with the rest of the Maven setup.</div>
<br />
<div>
<span style="font-family: Courier New, Courier, monospace;"><project</span><span style="font-family: 'Courier New', Courier, monospace;">></span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <modelVersion>4.0.0</modelVersion></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <groupId>org.coderthoughts</groupId></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <artifactId>jasmine-maven-example</artifactId></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <version>1.0.0-SNAPSHOT</version></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <packaging>war</packaging> <!-- your JavaScript will likely end up in a .war file --></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <dependencies></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <dependency></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <!-- Bring in the original Rhino implementation that contains the JavaAdapter class --></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <groupId>org.mozilla</groupId></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <artifactId>rhino</artifactId></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <version>1.7R3</version></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <scope>test</scope></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </dependency></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <dependency></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <!-- Adds the 'rhino-nonjdk' language to the supported scripting languages --></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <!-- Obtained from the repository at http://dist.codehaus.org/mule/dependencies/maven2/ --></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <groupId>javax.script</groupId></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <artifactId>js-engine</artifactId></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <version>1.0</version></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <scope>test</scope></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </dependency></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </dependencies></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <build></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <plugins></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <plugin></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <groupId>org.apache.maven.plugins</groupId></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <artifactId>maven-antrun-plugin</artifactId></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <version>1.7</version></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <executions></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <execution></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <phase>test</phase></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <configuration></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <target></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <property name="jasmine.dir" location="lib/jasmine-ant" /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <property name="script.classpath" refid="maven.test.classpath" /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <scriptdef name="jasmine" src="${jasmine.dir}/jasmineAnt.js"</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> language="rhino-nonjdk" classpath="${script.classpath}"></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <!-- Jasmine (jasmine-rhino.js) needs pure Rhino because </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> JDK-Rhino does not define JavaAdapter. --></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <attribute name="options" /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <attribute name="ignoredGlobalVars" /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <attribute name="haltOnFirstFailure" /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <attribute name="jasmineSpecRunnerPath" /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <attribute name="testOutputDir" /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <element name="fileset" type="fileset" /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </scriptdef></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <jasmine options="{verbose:true}"</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: Courier New, Courier, monospace;">testOutputDir="target/surefire-reports" </span><span style="font-family: 'Courier New', Courier, monospace;">haltOnFirstFailure="false"</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: Courier New, Courier, monospace;">jasmineSpecRunnerPath="${jasmine.dir}/AntSpecRunner.js"></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <fileset dir="test" includes="**/*Spec.js" /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </jasmine></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </target></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </configuration></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <goals></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <goal>run</goal></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </goals></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </execution></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </executions></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </plugin></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <!-- ... other plugins ... --></span></div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </plugins></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </build></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <repositories></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <repository></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <id>codehaus-mule</id></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <url>http://dist.codehaus.org/mule/dependencies/maven2/</url></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </repository></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </repositories></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"></project></span></div>
<div>
<br /></div>
</div>
<div>
A couple of things to note here:</div>
<div>
<ul>
<li>I couldn't find the <span style="font-family: Courier New, Courier, monospace;">js-engine.jar</span> in Maven Central. Fortunately it was available in the <a href="http://dist.codehaus.org/mule/dependencies/maven2" target="_blank">Mule repo at codehaus.org</a>.</li>
<li>I added the <span style="font-family: Courier New, Courier, monospace;">testOutputDir</span> as a configuration attribute for where the test results go.</li>
<li>No setup whatsoever required, no platform specific binaries needed, if you can run Maven you can run these Jasmine tests.</li>
</ul>
<div>
When I run it, it looks like this:</div>
</div>
<br />
<div>
<span style="font-family: Courier New, Courier, monospace;">$ mvn test</span></div>
<div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] Scanning for projects...</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] ------------------------------------------------------------------------</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] Building jasmine-maven-example 1.0.0-SNAPSHOT</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] ------------------------------------------------------------------------</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">...</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">-------------------------------------------------------</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> T E S T S</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">-------------------------------------------------------</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] ...</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] --- maven-antrun-plugin:1.7:run (default) @ jasmine-maven-example ---</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] Executing tasks</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">main:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> [jasmine] Spec: main/js/RomanNumeralsSpec.js</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> [jasmine] Tests run: 7, Failures: 0, Errors: 0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> [jasmine]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] Executed tasks</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] ------------------------------------------------------------------------</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] BUILD SUCCESS</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] ------------------------------------------------------------------------</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">[INFO] Total time: 2.551s</span></div>
<div>
<br /></div>
</div>
<div>
<br /></div>
<div>
Of course, the build fails when a test fails and also the test reports can be processed using anything that can JUnit test reports, such as <span style="font-family: Courier New, Courier, monospace;">mvn surefire-report:report</span></div>
<div>
<br /></div>
<div>
I find it pretty handy. A minimal project that does this that you can try yourself is available here: <a href="https://github.com/bosschaert/coderthoughts/tree/master/jasmine-maven" target="_blank">coderthoughts/jasmine-maven</a>. </div>
</div>
<br />
<div>
So it's a little different from the <span style="font-family: inherit;"><a href="http://searls.github.io/jasmine-maven-plugin/index.html" target="_blank">jasmine-maven-plugin</a></span> in that this doesn't fork a browser and is hence a little bit faster. It should be possible to speed it up even further by writing a proper Maven plugin for it...<br />
It's more of a pure unit testing environment, where running the jasmine-maven-plugin is closer to a system test setup...<br />
And of course, thanks again to Code Cop for providing an excellent starting point for this stuff.</div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com7tag:blogger.com,1999:blog-9210566578097047576.post-16386219208256203932013-10-03T03:32:00.000-07:002013-10-03T03:32:05.454-07:00JMX role-based access control for KarafRecently I worked on adding role-based access control to Karaf management operations. This work is split into two parts: one part focuses on adding role-based access to JMX. Another part focuses on the Karaf shell/console. In this post I'm looking at how JMX access is secured.<br />
<div>
<br /></div>
<div>
JMX plays an important role in Karaf as a remote management mechanism. A number of management clients are built on top JMX, <a href="http://hawt.io/" target="_blank"><i>hawtio</i></a> being probably the most popular one right now. While <i>hawtio</i> uses JMX through <a href="http://www.jolokia.org/" target="_blank">Jolokia</a> which exposes the JMX API over a REST interface, other clients use JMX locally (e.g. via JConsole) or over a remote connector. </div>
<div>
<br /></div>
<div>
Most functionality available in Karaf can be managed via MBeans, but up until now it was suffering from one issue, there was really only one level of access. If you were given rights to access, you had access to all the MBeans. It was not possible to give users access to certain areas in JMX while restricting access to other areas.</div>
<div>
<br /></div>
<div>
<a href="http://3.bp.blogspot.com/-DFtaudeSNxo/UkmADhuA9HI/AAAAAAAAA3w/PbOCNOwEaJ4/s1600/Karaf_JMX.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="277" src="http://3.bp.blogspot.com/-DFtaudeSNxo/UkmADhuA9HI/AAAAAAAAA3w/PbOCNOwEaJ4/s400/Karaf_JMX.png" width="400" /></a><br />
<h3>
Role-based Access Control</h3>
With commit <a href="http://svn.apache.org/viewvc?view=revision&revision=1528587" target="_blank">r1528587</a> my JMX role-based access has been added to Karaf trunk (extra kudos and thanks to Jean-Baptiste Onofré for additional testing, finding a number of bugs, fixing those and actually applying the commits!). It means that an administrator can now declare the roles required to access certain Karaf MBeans. And, it also applies to MBeans that are registered outside of Karaf, but running in the same MBeans server. So JRE-provided MBeans and MBeans coming from OSGi bundles that are installed on top of Karaf are also covered.</div>
<div>
<br /></div>
<h3>
How does it work?</h3>
<div>
It works by inserting a JMX Guard which is configured via a JVM-wide MBeanServerBuilder. The Karaf launching scrips are updated to contain the following argument: <span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">-Djavax.management.builder.initial=org.apache.karaf.management.boot.KarafMBeanServerBuilder</span></div>
<div>
This global JVM-level MBeanServerBuilder calls into an OSGi bundle that contains the JMX Guard for each JMX invocation made. The Guard in turn looks up the ACL of the accessed MBean in the OSGi Configuration Admin Service and checks the required roles for this MBean with the RolePrincipal objects present in the <a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/security/auth/Subject.html#getSubject(java.security.AccessControlContext)" target="_blank">Subject in the current AccessControlContext</a>. If no matching role is present, the JMX invocation will be blocked with a SecurityException.</div>
<div>
<br /></div>
<h3>
How can I define my ACLs?</h3>
<div>
The Access Control Lists are stored in OSGi Configuration Admin. This means that they can be defined in whatever way the currently configured Config Admin implementation stores its information, which could be a database, nosql, etc... In the case of Karaf this information is normally stored in the <span style="font-family: Courier New, Courier, monospace;">etc/</span> directory in <span style="font-family: Courier New, Courier, monospace;">.cfg</span> text files. The file name (excluding the <span style="font-family: Courier New, Courier, monospace;">.cfg</span> extension) represents the Config Admin PID. JMX ACLs are mapped to Config Admin PIDs by prefixing them with <span style="font-family: Courier New, Courier, monospace;">jmx.acl</span>. Then the Object Name as it appears in the JConsole tree is used to identify the MBean. So the ActiveMQ QueueA MBean as in the screenshot below would map to the PID <span style="font-family: Courier New, Courier, monospace;">jmx.acl.org.apache.activemq.Broker.amq-broker.Queue.QueueA</span>. </div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-myhqBN--mGg/Ukmc7sY7RsI/AAAAAAAAA4Q/giIspH-vM7o/s1600/JConsole2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="472" src="http://2.bp.blogspot.com/-myhqBN--mGg/Ukmc7sY7RsI/AAAAAAAAA4Q/giIspH-vM7o/s640/JConsole2.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The 'purge' operation is denied if the user does not have the required role</i></td></tr>
</tbody></table>
<div>
However, having to write a configuration file for every MBean isn't really that user-friendly. It would be nice if we could define this stuff on a slightly higher level. Therefore the code that looks for the ACL PIDs follows a hierarchical approach. If it cannot find any matching definitions for the operation invoked on <span style="font-family: Courier New, Courier, monospace;">...QueueA</span> PID, it goes up in the tree and looks for definitions in <span style="font-family: Courier New, Courier, monospace;">jmx.acl.org.apache.activemq.Broker.amq-broker.Queue</span> and then <span style="font-family: Courier New, Courier, monospace;">jmx.acl.org.apache.activemq.Broker.amq-broker</span> and so on. So if you want to specify an ACL for all queues on all ActiveMQ brokers you could do this in the <span style="font-family: Courier New, Courier, monospace;">jmx.acl.org.apache.activemq.Broker.cfg</span> file. For example:</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> browse* = manager, viewer</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> getMessage = manager, viewer</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> purge = admin</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> remove* = admin</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> copy* = manager</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> sendTextMessage* = manager</span></div>
<div>
Note that this example uses wildcards for method names, so <span style="font-family: Courier New, Courier, monospace;">browse*</span> covers <span style="font-family: Courier New, Courier, monospace;">browse()</span>, <span style="font-family: Courier New, Courier, monospace;">browseAsTable()</span> and <span style="font-family: Courier New, Courier, monospace;">browseMessages()</span>. Additionally even though the admin role has access to all APIs it's not explicitly listed everywhere. This is not because the <b>admin</b> role is special, this is because administrators are expected to be part of the <b>admingroup</b>, which has all the roles in the system.<br />
<br /></div>
<h3>
Groups</h3>
<div>
To keep the ACLs manageable I used the concept of JAAS groups. Typically you want to give an administrator access to everything in the system, but it's very cumbersome (and ugly) to add 'admin' to every single ACL definition in the system. Therefore the idea is that an administrator is not directly assigned the <i>admin</i> role, but is rather added to the <b>admingroup</b>. This group then has all the roles defined in the system. And no, it's not magic. If you decide to add a new group then the admingroup needs to be updated. Here's what the definition of some users might look like:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> karaf@root()> jaas:realm-manage --realm karaf</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> karaf@root()> jaas:user-list</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> User Name | Group | Role</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ----------------------------------</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> karaf | admingroup | admin</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> karaf | admingroup | manager</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> karaf | admingroup | viewer</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> joe | managergroup | manager</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> joe | managergroup | viewer</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> mo | | viewer</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
So in this example, the karaf user is in the admingroup and because of that has the roles admin, manager and viewer.<br />
<div>
<br />
<h3>
Default Configuration</h3>
<div>
There is default configuration that applies to any MBean if it doesn't have specific configuration. This can be found at the top of the hierarchy in the <span style="font-family: Courier New, Courier, monospace;">jmx.acl.cfg</span> file:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> list* = viewer</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> get* = viewer</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> is* = viewer</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> set* = admin</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> * = admin</span></div>
</div>
<div>
So the default is that any operation on any MBean starting with 'list', 'get' or 'is' is assumed to be an operation that you only need the <i>viewer</i> role for, while set* or any other operation name requires the <i>admin</i> role by default. This also maps well to MBeans that define JMX attributes. Obviously these defaults don't apply if a more specific definition for the MBean can be found...</div>
<div>
<br /></div>
<h3>
Redefine to suit</h3>
<div>
While the Karaf distro comes with some predefined configuration in the form of <span style="font-family: Courier New, Courier, monospace;">jmx.acl.**.cfg</span> files, it might be possible that this doesn't map 100% to the roles being used in your organization. Therefore all of this can be changed by the administrator. Nothing is hard coded, so feel free to add new roles, new groups and new ACLs to suit your organizational structure.</div>
<div>
<br /></div>
<h3>
ACL definition details</h3>
<div>
The ACL examples in this posting are on the method level, but in some cases you want to define roles based on the arguments being passed into the operation. For example, you might need <b>admin</b> rights to uninstall a karaf <i>system</i> bundle, but maybe the <b>manager</b> role is enough to uninstall <i>other</i> bundles. Therefore you can define roles based on arguments passed in to the JMX operation either as literal arguments or using regular expressions. For more information on this, see the original commit message in github: </div>
<div>
<a href="https://github.com/bosschaert/karaf/commit/f793e70612c47d16a95ef12287514c603613f2c0">https://github.com/bosschaert/karaf/commit/f793e70612c47d16a95ef12287514c603613f2c0</a></div>
<div>
<br /></div>
<h3>
What MBeans can I use?</h3>
<div>
If you're writing a rich client or other tool over JMX it can be nice to know in advance whether the current user can invoke certain operations or not. It allows the tool to only show the relevant widgets (buttons, menus etc) if it's actually possible to use the associated MBeans. For this use-case I added an MBean <span style="font-family: Courier New, Courier, monospace;">org.apache.karaf:type=security,area=jmx</span> that has a number of <span style="font-family: Courier New, Courier, monospace;">canInvoke()</span> operations. It allows you to check whether the currently logged in user can invoke any methods on a given MBean at all or whether it can invoke a certain method. There is also a bulk query operation that lets you check a whole bunch of operations in one go. The nice thing about this approach is that the client doesn't need to know anything about how the roles are mapped by the administrator. It simply checks whether the currently logged in user has the appropriate roles for the operations requested. This means that if the administrator decides to revamp the whole role-mapping on the back-end the client console will automatically adapt: no duplication of information or hard-coded role names needed. For more details about the <span style="font-family: Courier New, Courier, monospace;">canInvoke()</span> method see: <a href="https://github.com/bosschaert/karaf/blob/f793e70612c47d16a95ef12287514c603613f2c0/management/server/src/main/java/org/apache/karaf/management/JMXSecurityMBean.java">https://github.com/bosschaert/karaf/blob/f793e70612c47d16a95ef12287514c603613f2c0/management/server/src/main/java/org/apache/karaf/management/JMXSecurityMBean.java</a><br />
<br />
<div>
<h3>
Changing permissions at Runtime</h3>
</div>
<div>
As with nearly everything in OSGi, the Configuration Admin service is dynamic, which means that you can change the information at runtime. This means that you can change the role mappings while the system is running and even for a user that is logged in. You can add or take away privileges dynamically, for example if a trusted user is all of a sudden causing havoc, you can remove the rights associated with the roles of that user dynamically and stop any further damage instantly.<br />
<br /></div>
<h3>
What's next?</h3>
I am also working on implementing RBAC for Karaf shell/console commands and will write another post about that when available on trunk.</div>
</div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com3tag:blogger.com,1999:blog-9210566578097047576.post-85746398393882110842013-04-19T01:17:00.000-07:002013-04-22T08:55:30.670-07:00Using OSGi Subsystems to deploy your ApplicationsOne of the major new specs in the OSGi R5 Enterprise release is the Subsystem specification. While this spec itself is quite large and covers a wide number of angles and use-cases, I find the simplest way to explain Subsystems really as something like <i>Application deployment for OSGi</i> where an application is comprised of a number of bundles.<br />
<br />
OSGi always encourages modular development and during development this is indeed great, because you can focus on the module at hand and have clear visibility of the impact of any changes that you make. However, once you want to deploy your application of 150 bundles this may become a little bit complicated - you certainly don't want to hand the person performing the deployment of 150 different files to deploy. You want to put them together in some way or form. This has caused many projects to come up with their own solution around this. Karaf and Eclipse have <i>features</i>, Eclipse Virgo has <i>plans</i> and Apache Aries has <i>applications</i>. The OSGi Subsystem specification now provides a standard for combining a number of bundles in a single deployable (an .esa file) which means that an .esa file can be deployed in any compliant Subsystem implementation.<br />
I am really happy that the good people at Apache Aries have recently released version 1.0 of the Aries Subsystem implementation, so you can now use this without having to build an implementation yourself.<br />
<br />
I'm going to look at how to create and use Subsystems later in this post, but first let's get an OSGi framework set up with Subsystems. The Aries implementation with its dependencies consists of 15 bundles. In my example below I'm using Equinox as the OSGi framework, but it should obviously work just as well on any other OSGi R5 compliant framework.<br />
<br />
<b>Setting up the Subsystem infrastructure</b><br />
I'm going to be using the following:<br />
<ul>
<li>Equinox 3.8.2 (which comes with Eclipse 4.2.2 or 3.8.2)</li>
<li>The gogo shell (which comes with Equinox and also with Felix)</li>
<li>The Aries Subsystem 1.0 implementation</li>
<li>dependencies of the above...</li>
</ul>
<div>
Let's start getting Equinox up and running with the shell, add the following bundles to your Equinox runtime. These all come shipped with Eclipse so if you're working in Eclipse you can simply select them in the 'OSGi Framework' launch configuration:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">org.eclipse.osgi_3.8.2.v20130124-134944</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">org.eclipse.osgi.services_3.3.100.v20120522-1822</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">org.apache.felix.gogo.runtime_0.8.0.v201108120515</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">org.apache.felix.gogo.shell_0.8.0.v201110170705</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">org.eclipse.equinox.console_1.0.0.v20120522-1841</span></div>
</div>
<div>
<br /></div>
<div>
Now add the following bundles to install the Aries Subsystem implementation (the links below can be used to download them from Maven Central):</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/subsystem/org.apache.aries.subsystem.api/1.0.0/org.apache.aries.subsystem.api-1.0.0.jar">org.apache.aries.subsystem.api_1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/subsystem/org.apache.aries.subsystem.core/1.0.0/org.apache.aries.subsystem.core-1.0.0.jar">org.apache.aries.subsystem.core_1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/subsystem/org.apache.aries.subsystem.obr/1.0.0/org.apache.aries.subsystem.obr-1.0.0.jar">org.apache.aries.subsystem.obr_1.0.0</a></span></div>
</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/application/org.apache.aries.application.api/1.0.0/org.apache.aries.application.api-1.0.0.jar">org.apache.aries.application.api_1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/application/org.apache.aries.application.modeller/1.0.0/org.apache.aries.application.modeller-1.0.0.jar">org.apache.aries.application.modeller_1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/application/org.apache.aries.application.utils/1.0.0/org.apache.aries.application.utils-1.0.0.jar">org.apache.aries.application.utils_1.0.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/blueprint/org.apache.aries.blueprint/1.1.0/org.apache.aries.blueprint-1.1.0.jar">org.apache.aries.blueprint_1.1.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/proxy/org.apache.aries.proxy/1.0.1/org.apache.aries.proxy-1.0.1.jar">org.apache.aries.proxy_1.0.1</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/aries/org.apache.aries.util/1.1.0/org.apache.aries.util-1.1.0.jar">org.apache.aries.util_1.1.0</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/felix/org.apache.felix.bundlerepository/1.6.6/org.apache.felix.bundlerepository-1.6.6.jar">org.apache.felix.bundlerepository_1.6.6</a></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/apache/felix/org.apache.felix.resolver/1.0.0/org.apache.felix.resolver-1.0.0.jar">org.apache.felix.resolver_1.0.0</a></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/eclipse/equinox/org.eclipse.equinox.coordinator/1.1.0.v20120522-1841/org.eclipse.equinox.coordinator-1.1.0.v20120522-1841.jar">org.eclipse.equinox.coordinator_1.1.0.v20120522-1841</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/eclipse/equinox/org.eclipse.equinox.region/1.1.0.v20120522-1841/org.eclipse.equinox.region-1.1.0.v20120522-1841.jar">org.eclipse.equinox.region_1.1.0.v20120522-1841</a></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/slf4j/slf4j-api/1.7.5/slf4j-api-1.7.5.jar">slf4j.api_1.7.5</a></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><a href="http://search.maven.org/remotecontent?filepath=org/slf4j/slf4j-simple/1.7.5/slf4j-simple-1.7.5.jar">slf4j.simple_1.7.5</a></span></div>
</div>
<div>
<br /></div>
<div>
I'll leave it to the reader to find a convenient way to install all these (see also the comments section with a note about how to install this on a framework other than Equinox; you need an extra bundle). You can do it with a script, using a repository, etc... There is also a <a href="http://svn.apache.org/repos/asf/aries/trunk/subsystem/subsystem-bundle/pom.xml">subsystem-bundle artifact</a> that may help. In any case, this alone validates one of the key points why Subsystems were designed in the first place. If you have an application that is formed by a number of bundles you'd really want a nice and convenient way to deploy them. Once we have a subsystem implementation in place we can do this and start deploying large applications that consist of many bundles by simply deploying a single Subsystem archive file.</div>
<div>
<br /></div>
<div>
With the above bundles started the <a href="http://www.osgi.org/javadoc/r5/enterprise/org/osgi/service/subsystem/Subsystem.html">Subsystem Service</a> is registered and ready to deploy subsystems:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">osgi> services (objectClass=*Subsystem)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> {org.osgi.service.subsystem.Subsystem}=</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> {subsystem.id=0, subsystem.state=ACTIVE, </span><span style="font-family: 'Courier New', Courier, monospace;">subsystem.version=1.0.0,</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> subsystem.type=osgi.subsystem.application,</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> subsystem.symbolicName=org.osgi.service.subsystem.root, </span><span style="font-family: 'Courier New', Courier, monospace;">...}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> "Registered by bundle:" org.apache.aries.subsystem.core_1.0.0 [6]</span></div>
<div>
<br /></div>
</div>
There is one issue though - we have no tool yet that utilizes the subsystem service so we can interact with it. It would be really nice if we could add a command to the OSGi console to do this. Using the extensible Gogo command shell this is childs play. So let's add a few subsystem commands.<br />
<br />
<b>Add some Subsystem commands to Gogo</b><br />
Gogo is becoming the de-facto standard for shell commands in an OSGi framework. It's used by Equinox, Felix, Karaf and other OSGi distributions these days. Gogo is extensible and adding a few new commands to it is as simple as registering an OSGi service.<br />
<br />
I created a bundle with only a <a href="https://github.com/bosschaert/coderthoughts/blob/master/subsystem-gogo-command/src/main/java/org/coderthoughts/subsystems/gogo/Activator.java">single class, the activator</a> which provides the following commands:<br />
subsystem:list<br />
subsystem:install <url><br />
subsystem:uninstall <id><id></id></id><id><br />
subsystem:start <id><id></id></id><id><br />
subsystem:stop <id></id><id><br />
<div class="p1">
<br /></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1">import</span> java.io.IOException;</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1">import</span> java.net.URL;</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1">import</span> java.util.*;</span></div>
<div class="p1">
<span class="s1" style="font-family: 'Courier New', Courier, monospace;">import</span><span style="font-family: 'Courier New', Courier, monospace;"> org.osgi.framework.*;</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1">import</span> org.osgi.service.subsystem.Subsystem;</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1">public</span> <span class="s1">class</span> Activator <span class="s1">implements</span> BundleActivator {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">private</span> BundleContext <span class="s2">bundleContext</span>;</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">public</span> <span class="s1">void</span> start(BundleContext context) <span class="s1">throws</span> Exception {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s2">bundleContext</span> = context;</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> Dictionary<String, Object> props = </span><span class="s1" style="font-family: 'Courier New', Courier, monospace;">new</span><span style="font-family: 'Courier New', Courier, monospace;"> Hashtable<String, Object>();</span></div>
<div class="p1">
<span class="s3" style="font-family: 'Courier New', Courier, monospace;"> props.put(</span><span style="font-family: 'Courier New', Courier, monospace;">"osgi.command.function"</span><span class="s3" style="font-family: 'Courier New', Courier, monospace;">,</span></div>
<div class="p1">
<span class="s1" style="font-family: 'Courier New', Courier, monospace;"> new</span><span class="s3" style="font-family: 'Courier New', Courier, monospace;"> String [] {</span><span style="font-family: 'Courier New', Courier, monospace;">"install"</span><span class="s3" style="font-family: 'Courier New', Courier, monospace;">, </span><span style="font-family: 'Courier New', Courier, monospace;">"uninstall"</span><span class="s3" style="font-family: 'Courier New', Courier, monospace;">, </span><span style="font-family: 'Courier New', Courier, monospace;">"start"</span><span class="s3" style="font-family: 'Courier New', Courier, monospace;">, </span><span style="font-family: 'Courier New', Courier, monospace;">"stop"</span><span class="s3" style="font-family: 'Courier New', Courier, monospace;">, </span><span style="font-family: 'Courier New', Courier, monospace;">"list"</span><span class="s3" style="font-family: 'Courier New', Courier, monospace;">});</span></div>
<div class="p1">
<span class="s3" style="font-family: 'Courier New', Courier, monospace;"> props.put(</span><span style="font-family: 'Courier New', Courier, monospace;">"osgi.command.scope"</span><span class="s3" style="font-family: 'Courier New', Courier, monospace;">, </span><span style="font-family: 'Courier New', Courier, monospace;">"subsystem"</span><span class="s3" style="font-family: 'Courier New', Courier, monospace;">);</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> context.registerService(getClass().getName(), </span><span class="s1" style="font-family: 'Courier New', Courier, monospace;">this</span><span style="font-family: 'Courier New', Courier, monospace;">, props);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span"></span> }</span></div>
<div class="p1">
<span class="s1" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div class="p1">
<span class="s1" style="font-family: 'Courier New', Courier, monospace;"> public</span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span class="s1" style="font-family: 'Courier New', Courier, monospace;">void</span><span style="font-family: 'Courier New', Courier, monospace;"> install(String url) </span><span class="s1" style="font-family: 'Courier New', Courier, monospace;">throws</span><span style="font-family: 'Courier New', Courier, monospace;"> IOException {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span"> </span> Subsystem rootSubsystem = getSubsystem(0);</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> Subsystem s = rootSubsystem.install(url, </span><span class="s1" style="font-family: 'Courier New', Courier, monospace;">new</span><span style="font-family: 'Courier New', Courier, monospace;"> URL(url).openStream());</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> System.</span><span class="s2" style="font-family: 'Courier New', Courier, monospace;">out</span><span style="font-family: 'Courier New', Courier, monospace;">.println(</span><span class="s4" style="font-family: 'Courier New', Courier, monospace;">"Subsystem successfully installed: "</span><span style="font-family: 'Courier New', Courier, monospace;"> +</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> s.getSymbolicName() + </span><span class="s4" style="font-family: 'Courier New', Courier, monospace;">"; id: "</span><span style="font-family: 'Courier New', Courier, monospace;"> + s.getSubsystemId());</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> }</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">public</span> <span class="s1">void</span> uninstall(<span class="s1">long</span> id) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> getSubsystem(id).uninstall();</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">public</span> <span class="s1">void</span> start(<span class="s1">long</span> id) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> getSubsystem(id).start();</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">public</span> <span class="s1">void</span> stop(<span class="s1">long</span> id) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> getSubsystem(id).stop();</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">public</span> <span class="s1">void</span> list() <span class="s1">throws</span> InvalidSyntaxException {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">for</span> (ServiceReference<Subsystem> ref :</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><span class="s2"> bundleContext</span>.getServiceReferences(Subsystem.<span class="s1">class</span>, <span class="s1">null</span>)) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> Subsystem s = <span class="s2">bundleContext</span>.getService(ref);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">if</span> (s != <span class="s1">null</span>) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> System.<span class="s2">out</span>.printf(<span class="s4">"%d\t%s\t%s\n"</span>, s.getSubsystemId(), s.getState(), s.getSymbolicName());</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">private</span> Subsystem getSubsystem(<span class="s1">long</span> id) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">try</span> {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">for</span> (ServiceReference<Subsystem> ref :</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="s2"> bundleContext</span>.getServiceReferences(Subsystem.<span class="s1">class</span>, <span class="s4">"(subsystem.id="</span> + id + <span class="s4">")"</span>)) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> Subsystem svc = <span class="s2">bundleContext</span>.getService(ref);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">if</span> (svc != <span class="s1">null</span>)</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">return</span> svc;</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> } <span class="s1">catch</span> (InvalidSyntaxException e) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">throw</span> <span class="s1">new</span> RuntimeException(e);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">throw</span> <span class="s1">new</span> RuntimeException(<span class="s4">"Unable to find subsystem "</span> + id);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">public</span> <span class="s1">void</span> stop(BundleContext context) <span class="s1">throws</span> Exception {}</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
<br />
I shared a bundle that contains this command, you can get it from here: <a href="http://coderthoughts.googlecode.com/files/subsystem-gogo-command-1.0.0.jar">http://coderthoughts.googlecode.com/files/subsystem-gogo-command-1.0.0.jar</a><br />
<br />
<div>
Once the above bundles are installed and everything is started I have the following bundles in my framework:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">0 ACTIVE org.eclipse.osgi_3.8.2.v20130124-134944</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">1 ACTIVE org.eclipse.osgi.services_3.3.100.v20120522-1822</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">2 ACTIVE org.apache.felix.gogo.runtime_0.8.0.v201108120515</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">3 ACTIVE org.apache.felix.gogo.shell_0.8.0.v201110170705</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">4 ACTIVE org.eclipse.equinox.console_1.0.0.v20120522-1841</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">5 ACTIVE org.apache.aries.subsystem.api_1.0.0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">6 ACTIVE org.apache.aries.subsystem.core_1.0.0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">7 ACTIVE org.apache.aries.subsystem.obr_1.0.0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">8 ACTIVE org.apache.aries.application.api_1.0.0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">9 ACTIVE org.apache.aries.application.modeller_1.0.0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">10 ACTIVE org.apache.aries.application.utils_1.0.0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">11 ACTIVE org.apache.aries.blueprint_1.1.0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">12 ACTIVE org.apache.aries.proxy_1.0.1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">13 ACTIVE org.apache.aries.util_1.1.0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">14 ACTIVE org.apache.felix.bundlerepository_1.6.6</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">15 ACTIVE org.apache.felix.resolver_1.0.0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">16 ACTIVE org.eclipse.equinox.coordinator_1.1.0.v20120522-1841</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">17 ACTIVE org.eclipse.equinox.region_1.1.0.v20120522-1841</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">18 ACTIVE slf4j.api_1.7.5, Fragments=19</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">19 RESOLV slf4j.simple_1.7.5, Master=18</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">20 ACTIVE org.osgi.service.subsystem.region.context.0_1.0.0</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">21 </span><span style="font-family: 'Courier New', Courier, monospace;">ACTIVE subsystem-gogo-command_1.0.0</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
</div>
<div>
Note that bundle 20 is a synthesized bundle created automatically by the subsystem implementation. We can safely ignore it.</div>
<div>
</div>
<br />
Now I can start doing something. Let's list the available subsystems using our new command from the <span style="font-family: 'Courier New', Courier, monospace;">subsystem-gogo-command</span> listing/bundle:<br />
<span style="font-family: Courier New, Courier, monospace;">osgi> subsystem:list</span><br />
<span style="font-family: Courier New, Courier, monospace;">0 ACTIVE org.osgi.service.subsystem.root </span><br />
<br />
At this point there is only a single subsystem: the root one.<br />
<br />
<b>Working with Subsystems</b><br />
Let's create some sample subsystems to look at what you can do.<br />
<br />
I'm going to create two basic subsystems that should allow us to play with it. The subsystem specification defines a number of different subsystem types. In this post I will be looking at the <i>feature</i> subsystem type which deploys all the bundles from the subsystem in a shared space. As if you were just installing all the bundles in a plain framework. (<i>note</i>: other subsystem types provide isolation for the subsystems.)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-qYyKKg8SaTI/UXD0ymHTKLI/AAAAAAAAA0A/srd9ph2ZOyo/s1600/BundleVennDiagram.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="188" src="http://1.bp.blogspot.com/-qYyKKg8SaTI/UXD0ymHTKLI/AAAAAAAAA0A/srd9ph2ZOyo/s400/BundleVennDiagram.png" width="400" /></a></div>
Subsystem archives typically use the .esa file extension. Both my example subsystems contain 3 bundles. The <span style="font-family: Courier New, Courier, monospace;">subsystem1.esa</span> file contains Bundle A, Bundle B and a bundle called Shared Bundle. <span style="font-family: Courier New, Courier, monospace;">subsystem2.esa</span> contains Bundle C, Bundle D and also the same Shared Bundle. Both subsystems package the Shared Bundle as they both have a dependency on it. So in order to get a fully working system for either subsystem I need that Shared Bundle. However since these are <i>feature</i> subsystems, where everything is shared I only need the Shared Bundle deployed once.<br />
<br />
Creating a subsystem file is pretty easy. The .esa file is really just a zip file that contains the embedded bundles in the root. Additionally it contains a subsystem manifest. I created mine simply using the <span style="font-family: Courier New, Courier, monospace;">jar</span> command, but you can also use tools such as the <a href="http://svn.apache.org/repos/asf/aries/trunk/esa-maven-plugin/pom.xml">esa-maven-plugin</a>. Here's what you'll find inside:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">$ jar tvf subsystem1.esa</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> 99 Fri Apr 19 08:34:08 IST 2013 OSGI-INF/SUBSYSTEM.MF</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 1181 Fri Apr 19 08:33:06 IST 2013 BundleA_1.0.0.jar</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 1058 Fri Apr 19 08:33:06 IST 2013 BundleB_1.0.0.jar</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> 906 Fri Apr 19 08:33:06 IST 2013 SharedBundle_1.0.0.jar</span><br />
<br />
<br />
As you can see the zip file contains the relevant bundles in the root plus a subsystem manifest. Here's what the SUBSYSTEM.MF file in subsystem1.esa looks like:<br />
<span style="font-family: Courier New, Courier, monospace;"> Subsystem-SymbolicName: subsystem1</span><br />
<span style="font-family: Courier New, Courier, monospace;"> Subsystem-Version: 1.0.0</span><br />
<span style="font-family: Courier New, Courier, monospace;"> Subsystem-Type: osgi.subsystem.feature</span><br />
It looks a bit like a Bundle Manifest. Most of the information in there is optional...<br />
<br />
The subsystem2.esa file is very similar. You can download the sample subsystem files from here: <a href="http://coderthoughts.googlecode.com/files/subsystem1.esa">subsystem1.esa</a> and <a href="http://coderthoughts.googlecode.com/files/subsystem2.esa">subsystem2.esa</a>.<br />
<br />
Let's deploy a subsystem:<br />
<span style="font-family: Courier New, Courier, monospace;"> osgi> subsystem:install http://coderthoughts.googlecode.com/files/subsystem1.esa</span><br />
<span style="font-family: Courier New, Courier, monospace;"> Subsystem successfully installed: subsystem1; id: 1</span><br />
<div>
<br /></div>
If we list the bundles the three bundles that were in subsystem1.esa are now added.<br />
<span style="font-family: Courier New, Courier, monospace;"> 22 INSTALLED SharedBundle_1.0.0</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 23 INSTALLED BundleA_1.0.0</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 24 INSTALLED BundleB_1.0.0</span><br />
<br />
Now let's start the subsystem:<br />
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> osgi> subsystem:start 1</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;">...</span></div>
<div class="p1">
</div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> 22 ACTIVE SharedBundle_1.0.0</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 23 </span><span style="font-family: 'Courier New', Courier, monospace;">ACTIVE</span><span style="font-family: Courier New, Courier, monospace;"> BundleA_1.0.0</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 24 </span><span style="font-family: 'Courier New', Courier, monospace;">ACTIVE</span><span style="font-family: Courier New, Courier, monospace;"> BundleB_1.0.0</span><br />
This is pretty handy: starting the subsystem will start all of the bundles that it contains!</div>
<br />
Let's add the other subsystem:<br />
<span style="font-family: Courier New, Courier, monospace;"> osgi> subsystem:install http://coderthoughts.googlecode.com/files/subsystem2.esa</span><br />
<span style="font-family: Courier New, Courier, monospace;"> Subsystem successfully installed: subsystem2; id: 2</span><br />
<span style="font-family: Courier New, Courier, monospace;"> osgi> subsystem:start 2</span><br />
<br />
Now both subsystems are active:<br />
<span style="font-family: Courier New, Courier, monospace;"> 22 ACTIVE SharedBundle_1.0.0</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 23 ACTIVE BundleA_1.0.0</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 24 ACTIVE BundleB_1.0.0</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 25 ACTIVE BundleC_1.0.0</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 26 ACTIVE BundleD_1.0.0</span><br />
And we can see that the SharedBundle was only deployed once, because it could be shared across subsystems.<br />
<br />
You can also query the subsystems known in the system:<br />
<span style="font-family: Courier New, Courier, monospace;"> osgi> subsystem:list</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 0 ACTIVE org.osgi.service.subsystem.root</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 1 ACTIVE subsystem1</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 2 ACTIVE subsystem2</span><br />
<br />
Another interesting aspect is how stopping and un-installation works. Especially in relation to the SharedBundle. I'll leave is as an exercise for the reader but you can see that the Subsystems implementation keeps track of the bundle sharing. If you only stop subsystem1, the SharedBundle will remain ACTIVE. Only when both subsystems that use the bundle are stopped the bundle will move to the RESOLVED state. Uninstalling works similarly. When you uninstall subsystem1, BundleA and BundleB will be uninstalled, but the SharedBundle won't as it is still being used by subsystem2. Only when subsystem2 is uninstalled as well all of the bundles associated with subsystem1 and subsystem2 are uninstalled.<br />
<br />
There is a lot more to talk about in relation to subsystems. For example, subsystems don't have to actually embed their dependencies. They can also download them from an <a href="http://www.osgi.org/javadoc/r5/enterprise/org/osgi/service/repository/Repository.html">OSGi Repository service</a>. In that case your .esa file can be limited to only contain a SUBSYSTEM.MF which lists what your root application bundles should be. The subsystem implementation can also use the Repository Service to automatically find transitive dependencies.<br />
<br />
In my little example, the subsystems only contain 3 bundles each, but using .esa files can become really handy when your application becomes large and contains tens or hundreds of bundles. You can even nest them, so subsystems can contain other subsystems - becoming building blocks of higher-level subsystems.<br />
<br />
OSGi Subsystems should make the distribution and deployment of larger OSGi applications much easier. The .esa file provides a portable format which allows you to hand your users a single artifact to deploy, regardless of how many bundles your application is made up of.<br />
<br />
For more information about OSGi Subsystems see chapter 134 of the OSGi R5 Enterprise specification: <a href="http://www.osgi.org/Download/Release5">http://www.osgi.org/Download/Release5</a>David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com6tag:blogger.com,1999:blog-9210566578097047576.post-19289101019429645092013-03-11T03:46:00.001-07:002013-03-11T03:46:51.692-07:00HTML5 video funI started playing with HTML5 and found that it contains some very cool stuff. Take for example the native ability to play video with the <video> tag.<br />
<br />
But your browser can do more natively these days - it can access your computer's webcam too! In fact, connecting to your webcam and streaming it out to a video tag in a html page can be done in only a few lines of HTML with some embedded JavaScript!<br />
<br />
Take a look at the <a href="https://github.com/bosschaert/coderthoughts/blob/master/htmlvideo/video.html" target="_blank">following small html page</a>:
<pre><html>
<head>
<title>HTML5 Video with no plugins!</title>
<script>
function setup() {
navigator.myGetMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
navigator.myGetMedia({video: true}, connect, error);
}
function connect(stream) {
var video = document.getElementById("my_video");
video.src = window.URL ? window.URL.createObjectURL(stream) : stream;
video.play();
}
function error(e) { console.log(e); }
addEventListener("load", setup);
</script>
</head>
<body>
<header><h1>HTML5 Video with no plugins!</h1></header>
<video id="my_video"/>
</body>
</html>
</pre>
<br />
When the page is loaded (on the load event) the setup() function is executed. This connects to your webcam through the navigator.getUserMedia() API. Currently there are a few different browser variants with various prefixes of this. When the WebRTC spec is finalized they will most likely all be unified to getUserMedia (without any prefix), which will take another 5 lines out of the above code. Once connected to the webcam the script obtains a URL to this connection and sets it as the source for the video tag. That's it!
<br />
Audio works similarly too so you can also create a combined audio/video stream.<br />
<br />
Running the webpage shows what your webcam sees in on the browser page:<br />
<a href="http://2.bp.blogspot.com/-rR240QoL2WY/UT2q-iJM9aI/AAAAAAAAAzQ/kHgffu-jPZw/s1600/html5video2.png" imageanchor="1" ><img border="0" src="http://2.bp.blogspot.com/-rR240QoL2WY/UT2q-iJM9aI/AAAAAAAAAzQ/kHgffu-jPZw/s320/html5video2.png" /></a>
<br />
And, it works on my Android phone too!<br />
<a href="http://2.bp.blogspot.com/-au2WmB-gQh8/UT2rHCthhSI/AAAAAAAAAzY/z2CyK87cxfE/s1600/Screenshot_2013-03-11-09-46-42.png" imageanchor="1" ><img border="0" src="http://2.bp.blogspot.com/-au2WmB-gQh8/UT2rHCthhSI/AAAAAAAAAzY/z2CyK87cxfE/s320/Screenshot_2013-03-11-09-46-42.png" /></a>
<br />
You can try it yourself or you can <a href="http://htmlpreview.github.com/?https://github.com/bosschaert/coderthoughts/blob/master/htmlvideo/video.html" target="_blank">launch the page from here</a>.<br />
<br />
This opens some really interesting possibilities. For example the people from html5videoguide have used this to create a video conferencing system: <br />
<a href="http://html5videoguide.net/presentations/LCA_2013_webrtc">http://html5videoguide.net/presentations/LCA_2013_webrtc</a><br /> powered by your browser and a few lines of serverside JavaScript.
You can use these APIs to record video or audio. Or turn your browser into a camera app! Combine with the new canvas APIs and you can use it to do photo editing too! All right there in your browser, all without the need for additional plugins.<br />
<br />
The code above still contains some variations to cater for slight differences that can be found in browsers, that should all be resolves as soon as the <a href="http://dev.w3.org/2011/webrtc/editor/webrtc.html" target="_blank">webrtc spec</a> goes final.<br />
<br />David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com1tag:blogger.com,1999:blog-9210566578097047576.post-38087313780840644812013-01-03T03:52:00.001-08:002013-01-03T03:52:17.275-08:00A mobile device photo organizer (using OSGi)When people think about OSGi applications they often think of complex server-side applications or embedded programs that are running as part of a set-top-box of some sort. Or they think of Eclipse-based RCP applications which are also based on OSGi.<br />
<br />
When I started writing a little program to organize photos from the various mobile devices in my home it was unlike many of those applications. It was small, it had a (Swing-based) GUI and it wasn't running on the server side. Still using OSGi helped me enormously with the development. In this blog post I will discuss how.<br />
<br />
<b>The application</b><br />
I'm sure I'm not the only person where the house is full of devices that can take photos. I like to store these pictures centrally on a NAS drive where they are neatly organized. Most devices come with a photo management solution of some sort, but they are often only working with a certain device and not with others and they mostly store the photos in different ways on disk. On top of that I want to keep the photos in a directory structure that looks like this: year/date-taken where I don't mean date-copied. All in all I couldn't find a solution that did this the way I wanted so I started a little tool for this over the holiday season.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-zuImB9tnNZE/UOVgrnND9QI/AAAAAAAAAxY/4Zx2x4-ox-Y/s1600/main.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="304" src="http://3.bp.blogspot.com/-zuImB9tnNZE/UOVgrnND9QI/AAAAAAAAAxY/4Zx2x4-ox-Y/s640/main.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
While most of the actual work is done by headless processing logic, I needed a little GUI to kick it off. While there are tons of options available, I chose to create a simple Java Swing GUI, made to look nice with one of the awesome look and feels from the <a href="http://www.jtattoo.net/" target="_blank">JTattoo</a> guys.<br />
<br />
While the project itself (which is <a href="http://code.google.com/p/coderthoughts/source/browse/#git%2FPhotoTools" target="_blank">available here</a>) has the ASL2 license, I am using a variety of libraries to extract information from photo files, video files and for example to access my Android device, because I want to be able to copy photos directly from that.<br />
<br />
<b>I need modularization</b><br />
Most of the libraries that I'm using have a license compatible with my ASL2 license, but I really wanted to access my Android phone directly. Android phones, as well as other mobile devices cannot easily be mounted as a directory on the file system. They need to be accessed using <a href="http://en.wikipedia.org/wiki/Media_Transfer_Protocol" target="_blank">MTP</a>. I found a library that allowed me to access my phone from Java: <a href="http://code.google.com/p/jusbpmp/" target="_blank">jusbpmp</a>. It worked fine for the most part but there was one issue: it's GPL-licensed. I don't want to get into which open source license is better, but the viral effects of GPL are well known and I already decided that my application was to be ASL2-licensed. I didn't want to relicense my whole project because of the fact that one dependency has this other license.<br />
<br />
If I could single out the functionality that uses this library and only license that piece GPL (as needed) and then plug it into the rest of my application that would limit the scope and the amount of code that is required to have the GPL license. Ideally I want that component to be loosely coupled so that it can be downloaded separately and plugged into the main application.<br />
<br />
OSGi Bundles to the rescue. And in particular OSGi Services! OSGi Services use a contribution model where service implementations contribute them to the Service Registry. Consumers find them there. It all works by defining the Service APIs in a separate module that they all communicate through.<br />
<br />
The MTP library allows me to access the files on my Android phone. So I started by defining an API module to create an <span style="font-family: Courier New, Courier, monospace;">Iterable</span> that can give me entries that serve the photo information from anything any type of source. The <a href="http://code.google.com/p/coderthoughts/source/browse/PhotoTools/phototools.core/src/org/coderthoughts/phototools/api/PhotoIterable.java" target="_blank">PhotoIterable interface</a> can represent a file directory-based photo store, but also one that comes from an MTP device or other device. It looks somewhat like this:<br />
<br />
<div class="p1">
</div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1">public</span> <span class="s1">interface</span> PhotoIterable <span class="s1">extends</span> Iterable<PhotoIterable.Entry><photoiterable .entry=".entry"><photoiterable .entry=".entry"> {</photoiterable></photoiterable></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s2">/**</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * Get a human readable identification of the location.</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * <span class="s3">@return</span> The location string.</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> */</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> String getLocationString();</span></div>
<div class="p3">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s2">/**</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * This class represents a photo object that can be read.</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> */</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">public</span> <span class="s1">interface</span> Entry {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s2">/**</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * Returns the file name to use for the photo.</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * <span class="s3">@return</span> The file name without path information, for example IMG_01429.JPG</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> */</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> String getName();</span></div>
<div class="p3">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s2">/**</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * <span class="s3">@return</span> The stream to read the photo bytes from.</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> */</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> InputStream getInputStream();</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
<br />
Now I need a way to obtain such a <span style="font-family: Courier New, Courier, monospace;">PhotoIterable</span>. Typically the user wants to select a location on the device to copy the photos from (instead of getting all the image files on the device) and to do this I defined the <a href="http://code.google.com/p/coderthoughts/source/browse/PhotoTools/phototools.core/src/org/coderthoughts/phototools/api/PhotoSource.java" target="_blank">PhotoSource</a> interface. This is what I will register in the OSGi Service Registry. For every supported type of source a corresponding service will be registered. The core bundle ships with one for loading photos from a <a href="http://code.google.com/p/coderthoughts/source/browse/PhotoTools/phototools.core/src/org/coderthoughts/phototools/impl/DirectoryPhotoSource.java" target="_blank">file system directory</a>, and the phototools.mtp bundle registers one <a href="http://code.google.com/p/coderthoughts/source/browse/PhotoTools/phototools.mtp/src/org/coderthoughts/phototools/mtp/impl/MTPPhotoSource.java" target="_blank">to handle MTP devices</a>.<br />
<div class="p2">
<span class="s2"><br /></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><span class="s2">public</span> <span class="s2">interface</span> PhotoSource {</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s3">/**</span></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> * The label for this source, for example 'File System Directory' or 'Android'.</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> * <span class="s4">@return</span> The label to use.</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> */</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> String getLabel();</span></div>
<div class="p3">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s3">/**</span></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> * Calling this method should open a selection window where the user can select where the</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> * photos are to be copied or <span class="s1">downloaded</span> from.</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> * <span class="s4">@return</span> A PhotoIterable to obtain photos from the selected location.</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> */</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> PhotoIterable getPhotoIterable();</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
<div class="p2">
<br /></div>
<div class="p2">
At this stage I have 3 bundles: the API bundle, the core implementation bundle and the MTP implementation bundle.</div>
<div class="p2">
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-yP1iqnEi4q0/UOVmpzq7xKI/AAAAAAAAAxw/qK90UonjZl4/s1600/PhotoTools.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="265" src="http://2.bp.blogspot.com/-yP1iqnEi4q0/UOVmpzq7xKI/AAAAAAAAAxw/qK90UonjZl4/s400/PhotoTools.png" width="400" /></a></div>
<br />
This separation is nice because:</div>
<div class="p2">
</div>
<ul>
<li>The API bundle is small. Anyone who wants to write support for another mobile device has only a very small number of interfaces to look at. No distracting implementation code to get in the way.</li>
<li>I can find all the <span style="font-family: 'Courier New', Courier, monospace;">PhotoSource</span> implementations by looking them up in the OSGi Service Registry, for example with <span style="font-family: Courier New, Courier, monospace;">bundleContext.getServiceReferences(PhotoSource.class, null))</span></li>
<li>I can contribute support for additional devices without changing the rest of the code. Just add the bundle that contains the support (and registers the PhotoSource service) and it will appear (even the GUI will react to this).</li>
<li>I didn't have to write my own plugin mechanism. OSGi Bundles and Services provide that to me.</li>
<li>I could isolate the functionality that depends on a GPL library in a separate bundle. This means that the main application is still ASL2 licensed. The GPL-based bundle is optional and can be provided separately if I want to create a pure ASL2-based product.</li>
</ul>
<div>
The PhotoSource that the MTP provide bundle contributes is visible as a widget in the main screen (the <b><i>Mobile Device via USB</i></b> Radio Button). When I click <b>Select</b> I can see the custom MTP selection GUI in action:</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-Kelqhbzhd40/UOVgsFd_jZI/AAAAAAAAAxc/atjL7lZco3w/s1600/selectandroid.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://3.bp.blogspot.com/-Kelqhbzhd40/UOVgsFd_jZI/AAAAAAAAAxc/atjL7lZco3w/s320/selectandroid.jpg" width="320" /></a></div>
<br /></div>
<div>
<b>More OSGi Bundles and Services to keep things clean</b></div>
While the phototools.core bundle can deal with extracting metadata from some of the Photo formats (thanks to <a href="http://code.google.com/p/metadata-extractor/" target="_blank">Drew's metadata-extractor</a>) I also want my application to handle movie files. I found another nice library that could handle mp4 files for me: <a href="https://code.google.com/p/mp4parser/" target="_blank">Sebastian's mp4parser</a>. Although mp4parser is ASL2 licensed I didn't want to include it the core bundle because it was getting fairly heavy on embedded libraries and also there were still a number of photo/video formats unsupported. Getting the core bundle to support them all didn't seem the right thing to do. Allowing separate bundles to contribute a format handler did! So I defined an additional Service API:<br />
<div>
<br />
<div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1">public</span> <span class="s1">interface</span> <a href="http://code.google.com/p/coderthoughts/source/browse/PhotoTools/phototools.core/src/org/coderthoughts/phototools/api/PhotoMetadataProvider.java" target="_blank">PhotoMetadataProvider</a> {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s2">/**</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * Get <span class="s3">metadata</span> for a photo or video file</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * <span class="s4">@param</span> f The file to process.</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * <span class="s4">@return</span> The <span class="s3">metadata</span> found.</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> */</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> Metadata getMetaData(File f);</span></div>
<div class="p3">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">public</span> <span class="s1">interface</span> Metadata {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s2">/**</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * Obtain the date the photo was taken (not necessarily the file date).</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * <span class="s4">@return</span> The date taken.</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> */</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> Date getDateTaken();</span></div>
<div class="p3">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s2">/**</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * Obtain a small preview file for the photo or movie. If available,</span><br />
<span style="font-family: Courier New, Courier, monospace;"> * the preview file will always be a JPEG file.</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> * <span class="s4">@return</span> The preview file.</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> */</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> File getPreviewFile();</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
<div class="p1">
<br /></div>
<div class="p1">
I can find an appropriate Metadata Provider for my photos by looking up one from the OSGi Service Registry that is registered for the relevant extension. This is done by using OSGi Service Registration properties. Each Photo Metadata Provider registers the formats it can handle with the <span style="font-family: 'Courier New', Courier, monospace;">format</span> property. Then I can look one up by querying on the extension of the file I want to process e.g:</div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> ServiceReferences[] refs = bundleContext.getServiceReferences(</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> PhotoMetadataProvider.class, "(format=.<span class="s1">jpeg</span>)");</span></div>
<div class="p1">
</div>
<div class="p2">
When I find a service that can handle my format, get it to process the file:</div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"> PhotoMetadataProvider p = bundleContext.getService(<span class="s1">sref</span>);</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1"> Metadata</span> <span class="s1">metadata</span> = p.getMetaData(myPhotoOrVideoFile);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> Date dateTaken = metadata.getDateTaken();</span></div>
<div class="p1">
<br /></div>
<div class="p1">
The mp4 handling code is now nicely separated in its own bundle. In addition, if I ever want to add support for other formats (.AVI for example) I can do this by simply adding another bundle, which drastically reduces the scope of my changes and also reduces the amount of code that I may have to look at.</div>
<div class="p1">
<br /></div>
<div class="p1">
Conclusion? Well my little project is not finished yet, it's still work in progress. But OSGi really helped me by providing a nice plug-in architecture and its modularity almost forced me to write nice interface-based components which will be easier to maintain in the long run. Because the bundles have a clear scope they tend to be quite small and when making changes the amount of code you have to look at as a developer is much smaller than if this was part of a monolithic application. This is great because I generally only sporadically have time to go back to my hobby projects and having less code to refresh my brain is good :) Oh, and the fact that I'm using OSGi is completely hidden to the end user. It's really just an architectural choice under the covers.</div>
<div class="p1">
<br /></div>
<div class="p1">
Happy new year, everyone.</div>
<br /></div>
</div>
David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com0tag:blogger.com,1999:blog-9210566578097047576.post-71525915302857993182012-08-28T05:34:00.002-07:002012-08-28T05:34:30.154-07:00Dynamic Provisioning OSGi Cloud EcosystemsIn my recent posts I looked at building a cloud ecosystem using OSGi and adding extra control to remote service invocations in such ecosystems.<br />
<br />
In this post I'm starting to look at the provisioning of such ecosystems. OSGi frameworks are <i>dynamic </i>which means they can dynamically be assigned a function and when the need arises they can be dynamically repurposed. This repurposing is interesting in particular because it saves you from sending the cloud vm image over the network again. And those images can be big. You can reuse the image that you started with, changing its function and role over time. <br />
<br />
In my previous post I was using pre-configured VM images, each representing a different deployment in the ecosystem. In this post I will start out with a number of identical images. In addition, I will deploy one other special image: the provisioner. However, since that one is also running in an OSGi framework, even that one can be repurposed or moved if the need arises.<br />
<br />
You'll see that, as in the previous posts, none of the OSGi bundles are statically configured to know where the remote services they use are located. They are all part of the dynamic OSGi Cloud Ecosystem which binds all services together via the OSGi Remote Services compliant Discovery mechanism.<br />
I wrote a very simple demo provisioner which keeps an eye on what frameworks are up, provisions them with OSGi bundles, and if one goes down it redeploys services to an alternative.<br />
<br />
Altogether, the setup is as follows:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-HnKj-9dirSI/UCvjrurbkqI/AAAAAAAAAsA/zGJ1QD4f-M8/s1600/CloudProvisioning.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="257" src="http://3.bp.blogspot.com/-HnKj-9dirSI/UCvjrurbkqI/AAAAAAAAAsA/zGJ1QD4f-M8/s400/CloudProvisioning.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Provisioning an ecosystem of initially identical OSGi frameworks</td></tr>
</tbody></table>
The ecosystem contains 5 OSGi frameworks, 4 of which are identical: osgi1, osgi2, osgi3 and web. The only difference wrt to the last one is that it has a well-known hostname because it's hosting the web component that will be accessed from the outside world. The 5th framework has the same baseline as the first 4, but it acts as the provisioner/management agent, so this one has some extra bundles deployed that can provision OSGi bundles in the other frameworks in the ecosystem.<br />
<br />
To be able to work with such a dynamic ecosystem of OSGi frameworks, the provisioner needs to monitor the frameworks available in it. Since these frameworks are represented as OSGi services this monitoring can simply be done through OSGi service mechanics, in my case I'm using a simple <a href="http://www.osgi.org/javadoc/r4v42/org/osgi/util/tracker/ServiceTracker.html" target="_blank">ServiceTracker</a>, but you could do the same with Blueprint or Declarative Services or any other OSGi services-based framework.<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
Filter filter = bundleContext.createFilter(</div>
<div style="font-family: "Courier New",Courier,monospace;">
"(&(objectClass=org.coderthoughts<span style="font-family: Times,"Times New Roman",serif;">...</span>api.OSGiFramework)(service.imported=*))");<br />frameworkTracker = new ServiceTracker(bundleContext, filter, null) {<br /> @Override<br /> public Object addingService(ServiceReference reference) {<br /> handleTopologyChange();<br /> return super.addingService(reference);<br /> }<br /><br /> @Override<br /> public void removedService(ServiceReference reference, Object service) {<br /> handleTopologyChange();<br /> super.removedService(reference, service);<br /> }</div>
<div style="font-family: "Courier New",Courier,monospace;">
}; </div>
My ServiceTracker listens for services with OSGiFramework as <span style="font-family: "Courier New",Courier,monospace;">objectClass</span> which have the <span style="font-family: "Courier New",Courier,monospace;">service.imported</span> property set. This means that it listens to remote frameworks only. Whenever a framework appears or disappears the handleTopologyChange() will be called.<br />
<br />
For this posting I wrote a really simple demo provisioner that has the following rules that are evaluated when the topology changes (frameworks arrive and disappear in the ecosystem):<br />
<ul>
<li>Deploy the web bundles to a framework with an ip address starting with 'web-'.</li>
<li>Deploy one instance of the service provider bundles to the framework <i>with the largest amount of memory available</i>. </li>
<li>Ensure that if the vm hosting the service provider bundles disappears for some reason, they get redeployed elsewhere. Note that by virtue of using the OSGi Remote Services Discovery functionality the client will automatically find the moved service.</li>
</ul>
<div>
Obviously these aren't real production rules, but they give you an idea what you can do in a management/provisioning agent:</div>
<div>
<ul>
<li>Decide what to deploy based on image characteristics, I used image name for the web component, but you can also use other metadata such as image type (Red Hat OpenShift/Amazon/Azure/etc), location (e.g. country where the VM is hosted) or other custom metadata.</li>
<li>Dynamically select a target image based on a runtime characteristic, in my case I'm using memory available.</li>
<li>Keep an eye on the topology. When images disappear or new ones arrive the provisioner gets notified - simply through OSGi Service dynamics - and changes can be made accordingly. For example a redeploy action can be taken when a framework disappears.</li>
<li>No hardcoded references to where the target services live. The service consumer use ordinary OSGi service APIs to look up and invoke the services in the cloud ecosystem. I'm not using a component system like Blueprint or DS here but you can also use those, of course. </li>
</ul>
</div>
<div>
the demo provisioner code is here: <a href="http://github.com/bosschaert/osgi-cloud-prov-src/blob/0.3/cloud-prov-demo/src/main/java/org/coderthoughts/cloud/provisioning/demo/impl/DemoProvisioner.java" target="_blank">DemoProvisioner.java</a></div>
<br />
As before, I'm using <a href="http://openshift.redhat.com/" target="_blank">Red Hat's free OpenShift</a> cloud instances, so anyone can play with this stuff, just create a free account and go.<br />
<br />
My basic provisioner knows the sets of bundles that need to be deployed in order to get a resolvable system. In the future I'll write some more about the OSGi Repository and Resolver services which can make this process more declarative and highly flexible.<br />
<br />
However, the first problem I had was: what will I use to do the remote deployment to my OSGi frameworks? There are a number of OSGi specs that deal with remote management, most notably JMX. But remote JMX deployment doesn't really work that well in my cloud environment as it requires me to open an extra network port, which I don't have. Probably the best solution would be based on the REST API that is being worke<span style="font-family: inherit;">d on in the EEG (RFC 182) and hook that in with the OSGi HTTP service. For the moment, I decided to create a simple deployment service API that works well with OSGi Remote Services:</span><br />
<br />
<span style="font-family: Courier New, Courier, monospace;"><span class="s1">public</span> <span class="s1">interface</span> <a href="http://github.com/bosschaert/osgi-cloud-prov-src/blob/0.3/cloud-prov-remsvc/src/main/java/org/coderthoughts/cloud/provisioning/api/RemoteDeployer.java" target="_blank">RemoteDeployer</a> {</span><br />
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">long</span> getBundleID(String location);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> String getSymbolicName(<span class="s1">long</span> id);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">long</span> installBundle(String location, <span class="s1">byte</span> [] base64Data);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">long</span> [] listBundleIDs();</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">void</span> startBundle(<span class="s1">long</span> id);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">void</span> stopBundle(<span class="s1">long</span> id);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> <span class="s1">void</span> uninstallBundle(<span class="s1">long</span> id);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">This gives me very basic management control over remote frameworks and allows me to find out what they have installed. It's a simple API that's designed to work well with Remote Services. Note that the installBundle() method takes the actual bundle content as base-64 encoded bytes. This saves me from having to host the bundles as downloadable URLs from my provisioner framework, and makes it really easy to send over the bytes.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">I'm registering my <a href="http://github.com/bosschaert/osgi-cloud-prov-src/blob/0.3/cloud-prov-remsvc/src/main/java/org/coderthoughts/cloud/provisioning/api/RemoteDeployer.java" target="_blank">RemoteDeployer</a> service in each framework in the ecosystem using the Remote Services based cloud ecosystem service distribution I <a href="http://coderthoughts.blogspot.ie/2012/07/controlling-osgi-services-in-cloud.html" target="_blank">described in my previous post</a>. This way the provisioner can access the service in the remote frameworks and deploy bundles to each.</span><br />
<br />
<span style="font-family: inherit;">That's pretty much it! I've simplified the <a href="http://github.com/bosschaert/osgi-cloud-disco-demo/blob/0.3/cloud-disco-demo-web-ui/src/main/java/org/coderthoughts/cloud/demo/webui/impl/MyServlet.java" target="_blank">web servlet</a> a little from the previous post to only show a single TestService invocation, let's try it out... </span><br />
<br />
<span style="font-size: small;"><b>Try it out!</b></span><br />
First thing I'm doing is start the Discovery Server in the ecosystem. This is done exactly as in my earlier post: <a href="http://coderthoughts.blogspot.ie/2012/06/cloud-ecosystems-with-osgi.html" target="_blank">Cloud Ecosystems with OSGi</a>. <br /><br />
Having the discovery server running, I begin by launching my provisioner framework. After that I'll be adding the empty OSGi frameworks one-by-one to see how it all behaves.<br />
Finally I'll kill a VM to see redeployment at work.<br />
<br />
<b>The common OSGi Framework bits</b><br />
The provisioner framework is created by first adding the baseline infrastructure to a new <a href="https://openshift.redhat.com/community/blogs/a-paas-that-runs-anything-http-getting-started-with-diy-applications-on-openshift" target="_blank">OpenShift DIY</a> clone:<br />
<div style="font-family: "Courier New",Courier,monospace;">
<div>
<div>
$ git clone ssh://blahblahblah@provisioner-coderthoughts.rhcloud.com/~/git/provisioner.git/</div>
<div>
$ cd provisioner</div>
</div>
</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git fetch https://github.com/bosschaert/osgi-cloud-infra.git</div>
<div style="font-family: "Courier New",Courier,monospace;">
<div>
$ git merge -Xtheirs FETCH_HEAD </div>
</div>
Don't forget to set up the Discovery SSH tunnel, which is also described in <a href="http://coderthoughts.blogspot.ie/2012/06/cloud-ecosystems-with-osgi.html" target="_blank">Cloud Ecosystems with OSGi</a>.<br />
<br />
<b>The specific Provisioner bits</b><br />
Specific to the provisioner vm are the provisioner bundles. The demo provisioner I wrote can be built and deployed from the <a href="http://github.com/bosschaert/osgi-cloud-prov-src" target="_blank">osgi-cloud-prov-src</a> github project:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ git clone https://github.com/bosschaert/osgi-cloud-prov-src.git<br />$ cd osgi-cloud-prov-src</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ mvn install </div>
<div style="font-family: "Courier New",Courier,monospace;">
$ ./copy-to-provisioner.sh .../provisioner</div>
finally add the changed configuration and provisioner bundle to git in the provisioner clone, then commit and push:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ cd .../provisioner </div>
<span style="font-family: "Courier New",Courier,monospace;">$ git add osgi</span><br />
<div style="font-family: "Courier New",Courier,monospace;">
$ git commit -m "Provisioner VM"<br />$ git push </div>
<br />
The provisioner image will be uploaded to the cloud and if you have a console visible (which you'll get by default by doing a <span style="font-family: "Courier New",Courier,monospace;">git push</span> on OpenShift) you will eventually see the OSGi Framework coming up.<br />
<br />
<b>Adding the identical OSGi Frameworks</b><br />
I'm launching 3 identical cloud images all containing an unprovisioned OSGi framework. As they become active my demo provisioner will exercise the <a href="http://github.com/bosschaert/osgi-cloud-prov-src/blob/0.3/cloud-prov-remsvc/src/main/java/org/coderthoughts/cloud/provisioning/api/RemoteDeployer.java" target="_blank">RemoteDeployer</a> API to provision them with bundles.<br />
<br />
First add one called <span style="font-family: "Courier New",Courier,monospace;">web-<i>something</i></span>. Mine is called web-coderthoughts. The demo provisioner looks for one with the 'web-' prefix and will deploy the servlet there, this will allow us to use a fixed URL in the browser to see what's going on. This is exactly the same as what is descibed above in the <b>common OSGi Framework bits</b>:<br />
<div style="font-family: "Courier New",Courier,monospace;">
<div>
<div>
$ git clone ssh://blahblahblah@web-coderthoughts.rhcloud.com/~/git/web.git/</div>
<div>
$ cd web</div>
</div>
</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git fetch https://github.com/bosschaert/osgi-cloud-infra.git</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git merge -Xtheirs FETCH_HEAD</div>
And don't forget to set up the SSH tunnel.<br />
<br />
Once the web framework is started, the provisioner starts reacting, if you have a console, you'll see messages appear such as:<br />
<div style="font-family: "Courier New",Courier,monospace;">
remote: *** Found web framework<br />remote: *** Web framework bundles deployed</div>
<br />
Great! Let's look at the servlet page:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-gl8yxfxC5zw/UDyT-EHi8cI/AAAAAAAAAuM/dGZdvIJGuw8/s1600/shot1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="296" src="http://4.bp.blogspot.com/-gl8yxfxC5zw/UDyT-EHi8cI/AAAAAAAAAuM/dGZdvIJGuw8/s640/shot1.png" width="640" /></a></div>
So far so good. We are seeing 2 frameworks in the ecosystem: the web framework and the provisioner. No invocations where made on the TestService because it isn't deployed yet.<br />
<br />
So let's deploy our two other frameworks. The steps are the same as for the web framework, except the vm names are osgi1 and osgi2 and reload the web page:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-j6ZBtKEo9QE/UDyZURtJEzI/AAAAAAAAAuc/8kdaMTx7BKI/s1600/shot2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="215" src="http://3.bp.blogspot.com/-j6ZBtKEo9QE/UDyZURtJEzI/AAAAAAAAAuc/8kdaMTx7BKI/s640/shot2.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">click to see full size</td></tr>
</tbody></table>
The two new frameworks are now visible in the list. Our TestService, which is <a href="http://github.com/bosschaert/osgi-cloud-disco-demo/blob/0.3/cloud-disco-demo-web-ui/src/main/java/org/coderthoughts/cloud/demo/webui/impl/MyServlet.java#L142" target="_blank">invoked by the servlet/web vm</a>, is apparently deployed to osgi1. You can see that by comparing the UUIDs.<br />
<b><br /></b>
<b>Reacting to failure</b><br />
Let's kill the framework hosting the TestService and see what happens :)<br />
OpenShift has the following command for this, if you're using a different cloud provider I'm sure they provide a means to stop a Cloud VM image.<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ rhc app stop -a osgi1</div>
<br />
Give the system a few moments to react, then refresh the web page:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-stY3lro9WOY/UDybF-JLjvI/AAAAAAAAAuk/f0rTHaGQhD4/s1600/shot3.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="216" src="http://4.bp.blogspot.com/-stY3lro9WOY/UDybF-JLjvI/AAAAAAAAAuk/f0rTHaGQhD4/s640/shot3.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">click to see full size</td></tr>
</tbody></table>
The osgi1 framework has disappeared from the list and the TestService invocation reports that it's now running in the osgi2 framework. The provisioner has reacted to the disappearance of osgi1 and reprovisioned the TestService bundles to osgi2. The service client (the servlet) was automatically reconfigured to use osgi2 throught the Remote Services Discovery mechanism. No static configuration or binding file, simply OSGi service dynamics at work across VMs in the cloud.<br />
<br />
<b>Repurposing</b> <br />
Obviously the provisioner in this posting was very simplistic and only really an example of how you could write one. Additionally, things will become even more interesting when you start using an OSGi Repository Service to provide the content to the provisioner. That will bring a highly declarative model to the provisioner and allows you do create a much more generic management agent. I'll write about that in a future post.<br />
<br />
Another element not worked out in this post is the re-purposing of OSGi frameworks. However it's easy to see that with the primitives available this can be done. Initially the frameworks in the cloud ecosystem are identical, and although provisioner will fill them with different content, the content can be removed (the bundles can be uninstalled) and the framework can be given a different role by the provisioner when the need arises.<br />
<br />
<i>Note that all the code in the various github projects used in this post has been tagged <span style="font-family: "Courier New",Courier,monospace;">0.3</span>.</i>David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com1tag:blogger.com,1999:blog-9210566578097047576.post-23560094492573573432012-07-12T02:59:00.000-07:002012-07-12T03:05:18.900-07:00Controlling OSGi Services in Cloud EcosystemsIn <a href="http://coderthoughts.blogspot.com/2012/06/cloud-ecosystems-with-osgi.html" target="_blank">my previous post</a> I looked at the basics of setting up an OSGi Cloud Ecosystem. With that as a basis I'm going to look <span style="background-color: white;">at an issue that was brought up during the second OSGi Cloud Workshop, held this year at EclipseCon in Washington, where <a href="https://mail.osgi.org/pipermail/cloud-workshop/2012-March/000112.html" target="_blank">this list of ideas</a> was produced. </span><span style="background-color: white;">One of topics on this list is about new ways in which services can fail in a cloud scenario. </span><span style="background-color: white;">For example, a service invocation might fail because the service consumer's credit card has expired. Or the number of invocations allocated to a certain client has been used up, etc.</span><br />
<br />
<span style="background-color: white;">In this post I'll be looking at how to address this in the OSGi Cloud Ecosystem architecture that I've been writing about.</span><br />
<br />
First lets look at the scenario where a cloud service grants a maximum number of invocations to a certain consumer. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-WHEA4FF8tWw/T_3POJA88MI/AAAAAAAAArI/7W8lbe51pbg/s1600/invocationpolicy.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="150" src="http://1.bp.blogspot.com/-WHEA4FF8tWw/T_3POJA88MI/AAAAAAAAArI/7W8lbe51pbg/s400/invocationpolicy.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">An invocation policy for a remote OSGi service</td></tr>
</tbody></table>
<span style="background-color: white;"><br /></span><br />
<span style="background-color: white;">From the consumer side, I'm simply invoking my remote demo TestService as usual:</span><br />
<div style="font-family: "Courier New",Courier,monospace;">
TestService ts = ... from the Service Registry ...</div>
<span style="font-family: 'Courier New',Courier,monospace;"> String result = ts.doit();</span><br />
<br />
On the Service Provider side we need to add additional control. As this control (normally) only applies to remote consumers from another framework in the cloud I've extended the OSGi Remote Services implementation to provide extra control points. I did this on <a href="https://github.com/bosschaert/cxf-dosgi/tree/cloud" target="_blank">a branch of the Apache CXF-DOSGi project</a>, which is the Remote Services implementation I'm using. I came up with the idea of a RemoteServiceFactory. Conceptually a bit similar to a normal <a href="http://www.osgi.org/javadoc/r4v42/org/osgi/framework/ServiceFactory.html" target="_blank">OSGi ServiceFactory</a>. Where the normal OSGi ServiceFactory can provide a new service instance for each client <i>bundle</i>, a RemoteServiceFactory can provide a new service (and exert other control) for each <i>remote client</i>. I'm currently distinguishing each client by IP address, not completely sure whether this covers all the cases, maybe some sort of a security context would also make sense in here.<br />
My initial version of the RemoteServiceFactory interface looks like this:<br />
<span style="font-family: 'Courier New',Courier,monospace;"> public interface <a href="https://github.com/bosschaert/cxf-dosgi/blob/46f328ddefb2e93db0318215c94d16c2d0f5735a/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/RemoteServiceFactory.java" target="_blank">RemoteServiceFactory</a> {</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> public Object getService(String clientIP, ServiceReference reference);</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> public void ungetService(String clientIP, ServiceReference reference, Object service);</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> }</span><br />
<br />
Now we can put the additional controls in place. I can limit service invocations to 3 per IP address:<br />
<span style="font-family: 'Courier New',Courier,monospace;">public class <a href="https://github.com/bosschaert/osgi-cloud-disco-demo/blob/0.2/cloud-disco-demo-provider/src/main/java/org/coderthoughts/cloud/demo/provider/impl/TestServiceRSF.java" target="_blank">TestServiceRSF</a> implements RemoteServiceFactory, ... {</span><br />
<div class="line" id="LC19">
<span style="font-family: 'Courier New',Courier,monospace;"><span style="background-color: white;"> </span><span style="background-color: white;">ConcurrentMap<String, AtomicInteger> </span><span class="s2" style="background-color: white;">invocationCount</span><span style="background-color: white;"> = </span></span><span style="background-color: white; font-family: 'Courier New',Courier,monospace;"><span class="s1" style="background-color: white;">new</span><span style="background-color: white;"> ConcurrentHashMap</span></span><span style="background-color: white; font-family: 'Courier New',Courier,monospace;"><</span><span style="background-color: white; font-family: 'Courier New',Courier,monospace;"><span style="background-color: white;">>();</span></span><br />
<br /></div>
<span style="font-family: 'Courier New',Courier,monospace;"><string, atomicinteger=""> @Override<br /> public Object getService(String clientIP, ServiceReference reference) {<br /> AtomicInteger count = getCount(clientIP);<br /> int amount = count.incrementAndGet();<br /> if (amount > 3)<br /> throw new InvocationsExhaustedException("Maximum invocations reached for: " + clientIP);<br /><br /> return </string,></span><span style="font-family: 'Courier New',Courier,monospace;">new TestServiceImpl()</span><span style="font-family: 'Courier New',Courier,monospace;"><string, atomicinteger="">; // or reuse an existing one<br /> }</string,></span><span style="background-color: white; font-family: 'Courier New',Courier,monospace;"> </span><br />
<br />
<span style="background-color: white; font-family: 'Courier New',Courier,monospace;"> private AtomicInteger getCount(String ipAddr) {</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> AtomicInteger newCnt = new AtomicInteger();</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> AtomicInteger oldCnt = invocationCount.putIfAbsent(ipAddr, newCnt);</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> return oldCnt == null ? newCnt : oldCnt;</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> }</span><br />
<br />
A RemoteServiceFactory allows me to add other policies as well, for example it can prevent concurrent invocations from a single consumer, <a href="https://github.com/bosschaert/osgi-cloud-disco-demo/blob/0.2/cloud-disco-demo-provider/src/main/java/org/coderthoughts/cloud/demo/provider/impl/LongRunningServiceRSF.java" target="_blank">see here for an example</a>, select provided functionality based on the client or even charge the customer per invocation.<br />
<br />
To register the RemoteServiceFactory in the system I'm currently adding it as a service registration property:<br />
<div style="font-family: "Courier New",Courier,monospace;">
public class <a href="https://github.com/bosschaert/osgi-cloud-disco-demo/blob/0.2/cloud-disco-demo-provider/src/main/java/org/coderthoughts/cloud/demo/provider/impl/Activator.java" target="_blank">Activator</a> implements BundleActivator {</div>
<div style="font-family: "Courier New",Courier,monospace;">
// ...<br />
public void start(BundleContext context) throws Exception { </div>
<div style="font-family: "Courier New",Courier,monospace;">
TestService ts = new TestServiceImpl();<br />
Dictionary<String, Object> tsProps = new Hashtable<>();<br />
tsProps.put("service.exported.interfaces", "*");<br />
tsProps.put("service.exported.configs", "org.coderthoughts.configtype.cloud");<br />
<span style="font-family: 'Courier New',Courier,monospace;">RemoteServiceFactory</span> tsControl = new TestServiceRSF(context);<br />
tsProps.put("org.coderthoughts.remote.service.factory", tsControl);<br />
tsReg = context.registerService(TestService.class.getName(), ts, tsProps);<br />
...</div>
<br />
<h3>
More control to the client</h3>
Being able to control the service as described above is nice, but seeing an exception in the client when trying to invoke the service isn't great. It would be good if the client could prevent such a situation by asking the framework whether a service it's hosting will accept invocations. For this I added service variables to the OSGiFramework interface. You can ask frameworks in the ecosystem for metadata regarding the services it provides:<br />
<span style="font-family: 'Courier New',Courier,monospace;"> public interface <a href="https://github.com/bosschaert/osgi-cloud-infra-src/blob/0.2/cloud-infra-framework-service/src/main/java/org/coderthoughts/cloud/framework/service/api/OSGiFramework.java" target="_blank">OSGiFramework</a> {</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> String getServiceVariable(long serviceID, String name);</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> }</span><br />
<br />
I implemented this idea using the OSGi Monitor Admin service (chapter 119 of the <a href="http://www.osgi.org/Download/Release5" target="_blank">Compendium Specification</a>) <br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-G4hRALOv97g/T_3PQIMc-fI/AAAAAAAAArQ/FR8s2ZCjUpo/s1600/servicevariables.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="201" src="http://4.bp.blogspot.com/-G4hRALOv97g/T_3PQIMc-fI/AAAAAAAAArQ/FR8s2ZCjUpo/s400/servicevariables.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Service Variables are accessed via OSGi Monitor Admin</td></tr>
</tbody></table>
<br />
<span style="background-color: white;">From <a href="https://github.com/bosschaert/osgi-cloud-disco-demo/blob/0.2/cloud-disco-demo-web-ui/src/main/java/org/coderthoughts/cloud/demo/webui/impl/MyServlet.java#L134" target="_blank">my client servlet</a> I'm checking the service status before calling the service:</span><br />
<div class="p1">
<span style="font-family: 'Courier New',Courier,monospace;"> fw.getServiceVariable(serviceID, <span style="background-color: white;">OSGiFramework.</span><span class="s2" style="background-color: white;">SV_STATUS</span><span style="background-color: white;">);</span></span></div>
<div class="p1">
<blockquote class="tr_bq">
<span style="background-color: white;"><i>(note given a service reference, you can find out whether it's remote by checking for the </i><span style="font-family: 'Courier New',Courier,monospace;">service.imported</span><i> property, you can find the hosting framework instance by matching their </i><span style="font-family: 'Courier New',Courier,monospace;">endpoint.framework.uuid</span><i> properties and you can get the service ID of the service in that framework by looking up the </i><span style="font-family: 'Courier New',Courier,monospace;">endpoint.service.id</span><i> of the remote service - </i></span><a href="https://github.com/bosschaert/osgi-cloud-disco-demo/blob/0.2/cloud-disco-demo-web-ui/src/main/java/org/coderthoughts/cloud/demo/webui/impl/MyServlet.java#L126" style="font-style: italic;" target="_blank">see here for an example</a><span style="background-color: white; font-style: italic;">).</span></blockquote>
</div>
<div class="p1">
<span style="background-color: white;">So I can ask the OSGiFramework whether I can invoke the service, it can respond with various return codes, as a starting point for possible return values, I took the following list, largely inspired by the HTTP response codes:</span></div>
<div class="p1">
</div>
<div class="p1">
<span style="font-family: 'Courier New',Courier,monospace;"> <span class="s2">SERVICE_STATUS_OK</span> <span class="s4">// HTTP 200</span></span></div>
<span style="font-family: 'Courier New',Courier,monospace;"><span class="s5"> </span>SERVICE_STATUS_UNAUTHORIZED<span class="s5"> </span><span class="s4">// HTTP 401</span></span><br />
<div class="p2">
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"><span class="s5"> </span>SERVICE_STATUS_PAYMENT_NEEDED<span class="s5"> </span><span class="s4">// HTTP 402</span></span></div>
<span style="font-family: 'Courier New',Courier,monospace;"><span class="s5"> S</span>ERVICE_STATUS_FORBIDDEN<span class="s5"> </span><span class="s4">// HTTP 403</span></span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"><span class="s5"> </span>SERVICE_STATUS_NOT_FOUND<span class="s5"> </span><span class="s4">// HTTP 404</span></span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"><span class="s5"> </span>SERVICE_STATUS_QUOTA_EXCEEDED<span class="s5"> </span><span class="s4">// HTTP 413</span></span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"><span class="s5"> </span>SERVICE_STATUS_SERVER_ERROR<span class="s5"> </span><span class="s4">// HTTP 500</span></span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"><span class="s5"> </span>SERVICE_STATUS_TEMPORARY_UNAVAILABLE<span class="s5"> </span><span class="s4">// HTTP 503</span></span></div>
<br />
<div class="p1">
<span style="background-color: white;">It might also be worth adding some response codes around saying that things are still ok, but not for long, I was thinking of these responses:</span></div>
<div class="p1">
</div>
<div class="p1">
<span style="font-family: 'Courier New',Courier,monospace;"><span class="s1"> </span><span class="s3">SERVICE_STATUS_OK_QUOTA_ALMOST_EXCEEDED</span></span></div>
<div class="p1">
<span class="s3" style="background-color: white; font-family: 'Courier New',Courier,monospace;"> SERVICE_STATUS_OK_PAYMENT_INFO_NEEDED_SOON</span></div>
<div class="p1">
<span class="s3" style="background-color: white;">These need more thought but I think they can provide an interesting mechanism in preventing outages.</span></div>
<div class="p1">
<span class="s3" style="background-color: white;"><br /></span></div>
<div class="p1">
<span class="s3" style="background-color: white;">Under the hood I'm using the OSGi Monitor Admin Specification. I took an <a href="http://code.google.com/p/osgilab/wiki/MonitorAdmin" target="_blank">implementation from KnowHowLab</a> (thanks guys!). It gives a nice Whiteboard pattern-based approach to providing metadata via a Monitorable service. As the RemoteServiceFactory is the place where I'm implementing the policies for my TestService, it provides me a natural place to publish the metadata too.</span></div>
<div class="p1">
<span class="s3" style="background-color: white;"><br /></span></div>
<div class="p1">
<span class="s3" style="background-color: white;">When the client calls <span style="font-family: 'Courier New',Courier,monospace;">OSGiFramework.getServiceVariable(id, SV_STATUS)</span> the OSGiFramework service implementation in turn finds the matching Monitorable, which provides the status information. The Monitorable for my TestService is implemented by its RemoteServiceFactory:</span></div>
<div class="p1">
<span class="s3" style="background-color: white;">
</span></div>
<div class="p1">
<span style="font-family: 'Courier New',Courier,monospace;">public class <a href="https://github.com/bosschaert/osgi-cloud-disco-demo/blob/0.2/cloud-disco-demo-provider/src/main/java/org/coderthoughts/cloud/demo/provider/impl/TestServiceRSF.java" target="_blank">TestServiceRSF</a> implements ..., Monitorable {</span><br />
<div class="line" id="LC19">
<span style="font-family: 'Courier New',Courier,monospace;"><span style="background-color: white;"> </span><span style="background-color: white;">ConcurrentMap<String, AtomicInteger> </span><span class="s2" style="background-color: white;">invocationCount</span><span style="background-color: white;"> = </span></span><span style="background-color: white; font-family: 'Courier New',Courier,monospace;"><span class="s1" style="background-color: white;">new</span><span style="background-color: white;"> ConcurrentHashMap</span></span><span style="background-color: white; font-family: 'Courier New',Courier,monospace;"><</span><span style="background-color: white; font-family: 'Courier New',Courier,monospace;"><span style="background-color: white;">>();</span> </span><br />
<br />
<span style="background-color: white; font-family: 'Courier New',Courier,monospace;"> // ...</span><br />
<br />
<span style="background-color: white; font-family: 'Courier New',Courier,monospace;"> @Override</span></div>
</div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> <span class="s2">public</span> StatusVariable getStatusVariable(String var) <span class="s2">throws</span> IllegalArgumentException {</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> String ip = getIPAddress(var);</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> AtomicInteger count = <span class="s3">invocationCount</span>.get(ip);</span></div>
<div class="p3">
<span style="font-family: 'Courier New',Courier,monospace;"><br /></span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> String status;</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> <span class="s2">if</span> (count == <span class="s2">null</span>) {</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> status = OSGiFramework.<span class="s3">SERVICE_STATUS_NOT_FOUND</span>;</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> } <span class="s2">else</span> {</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> <span class="s2">if</span> (count.get() < 3)</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> status = OSGiFramework.<span class="s3">SERVICE_STATUS_OK</span>;</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> <span class="s2">else</span></span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> status = OSGiFramework.<span class="s3">SERVICE_STATUS_QUOTA_EXCEEDED</span>;</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> }</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> <span class="s2">return</span> <span class="s2">new</span> StatusVariable(var, StatusVariable.<span class="s3">CM_SI</span>, status);</span></div>
<div class="p2">
<span style="font-family: 'Courier New',Courier,monospace;"> }</span><br />
<br />
<span style="font-family: 'Courier New',Courier,monospace;"> private String getIPAddress(String var) {</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> // The client IP is the suffix of the variable name</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> if (!var.startsWith(OSGiFramework.SERVICE_STATUS_PREFIX))</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> throw new IllegalArgumentException("Not a valid status variable: " + var);</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"><br /></span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> String ip = var.substring(OSGiFramework.SERVICE_STATUS_PREFIX.length());</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> return ip;</span><br />
<span style="font-family: 'Courier New',Courier,monospace;"> }</span><br />
<div>
<span style="font-family: 'Courier New',Courier,monospace;"><br /></span></div>
</div>
<div class="p2">
Using MonitorAdmin thought the OSGi ServiceRegistry gives me a nice, loosely coupled mechanism to provide remote service metadata. It fits nicely with the RemoteServiceFactory approach but can also be implemented otherwise.</div>
<div class="p2">
<br /></div>
<div class="p2">
When I use my web servlet again I can see it all in action. It invokes the test service 5 times:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-Jcq9wxzctgA/T_1infJXDRI/AAAAAAAAAq8/4mXCQYxIfkk/s1600/webui.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="392" src="http://4.bp.blogspot.com/-Jcq9wxzctgA/T_1infJXDRI/AAAAAAAAAq8/4mXCQYxIfkk/s640/webui.png" width="640" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
In the Web UI you can see the 2 OSGi Frameworks in this cloud ecosystem. The local one (that also hosts the Web UI) and another one in a different cloud VM.<br />
The Servlet hosting the webpage invokes the TestService 5 times. In this case there is only a remote instance available. After 3 invocations it reports that the invocation quota have been used up.<br />
<br />
The Web UI servlet also invokes another remote service (the <a href="https://github.com/bosschaert/osgi-cloud-disco-demo/blob/0.2/cloud-disco-demo-provider/src/main/java/org/coderthoughts/cloud/demo/provider/impl/LongRunningServiceImpl.java" target="_blank">LongRunningService</a>) twice concurrently. You can see <a href="https://github.com/bosschaert/osgi-cloud-disco-demo/blob/0.2/cloud-disco-demo-provider/src/main/java/org/coderthoughts/cloud/demo/provider/impl/LongRunningServiceRSF.java" target="_blank">the policy that prevents concurrent invocation</a> in action where only one invocation succeds (it waits for a while then returns 42) and the other reports an error and does not invoke the actual service.<br />
<br />
The demo simply displays the service status and the return value from the remote service, but given this information I can do some interesting things.</div>
<div class="p2">
<ul>
<li><span style="background-color: white;">I can make the OSGi service consumer aware and avoid services that are not OK to invoke. Standard OSGi service mechanics allow me to switch to another service without much ado. </span></li>
<li><span style="background-color: white;">I can go even further and add a mechanism in the OSGi framework that automatically hides services if they are not OK to invoke. I wrote a blog post a while ago on how that can be done: </span><span style="background-color: white;"><a href="http://coderthoughts.blogspot.com/2009/11/altering-osgi-service-lookups-with.html" target="_blank">Altering OSGi Service Lookups with Service Registry Hooks</a> - the standard OSGi Service Registry Hooks allow you to do things like this.</span></li>
</ul>
</div>
<h3>
Do it yourself!</h3>
I have updated <a href="http://github.com/bosschaert/osgi-cloud-infra" target="_blank">osgi-cloud-infra</a> (and <a href="http://github.com/bosschaert/osgi-cloud-infra-src" target="_blank">its source</a>) with the changes to the OSGiFramework service. The bundles in this project are also updated to contain the Monitor Admin service implementation and the changes to CXF-DOSGi that I made on <a href="https://github.com/bosschaert/cxf-dosgi/commits/cloud" target="_blank">my branch</a> to support the RemoteServiceFactory.<br />
<br />
Additionally I updated the demo bundles in <a href="http://github.com/bosschaert/osgi-cloud-disco-demo" target="_blank">osgi-cloud-disco-demo</a> to implement the RemoteServiceFactory, add Service Variables and update the webui as above.<br />
<br />
There are no changes to the discovery server component.<br />
<br />
Instructions to run it all are identical to what was described in <a href="http://coderthoughts.blogspot.com/2012/06/cloud-ecosystems-with-osgi.html" target="_blank">the previous blog post</a> - just follow the steps from 'try it out' in that post and you'll see it in action.<br />
Note that it's possible that this code will be updated in the future. I've tagged this version as 0.2 in git.David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com0tag:blogger.com,1999:blog-9210566578097047576.post-31370538136470170132012-06-28T07:36:00.001-07:002012-06-28T08:07:47.493-07:00Cloud ecosystems with OSGiOne of the areas where I think that the dynamic services architecture of OSGi can really shine is in the context of cloud. And what I have in mind here is a cloud ecosystem comprised of multiple nodes in a cloud, or possibly across clouds, <span style="background-color: white;">where each node in this ecosystem potentially has a different role from the other</span><span style="background-color: white;"></span><span style="background-color: white;">. In such a system the various nodes need to be able to work together to perform some function, and hooking the pieces together is really where the fun starts because how do you know from inside one cloud vm where the other ones are? Various people are working on solutions for this which range from elastic IP addresses to plugging in variables when launching a VM and various other ones. While I agree that these solutions provide value I think that they should not necessarily bleed into the space of the developer or even the deployer. The deployer should simply be able to select a cloud, create a few instances and associate them together. At that point they should nicely work together. </span><br />
<span style="background-color: white;"><br /></span><br />
<span style="background-color: white;">This is where OSGi Services come in. OSGi Services implement a Java interface (we might see OSGi services in other languages too in the not too distant future) and are registered in the OSGi Service Registry. Consumers of these services are not tied to the provider as they select the service on its interface or other properties. The provider could be any other bundle in the OSGi Framework, or when using OSGi Remote Services they could be in a <i>different</i> framework. The OSGi Remote Services specs also describe a mechanism for discovery which makes it possible to find remote OSGi services using the standard OSGi Service Registry mechanisms (or component frameworks such as Blueprint, DS, etc).</span><br />
<span style="background-color: white;"><br /></span><br />
So I started prototyping such a cloud ecosystem using Red Hat's OpenShift cloud combined with OSGi Remote Services. However you'll see that my bundles are pure OSGi bundles that don't depend on any type of cloud - they simply use the OSGi Service Registry as normal...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-xHIgzUlPIcs/T-w_X8-hnkI/AAAAAAAAAqE/zMZucu0_lQg/s1600/CloudEcosystem.png" imageanchor="1"><img border="0" height="287" src="http://4.bp.blogspot.com/-xHIgzUlPIcs/T-w_X8-hnkI/AAAAAAAAAqE/zMZucu0_lQg/s400/CloudEcosystem.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<span style="background-color: white;"><br /></span><br />
In the diagram each OSGi Framework is potentially running in its own Cloud VM, although multiple frameworks could also share VMs (this would be a deployment choice and doesn't affect the architecture). <br />
<br />
Before diving into the details, my setup allows me to:<br />
<ul>
<li>register OSGi Services to be shared with other OSGi frameworks within the ecosystem.</li>
<li>see what other frameworks are running in this ecosystem. This would be useful information for a provisioning agent.</li>
</ul>
<div>
What's so special about this? Isn't this just OSGi Remote Services? Yes, I'm using those, but the interesting bit is the discovery component which binds the cloud ecosystem together. The user of the Remote Services doesn't need to know where they physically are. Similarly, the provider of the remoted service doesn't need to know how its distributed.</div>
<br />
<div>
As with any of my blog articles, I'm sharing the details of all this below, so do try this at home :) Most of the work here relates to setting up the infrastructure. Hopefully we can see cloud vendors provide something like this in the not too distant future which would give you a nice and clean deployment infrastructure for creating dynamic OSGi-based cloud ecosystems (i.e. an OSGi PAAS).<br />
<br /></div>
<h3>
The view from inside an OSGi bundle</h3>
<div>
Very simple. The provider of a service marks it as shared for use in the cloud ecosystem by adding 2 extra service registration properties. I'm using the standard OSGi Remote Service property <span style="background-color: white;"><span style="font-family: 'Courier New', Courier, monospace;">service.exported.interfaces</span> for this, however with a special config type: </span><span style="background-color: white;"><span style="font-family: 'Courier New', Courier, monospace;">org.coderthoughts.configtype.cloud</span>. This config type is picked up by the infrastructure to mean that it needs to be shared in the current cloud ecosystem.</span><br />
<br />
<span style="background-color: white;">I wrote a set of demo bundles to show the OSGi cloud ecosystem in action. One of the demo bundles registers a TestService, using the standard BundleContext API and adds these properties:</span></div>
<div>
<span style="background-color: white;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<div class="p1">
<span class="s1">public</span> <span class="s1">class</span> Activator <span class="s1">implements</span> BundleActivator {</div>
<div class="p1">
<span style="background-color: white;"> </span><span class="s1" style="background-color: white;">public</span><span style="background-color: white;"> </span><span class="s1" style="background-color: white;">void</span><span style="background-color: white;"> start(BundleContext context) </span><span class="s1" style="background-color: white;">throws</span><span style="background-color: white;"> Exception {</span></div>
<div class="p1">
TestService dr = <span class="s1">new</span> TestServiceImpl();</div>
<div class="p1">
Dictionary<string, object=""> props = <span class="s1">new</span> Hashtable<string, object="">();</string,></string,></div>
<div class="p4">
<span class="s3"> props.put(</span>"service.exported.interfaces"<span class="s3">, </span>"*"<span class="s3">);</span></div>
<div class="p4">
<span class="s3"> props.put(</span>"service.exported.configs"<span class="s3">,</span><br />
<span class="s3"> </span>"org.coderthoughts.configtype.cloud"<span class="s3">);</span></div>
<div class="p1">
context.registerService(TestService.<span class="s1">class</span>.getName(), dr, props);</div>
<div class="p1">
}</div>
<div class="p1">
....</div>
<div class="p1">
}</div>
</div>
<div>
You can see the full provider class here: <a href="http://github.com/bosschaert/osgi-cloud-disco-demo/blob/master/cloud-disco-demo-provider/src/main/java/org/coderthoughts/cloud/demo/provider/impl/Activator.java">Activator.java</a><br />
<br />
Consuming the service is completely non-intrusive. My demo also contains a Servlet that provides a simple Web UI to test the service and makes invocations on it. It doesn't need to specify anything special to use an OSGi service that might be in another framework instance. It uses a standard OSGi <a href="http://www.osgi.org/javadoc/r4v42/org/osgi/util/tracker/ServiceTracker.html">ServiceTracker</a> to look up the TestService:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">ServiceTracker testServiceTracker = new ServiceTracker(context, TestService.class.getName(), null) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> public Object addingService(ServiceReference reference) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> testServicesRefs.add(reference);</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> return super.addingService(reference);</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;"> public void removedService(ServiceReference reference, </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> Object service) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> testServicesRefs.remove(reference);</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> super.removedService(reference, service);</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">};</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">testServiceTracker.open();</span></div>
<div>
<div>
For the whole thing, see here: <a href="http://github.com/bosschaert/osgi-cloud-disco-demo/blob/master/cloud-disco-demo-web-ui/src/main/java/org/coderthoughts/cloud/demo/webui/impl/MyServlet.java">MyServlet.java</a></div>
<div>
</div>
</div>
<div>
<br /></div>
<div>
I used plain OSGi Service APIs here, but you can also use Blueprint, DS or whatever OSGi Component technology to work with the services...</div>
<div>
<br />
The main point is that we are doing purely Service Oriented Programming. As long as the services are available somewhere in the ecosystem their consumers will find them. If a cloud VM dies or another is added, the dynamic capabilities of OSGi Services will rebind the consumers to the changed service locations. The code that deals with the services doesn't deal with the physical cloud topology at all.<br />
<span style="background-color: white;"><br /></span><br />
<h3>
<span style="background-color: white;">Try it out!</span></h3>
</div>
<div>
As always on this blog I'm providing detailed steps to try this out yourself. Note that I'm using Red Hat's OpenShift which gives you 3 cloud instances for development purposes for free. The rest is all opensource software so you can get going straight away.<br />
<br />
Also note that you can set this up using other clouds too, or even across different clouds, the OSGi bundles aren't affected by this at all. So if you prefer another cloud the only thing you need to do there is setup the Discovery system for that cloud; the same OSGi bundles will work.</div>
<div>
<br /></div>
<h3>
Cloud instances</h3>
<div>
For this example I'm using 3 cloud VMs to create my ecosystem. All of which are based on the OpenShift 'DIY' cartridge as I explained in my <a href="http://coderthoughts.blogspot.com/2012/05/osgi-on-openshift-free-opensource-cloud.html">previous posting</a>. They have the following names:</div>
<div>
<ul>
<li>discoserver - provides the Discovery functionality</li>
<li>osgi1 and osgi2 - two logically identical OSGi frameworks </li>
</ul>
</div>
<h3>
Discovery</h3>
<div>
The Discovery functionality is based on Apache ZooKeeper and actually runs in its own cloud vm. Everything you need is available from the github project <a href="http://github.com/bosschaert/osgi-cloud-discovery" target="_blank">osgi-cloud-discovery</a>.</div>
<div>
<br /></div>
<div>
Here's how I get it into my cloud image (same as described in my <a href="http://coderthoughts.blogspot.com/2012/05/osgi-on-openshift-free-opensource-cloud.html" target="_blank">previous post</a>):</div>
<div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">$ git clone ssh://blahblahblah@discoserver-coderthoughts.rhcloud.com/~/git/discoserver.git/</span> (this is the URL given to you when you created the OpenShift vm)</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ cd discoserver</div>
</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git fetch https://github.com/bosschaert/osgi-cloud-discovery.git</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git merge -Xtheirs FETCH_HEAD </div>
<div>
<br /></div>
<div>
then launch the VM:</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git push</div>
<div style="font-family: "Courier New",Courier,monospace;">
... after a while you'll see:</div>
<div style="font-family: "Courier New",Courier,monospace;">
Starting zookeeper ... STARTED</div>
<div>
Done - I've got my discovery system started in the cloud.</div>
<div>
<br /></div>
<div>
I didn't replicate discovery (for fault tolerance) here for simplicity, that can be added later.<br />
<span style="background-color: white;"><br /></span><br />
<h3>
<span style="background-color: white;">The OSGi Frameworks</span></h3>
</div>
<div>
For the OSGi Frameworks I'm starting off with 2 identical frameworks which contain the baseline infrastructure. I put this infrastructure in the <a href="http://github.com/bosschaert/osgi-cloud-infra" target="_blank">osgi-cloud-infra</a> github project. To get this into your VM clone as provided by the OpenShift 'DIY' cartridge do similar to the above:</div>
<div style="font-family: "Courier New",Courier,monospace;">
<div>
<div>
$ git clone ssh://blahblahblah@osgi1-coderthoughts.rhcloud.com/~/git/osgi1.git/</div>
<div>
$ cd osgi1</div>
</div>
</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git fetch https://github.com/bosschaert/osgi-cloud-infra.git</div>
<div style="font-family: "Courier New",Courier,monospace;">
<div>
$ git merge -Xtheirs FETCH_HEAD </div>
</div>
<div>
<br /></div>
<div>
At this point it gets a little tricky as I'm setting up an SSH tunnel to the discovery instance to make this vm part of the discovery domain. To do this, I create an SSH key which I'm adding to my OpenShift account, then each instance that's part of my ecosystem uses that key to set up the SSH tunnel.</div>
<div>
<br /></div>
<div>
Create the key and upload it to OpenShift:</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ cd disco-tunnel</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ ssh-keygen -t rsa -f disco_id_rsa</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ rhc sshkey add -i disco -k disco_id_rsa.pub </div>
<div>
<br /></div>
<div>
Create a script that sets up the tunnel. For this we also need to know the SSH URL of the discovery VM. This is the <span style="background-color: white;">blahblahblah@discoserver-coderthoughts.rhcloud.com identifier (or whatever OpenShift provided to you). In the disco-tunnel directory is a template for this script, copy it and add the identifier to the DISCOVERY_VM variable in the script:</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="background-color: white;">$ </span><span style="background-color: white;">cp create-tunnel-template.sh create-tunnel.sh</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="background-color: white;">$ vi </span><span style="background-color: white;">create-tunnel.sh</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="background-color: white;">... set the DISCOVERY_VM variable ...</span></div>
<div>
<br /></div>
<div>
finally add the new files in here to git:</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git add create-tunnel.sh disco_id_rsa*</div>
<div>
<br /></div>
<div>
For any further OSGi framework instances, you can simply copy the files added to git here (<span style="background-color: white;">create-tunnel.sh and disco_id_rsa*</span><span style="background-color: white;">) and add to the git repo. </span></div>
<div>
<br /></div>
<div>
As you can see, this bit is quite OpenShift specific. It's a once-off thing that needs setting up and it's not really ideal, I hope that cloud vendors will make something like this easier in the future :)<br />
<span style="background-color: white;"><br /></span><br />
<h3>
<span style="background-color: white;">Add Demo bundles</span></h3>
</div>
<div>
At this point I have my cloud instances set up as far as the infrastructure goes. However, they don't do much yet given that I don't have any application bundles. I want to deploy my TestService as described above and I'm also going to deploy the little Servlet-based Web UI that invokes it so that we can see it happening. The demo bundles are hosted in a source project: <a href="http://github.com/bosschaert/osgi-cloud-disco-demo" target="_blank">osgi-cloud-disco-demo</a>.<br />
<br />
To deploy, clone and build the demo bundles:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ git clone git://github.com/bosschaert/osgi-cloud-disco-demo.git</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ cd osgi-cloud-disco-demo</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ mvn install</div>
<br />
Next thing we need to do is deploy the bundles. For now I'm using static deployment but I'm planning to expand to dynamic deployment in the future.<br />
<br />
I'm deploying the Servlet-based Web UI bundle first. The osgi-cloud-demo-disco source tree contains a script that can do the copying and updates the configuration to deploy the bundles in the framework:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ ./copy-to-webui-framework.sh ~/clones/osgi1</div>
<br />
In the osgi1 clone I can now see that the bundles have been added and the configuration to deploy them updated:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ git status </div>
<div style="font-family: "Courier New",Courier,monospace;">
# modified: osgi/equinox/config/config.ini</div>
<div style="font-family: "Courier New",Courier,monospace;">
# Untracked files:</div>
<div style="font-family: "Courier New",Courier,monospace;">
# osgi/equinox/bundles/cloud-disco-demo-api-1.0.0-SNAPSHOT.jar</div>
<div style="font-family: "Courier New",Courier,monospace;">
# osgi/equinox/bundles/cloud-disco-demo-web-ui-1.0.0-SNAPSHOT.jar</div>
Add them all to git and commit and push the git repo:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ git add osgi/equinox</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git commit -m "An OSGi Framework Image"</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git push</div>
<br />
The cloud VM is started as part of the 'git push'. <br />
<br />
Let's try the demo web ui, go to the /webui context of the domain that OpenShift provided to you and it will display the OSGi Frameworks known to the system and all the TestService instances:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-3UFMWU6eGqA/T-wueSiaetI/AAAAAAAAAps/GQq4MQeQPuo/s1600/Untitled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="425" src="http://2.bp.blogspot.com/-3UFMWU6eGqA/T-wueSiaetI/AAAAAAAAAps/GQq4MQeQPuo/s640/Untitled.png" width="640" /></a></div>
There is 1 framework known (the one running the webui) and no TestService instances. So far so good.<br />
<br />
Next we'll make the TestService available in another Cloud vm. <br />
Create another cloud VM (e.g. osgi2) identical to osgi1, but without the demo bundles.<br />
<br />
Then deploy the demo service provider bundles:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ ./copy-to-provider-framework.sh ~/clones/osgi2</div>
<br />
In the osgi2 clone I can now see that the bundles have been added and the configuration to deploy them updated:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ git status</div>
<div style="font-family: "Courier New",Courier,monospace;">
# On branch master</div>
<div style="font-family: "Courier New",Courier,monospace;">
# modified: osgi/equinox/config/config.ini</div>
<div style="font-family: "Courier New",Courier,monospace;">
#</div>
<div style="font-family: "Courier New",Courier,monospace;">
# Untracked files:</div>
<div style="font-family: "Courier New",Courier,monospace;">
# osgi/equinox/bundles/cloud-disco-demo-api-1.0.0-SNAPSHOT.jar</div>
<div style="font-family: "Courier New",Courier,monospace;">
# osgi/equinox/bundles/cloud-disco-demo-provider-1.0.0-SNAPSHOT.jar</div>
<div style="font-family: "Courier New",Courier,monospace;">
<br /></div>
Add them all to git and commit and push the git repo:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ git add osgi/equinox </div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git commit -m "An OSGi Framework Image"</div>
<div style="font-family: "Courier New",Courier,monospace;">
$ git push</div>
<br />
Give the system a minute or so, then refresh the web UI:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-pv9Zq9XIL_U/T-w3QrQy9yI/AAAAAAAAAp4/XibMEP34Fc0/s1600/shot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="472" src="http://2.bp.blogspot.com/-pv9Zq9XIL_U/T-w3QrQy9yI/AAAAAAAAAp4/XibMEP34Fc0/s640/shot.png" width="640" /></a></div>
<br />
You can now see that there are 2 OSGi frameworks available in the ecosystem. The web UI (running in osgi1) invokes the test service (running in osgi2) which, as a return value, reports its UUID to show that its running in the other instance.<br />
<br />
<h3>
Programming model</h3>
The nice thing here is that I stayed within the OSGi programming model. My bundles simply use an OSGi <a href="http://www.osgi.org/javadoc/r4v42/org/osgi/util/tracker/ServiceTracker.html" target="_blank">ServiceTracker</a> to look up the framework instances (which are represented as services) and the TestService. I don't have any configuration code to wire up the remote services. This all goes through the OSGi Remote Services-based discovery mechanism.<br />
Also, the TestService is invoked as a normal OSGi Service. The only 'special' thing I did here was to mark the TestService as exported in the cloud with some service properties.<br />
<br /></div>
<h3>
Conclusion</h3>
<div>
This is just a start... I think it opens up some very interesting possibilities and I intend to write more posts in the near future about dynamic provisioning in this context, service monitoring and other cloud topics. The example I've been running here is on my employer's (Red Hat) OpenShift cloud - but it can work on any cloud or even across clouds and the bundles providing the functionality generally don't need to know at all what cloud they're in...</div>
<div>
<br /></div>
<div>
<h3>
Some additional notes</h3>
</div>
<div>
Cloud instances typically have a limited set of ports they can open to the outside world. In the case of OpenShift you currently get only one: port 8080 which is mapped to external port 80. If you're running multiple applications in a single cloud VM this can sometimes be a problem as they each may want to have their own port. OSGi actually helps here too. It provides the OSGi HTTP Service where bundles can register any number of resources, servlets etc on different contexts of the same port. So in my example I'm running my Servlet on /webui but I'm also using <a href="http://cxf.apache.org/distributed-osgi.html">Apache CXF-DOSGi</a> as the Remote Services implementation which exposes the OSGi Services on the same port but different contexts. As many web-related technologies in OSGi are built on the HTTP Service they can all happily coexist on a single port.</div>David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com2tag:blogger.com,1999:blog-9210566578097047576.post-66958195187153810422012-05-18T00:17:00.002-07:002012-05-18T00:27:58.624-07:00OSGi on OpenShift (Free, OpenSource Cloud)While JBoss OSGi support for <a href="http://openshift.redhat.com/">OpenShift</a> is <a href="https://community.jboss.org/message/733302#733302" target="_blank">being looked at</a>, I had a go at getting <a href="http://felix.apache.org/" target="_blank">Apache Felix</a> to work on OpenShift. And I have to say, the results were good :)<br />
<br />
OpenShift is Red Hat's cloud. You can create and use cloud instances for free, for development purposes - a free cloud playground!! <br />
OpenShift comes with a number of cartridges, including a somewhat reduced version of JBoss AS7 (no OSGi), Node.js and Jenkins. However OpenShift also has a DIY application type. With this you can deploy whatever you like :) So, I took the Felix OSGi framework and deployed that in OpenShift using the DIY type!<br />
<br />
Here's what I did.<br />
1. Get a free OpenShift account by signing up at <a href="http://openshift.redhat.com/">http://openshift.redhat.com</a><br />
2. Once you have your account you can create an application:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-K77hhpHvdM4/T7VEQSrTNTI/AAAAAAAAApA/5vTMbU3t1K4/s1600/apptypes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="452" src="http://3.bp.blogspot.com/-K77hhpHvdM4/T7VEQSrTNTI/AAAAAAAAApA/5vTMbU3t1K4/s640/apptypes.png" width="640" /></a></div>
Select the Do-It-Yourself application type.<br />
<br />
3. Create a URL for the application:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-3NyQABT0sBU/T7VFCl4IMjI/AAAAAAAAApI/tilW5iHfNO4/s1600/create.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="277" src="http://4.bp.blogspot.com/-3NyQABT0sBU/T7VFCl4IMjI/AAAAAAAAApI/tilW5iHfNO4/s400/create.png" width="400" /></a></div>
I went for <a href="http://felix-coderthoughts.rhcloud.com/">http://felix-coderthoughts.rhcloud.com</a>.<br />
When you click <i><b>create</b></i>, the an empty holder application is created for you. We can fill the application in using <a href="http://git-scm.com/" target="_blank">git</a>.<br />
<br />
4. Now you can clone the application using git, OpenShift will tell you the URL to use, mine looks like this: <br />
<div style="font-family: "Courier New",Courier,monospace;">
$ git clone ssh://blahblahblah@felix-coderthoughts.rhcloud.com/~/git/felix.git</div>
Oh, yes, it only works once you have an SSH key added to your account. You can do that in the <a href="https://openshift.redhat.com/app/account" target="_blank">'My Account' section of the web console</a>, or from the command line, see <a href="http://docs.redhat.com/docs/en-US/OpenShift/2.0/html/User_Guide/sect-User_Guide-Managing_SSH_Keys.html" target="_blank">here for more info</a>.<br />
<br />
5. At this point you can start putting your cloud content into git. Initially your clone contains a README, some mostly empty directories and some openshift hooks:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ ls -a</div>
<span style="font-family: "Courier New",Courier,monospace;">.git README misc</span><br />
<span style="font-family: "Courier New",Courier,monospace;">.openshift diy</span><br />
<br />
So what next?<br />
I started by adding a modified Apache Felix installation to a directory called <span style="font-family: "Courier New",Courier,monospace;">osgi/felix-framework-4.0.2</span>. I took <a href="http://felix.apache.org/site/downloads.cgi" target="_blank">Felix 4.0.2</a> and instead of the gogo terminal-based console bundles I used the <a href="http://felix.apache.org/site/apache-felix-web-console.html" target="_blank">Web Console</a> with <a href="http://felix.apache.org/site/apache-felix-config-admin.html" target="_blank">Config Admin</a> and <a href="http://team.ops4j.org/wiki/display/paxweb/Pax+Web" target="_blank">Pax-Web</a>. <br />
<br />
Next we need to modify the OpenShift action hooks to start the OSGi Framework. This is what my <a href="https://github.com/bosschaert/felix-openshift/blob/master/.openshift/action_hooks/start" target="_blank"><span style="font-family: "Courier New",Courier,monospace;">.openshift/action_hooks/start</span></a> script does:<br />
<div style="font-family: "Courier New",Courier,monospace;">
cd $OPENSHIFT_GEAR_DIR/repo/osgi/felix-framework-4.0.2 <br />
export felixcmd="java -Dorg.ops4j.pax.web.listening.addresses=$OPENSHIFT_INTERNAL_IP -jar bin/felix.jar"<br />
nohup $felixcmd &</div>
First the current directory is changed to where Felix is stored, and then Felix is launched with as extra option for Pax Web, the IP address that OpenShift wants us to use. The port number is configured as 8080, as required by OpenShift, this is specified in the <a href="https://github.com/bosschaert/felix-openshift/blob/b4ef0674b5351ffdfc536423b137915724a7fc57/osgi/felix-framework-4.0.2/conf/config.properties" target="_blank">Felix configuration file</a>.<br />
<br />
That's pretty much it - I also created <a href="https://github.com/bosschaert/felix-openshift/blob/b4ef0674b5351ffdfc536423b137915724a7fc57/.openshift/action_hooks/stop" target="_blank">a fairly blunt stop script</a> that kills all the Java processed. I guess that could be refined ;)<br />
<br />
Ok, so let's try it out.<br />
As a convenience I added this setup as a github project called <a href="http://github.com/bosschaert/felix-openshift" target="_blank">felix-openshift</a>, you can get it into your freshly cloned OpenShift repo with:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ git fetch git://github.com/bosschaert/felix-openshift.git <br />
From git://github.com/bosschaert/felix-openshift<br />
* branch HEAD -> FETCH_HEAD</div>
Then merge the content in with:<br />
<span style="font-family: "Courier New",Courier,monospace;">$ git merge -Xtheirs FETCH_HEAD</span><br />
Note that -Xtheirs automatically takes the start and stop scripts from the fetched project and overwrites the local ones. If your git client doesn't support this option you'll probably have to merge these scripts by hand... <br />
<br />
To see it all in action, run:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ git push<br />
Counting objects: 32, done.<br />
Delta compression using up to 8 threads.<br />
Compressing objects: 100% (21/21), done.<br />
Writing objects: 100% (27/27), 3.49 MiB | 86 KiB/s, done.<br />
Total 27 (delta 2), reused 10 (delta 1)<br />
remote: Starting application...<br />
remote: Done<br />
... you will see more logging messages, and then ... </div>
<span style="font-family: "Courier New",Courier,monospace;">remote: 2012-05-17 16:19:11.133:INFO:oejs.AbstractConnector:Started NIOSocketConnectorWrapper@127.2.146.129:8080 STARTING</span><br />
<span style="font-family: "Courier New",Courier,monospace;">remote: 2012-05-17 16:19:11.561:INFO:oejsh.ContextHandler:started HttpServiceContext{httpContext=org.apache.felix.webconsole.internal.servlet.OsgiManagerHttpContext@174d93a}</span><br />
Aha - the webconsole has started!<br />
<br />
I can now launch it at <a href="http://felix-coderthoughts.rhcloud.com/system/console/bundles">http://felix-coderthoughts.rhcloud.com/system/console/bundles</a> (or whatever your OpenShift URL is). Default Felix Web Console credentials are admin/admin:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-o4MN6pHj8Q4/T7XyT1QRRSI/AAAAAAAAApU/ZD8tI7y49dA/s1600/webconsole.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="315" src="http://1.bp.blogspot.com/-o4MN6pHj8Q4/T7XyT1QRRSI/AAAAAAAAApU/ZD8tI7y49dA/s640/webconsole.png" width="640" /></a></div>
From here on I can use the Felix Web Console to deploy more bundles, control the framework and so on. <br />
<br />
After pushing the git repo, you can also control the cloud instance <a href="https://openshift.redhat.com/app/getting_started" target="_blank">from the commandline with rhc</a>:<br />
<div style="font-family: "Courier New",Courier,monospace;">
$ rhc app start -a felix -l user@domain.org</div>
and <br />
<div style="font-family: "Courier New",Courier,monospace;">
$ rhc app stop -a felix -l user@domain.org</div>
<br />
There you go, OSGi running in the cloud. And you can develop and play with this stuff without the need to give anyone your credit card details :)David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com7tag:blogger.com,1999:blog-9210566578097047576.post-44920392429429050552012-02-29T07:59:00.002-08:002012-03-01T11:26:52.594-08:00Running the Penrose (or OpenJDK 8) testsAs a follow-up on my previous post, <a href="http://coderthoughts.blogspot.com/2012/02/building-project-penrose-or-openjdk-8.html">Building project Penrose (or OpenJDK 8)</a>, I wanted to run the unit tests. And, as with many other things in OpenJDK, it's different from what I was used to. While other projects typically use JUnit for unit testing, OpenJDK uses a tool called <a href="http://openjdk.java.net/jtreg/">jtreg</a>, which you'll have to install manually before you can run any tests.<br />
<br />
Once you have jtreg installed you need to tell the build where it is (the directory specified contains linux/bin/jtreg) and add it to your path - note that the JT_HOME variable is used by the OpenJDK build:<br />
<div style="font-family: "Courier New",Courier,monospace;"><b>export JT_HOME=/home/david/apps/jtreg</b></div><div style="font-family: "Courier New",Courier,monospace;"><b>export PATH=$JT_HOME/linux/bin:$PATH</b></div><br />
Now you can run<span style="font-family: inherit;"> the whole test suite (in your clone of http://hg.openjdk.java.net/penrose/jigsaw or whatever your clone of OpenJDK 8 is):</span><br />
<div style="font-family: "Courier New",Courier,monospace;"><b>$ make test</b></div><div style="font-family: inherit;"><br />
</div><div style="font-family: inherit;">The above takes a very long time (<i>edit:</i> in fact it doesn't properly finish at all for me, it just hangs after a number of hours).<br />
<br />
If you're developing it's better to run a portion of the tests, for instance a single test directory:</div><div style="font-family: inherit;"><span style="font-family: 'Courier New', Courier, monospace;">.../jdk/test$ <b>jtreg -testjdk:/home/david/hg/pj_230212/build/linux-i586/jdk-module-image org/openjdk/jigsaw/cli</b></span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Test results: passed: 10</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Report written to /home/david/hg/pj_230212/jdk/test/JTreport/html/report.html</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Results written to /home/david/hg/pj_230212/jdk/test/JTwork</span></div><div style="font-family: inherit;"><br />
or a single test class: </div><div style="font-family: inherit;"><span style="font-family: 'Courier New', Courier, monospace;">.../jdk/test$ <b>jtreg -testjdk:/home/david/hg/pj_230212/build/linux-i586/jdk-module-image org/openjdk/jigsaw/cli/ModuleFileTest.java </b></span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Test results: passed: 1</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Report written to /home/david/hg/pj_230212/jdk/test/JTreport/html/report.html</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Results written to /home/david/hg/pj_230212/jdk/test/JTwork</span><br />
<br />
Note that in the above command lines it's important to pass in the full absolute path of the JDK you're running with as some of the tests rely on that to launch certain executables (like javac).</div>David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com0tag:blogger.com,1999:blog-9210566578097047576.post-5656589248597940112012-02-24T05:03:00.002-08:002012-02-24T05:18:21.053-08:00Building project Penrose (or OpenJDK 8)As <a href="http://mail.openjdk.java.net/pipermail/announce/2012-January/000115.html">OpenJDK Project Penrose</a> was <a href="http://mail.openjdk.java.net/pipermail/announce/2012-February/000117.html">approved last week</a>, I started looking at getting set up to actually be able to contribute to OpenJDK and Penrose.<br />
As I had never built OpenJDK before I started looking for the build page. While <a href="http://openjdk.java.net/groups/build/">OpenJDK build page</a> says that there is no OpenJDK 8 build readme yet, the link behind the <a href="http://hg.openjdk.java.net/jdk8/build/raw-file/tip/README-builds.html">'coming soon'</a> text actually contains some useful information but it relates to old versions of Linux and I want to get this working on Fedora 16 :)<br />
<br />
Currently the Penrose codebase is still the same as the Jigsaw code base, so the steps below also apply to building Jigsaw, and they also work for plain OpenJDK 8.<br />
At some point there will be changes in the Penrose codebase, but I don't expect that they will change the build process much, if at all.<br />
<br />
While Julien Ponge nicely describes <a href="http://live.julien.ponge.info/84822905">how to get this working on Ubuntu</a>, the steps are a bit different on Fedora 16, so I'm documenting here how to get started from a fresh new Fedora 16 machine.<br />
<br />
Get a few dependencies first:<br />
<div style="font-family: "Courier New",Courier,monospace;"># set up gcc and the static version of libstdc++</div><div style="font-family: "Courier New",Courier,monospace;">sudo yum install gcc gcc-c++ libstdc++-static</div><div style="font-family: "Courier New",Courier,monospace;"><br />
# install build deps on alsa, freetype and cups</div><div style="font-family: "Courier New",Courier,monospace;">sudo yum install alsa-lib-devel freetype-devel cups-devel<br />
<br />
# install some X11 headers <br />
sudo yum libXt-devel libXext-devel libXrender-devel libXtst-devel<br />
<br />
</div><div style="font-family: "Courier New",Courier,monospace;"># get mercurial</div><div style="font-family: "Courier New",Courier,monospace;">sudo yum install mercurial</div><div style="font-family: "Courier New",Courier,monospace;"><br />
</div><div style="font-family: "Courier New",Courier,monospace;"># install Java 7 and ant</div><div style="font-family: "Courier New",Courier,monospace;">sudo yum install java-1.7.0-openjdk-devel ant</div><br />
Now clone the whole forest of Mercurial trees (you can also use fclone with the Forest extension instead, which will save you from doing calling the bash script manually):<br />
<span style="font-family: 'Courier New', Courier, monospace;">$ hg clone http://hg.openjdk.java.net/penrose/jigsaw pj</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">$ cd pj</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">$ bash get_source.sh </span><br />
<br />
Set the following environment variables:<br />
<span style="font-family: 'Courier New', Courier, monospace;">export LANG=C</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">export ALT_BOOTDIR=/usr/lib/jvm/java-1.7.0-openjdk</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">export ALLOW_DOWNLOADS=true</span><br />
<br />
At this point 'make sanity', which does a rough check on the setup, should succeed:<br />
<span style="font-family: 'Courier New', Courier, monospace;">$ make sanity</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">... </span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Sanity check passed.</span><br />
<br />
Ok, we're ready to build:<br />
<div style="font-family: "Courier New",Courier,monospace;">$ make</div><div style="font-family: "Courier New",Courier,monospace;">... go for a coffee, walk the dog, book a holiday</div><div style="font-family: "Courier New",Courier,monospace;">... after quite a while you'll see that it's finished</div><span style="font-family: 'Courier New', Courier, monospace;">-- Build times ----------</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Target all_product_build</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Start 2012-02-24 08:15:23</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">End 2012-02-24 08:52:47</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">00:00:58 corba</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">00:17:31 hotspot</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">00:00:19 jaxp</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">00:00:23 jaxws</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">00:17:44 jdk</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">00:00:29 langtools</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">00:37:24 TOTAL</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">-------------------------</span><br />
<br />
There you go, you built Penrose and the result is a JDK 8 installation based on the Penrose codebase:<br />
<span style="font-family: 'Courier New', Courier, monospace;">$ cd build/linux-i586/jdk-module-image/bin</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">$ ./java -version</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">openjdk version "1.8.0-internal"</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">OpenJDK Runtime Environment (build 1.8.0-internal-david_2012_02_24_08_15-b00)</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">OpenJDK Client VM (build 23.0-b11, mixed mode)</span><br />
<br />
Looking forward to start hacking around in the code :)<br />
<br />
Thanks to Andrew Haley and Alan Bateman for helping me with some issues I came across along the way.David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com2tag:blogger.com,1999:blog-9210566578097047576.post-52520158128807954992012-01-31T03:59:00.000-08:002012-01-31T03:59:55.680-08:00Backing up through FTP on a MacIt's been a little over half a year now that I made the transition to mac. I was more or less forced into it by Dell because they didn't make the 1920x1200 high-res laptops any more that I like working with.<br />
But I have to say, the Apple hardware is second to none - I haven't experienced any mysterious crashes since.<br />
<br />
However I had to find Mac alternatives for all the software that I was using, and I managed to find most of what I was looking for quite quickly...<br />
<ul><li>For viewing zip archives I currently use <a href="http://www.zipeg.com/">Zipeg</a>, it only does extraction but that's good enough more most cases.</li>
<li>I really like <a href="http://www.autumnapps.com/breeze/">Breeze</a> for keyboard-based window alignment, similar to what Windows 7 has.</li>
<li>And I started using <a href="http://www.boastr.de/">BetterTouchTool</a> to map a keystroke the mousebuttons (saves me from having to press the touchpad all the time, which I don't really like).</li>
<li>I'm still undecided about iTunes, I don't really like it (e.g. it's missing a feature that shows what you're playing in the dock or something like that) but it kinda does what it needs to do and there doesn't seem to be anything better on the Mac, strangely enough...</li>
<li>Most other applications that I had been using before have Mac versions, except for...</li>
</ul><div>... a backup solution! I was always using <a href="http://www.areca-backup.org/">Areca</a> in the past which works great and had both Windows and Linux versions available. I guess everybody on Mac simply buys a Time Machine but I wanted to use my existing network storage drive for my backups.</div><div><br />
</div><div>I found <a href="http://www.grapefruit.ch/iBackup/">iBackup</a> and used it for about half a year. It was seemingly able to do the work over a Samba mount to my network drive, although I did notice that I was getting many failure runs, in fact over half of the runs were failures. It might have been caused by the SMB implementation on my NAS drive, I don't really know but I noticed that it was having issues with relatively large files (over a couple of megs).</div><div>I started looking at using FTP to do the backups instead (my NAS drive supports FTP) but iBackup doesn't support that.</div><div><br />
</div><div>After looking around for a while I couldn't find any freely available backup option for Mac that uses FTP. I might have missed one or two, but in the end I started writing a little shell script to do the job. One of the things that I was worried about was the backup speed. My script logs in and out for every directory recursively so I was concerned that it would take much longer to complete than the SMB option. I was very surprised that for my data it took 9 minutes to complete, where the SMB-based option took several hours if it completed at all.</div><div><br />
</div><div>So here it is, a simple little bash script to backup any directory on your mac using FTP, I invoke it like this</div><div>example..</div><div><blockquote class="tr_bq"><span style="font-family: 'Courier New', Courier, monospace; font-size: small;">ftp-bku.sh ~/bin ~/docs ~/etc ~/utils</span></blockquote></div><div>Note that I have my FTP credential in my ~/.netrc file so that I don't need to provide them in the script itself...</div><div><br />
</div><div>For those who are interested, you can find the script below (btw one thing that isn't completely reliable yet is the return code of the ftp program. It's 0 in some cases when there actually is a problem. Not sure if this can be fixed, although I could grep the output for error messages...)<br />
<br />
</div><pre>FTPURL=ftp://User@192.168.1.24:2121
FTPBASEDIR=MyDir
TIMESTAMP=$(date +"%d%m%y-%H%M%S")
BACKUPDIR=backup_`hostname`_$TIMESTAMP
# Perform the actual FTP backup
function backup {
PNAME="$1"
DNAME="$2"
ftp -i -V $FTPURL<<EOF
bin
cd "$FTPBASEDIR"
mkdir "$BACKUPDIR"
cd "$BACKUPDIR"
lcd "$PNAME"
mkdir "$DNAME"
cd "$DNAME"
mput *
quit
EOF
if [ "$?" == "0" ]; then
echo Backup succeeded: "$BACKUPDIR"
else
echo Backup failed: "$BACKUPDIR"
fi
}
# Backup the directory specified as $2 recursively where the base directory is $1
# So to backup /Users/david/etc/tmp as etc/tmp you pass in
# /Users/david/etc/tmp etc/tmp</pre><pre>function backup-dir {
cd "$1"
if [ -d "$2" ]; then
BASE="$1"
DIR="$2"
BASELEN="${#BASE}"
SUBDIR="${DIR:BASELEN}"
TARGET="`basename "$1"`$SUBDIR"
backup "$2" "$TARGET"
fi
cd "$2"
for dir in *
do
if [ -d "$2/$dir" ]; then
backup-dir "$1" "$2/$dir"
fi
done
}
# Staring point, backup all directories specified on the command line
for directory in "$@"
do
backup-dir "$directory" "$directory"
done
</pre>David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com6tag:blogger.com,1999:blog-9210566578097047576.post-81125327154481063082011-08-29T06:27:00.000-07:002011-08-29T06:27:46.898-07:00java.util.ServiceLoader in OSGiOne of the bigger stumbling blocks for migrating an existing application to OSGi has been java.util.ServiceLoader and its predecessors (the various FactoryFinders that can be found in technologies such as JAXP, JAXRS, etc...).<br />
<br />
ServiceLoader is a class provided by the JRE aimed at loading pluggable implementations of something. For those who need a little refreshing of the brain, here's what typical ServiceLoader usage looks like... Let's say you have an SPI called MySPI with a single method doit():<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">public abstract class MySPI {</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> public abstract String doit();</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;">}</span></span><br />
For some reason SPI providers are often realized as abstract classes, so that's what I'm doing here too. Obviously using an interface is also possible...<br />
You load the SPI implementations by calling ServiceLoader.load(). To obtain the actual implementation objects you iterate over the result of that call:<br />
<div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">ServiceLoader<myspi> ldr = ServiceLoader.load(MySPI.class);</myspi></span></div><div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">for (MySPI spiObject : ldr) {</span></div><div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> spiObject.doit(); // invoke my SPI object</span></div><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">}</span></span><br />
So why is this such a big problem in OSGi? The problem is that the JRE's ServiceLoader is non-modular by design. It relies on a single classloader that has visibility over everything in your system that can possibly contain an implemetation of the service requested. The load() method as used above uses the ThreadContextClassLoader (TCCL) to find all the ServiceLoader implementations in the system. There are a number of issues with this from an OSGi perspective.<br />
<ol><li>The value of the TCCL is generally not defined in an OSGi context.</li>
<li>The implementations are found by looking for a resource in META-INF/services. Every jar that contains an implementation of a service (e.g. my org.acme.MySPI implementation) should advertise this by putting a META-INF/service/org.acme.MySPI file in its jar file.</li>
<li>Once the above resources have been found, the contents of them are parsed to obtain the class names of implementations, normally these classes reside in an implementation package, something like org.acme.impl.MySPIImpl... ServiceLoader will instantiate the classes found using their no-arg constructor.</li>
</ol><br />
Issue 2 clearly poses an issue if this resource is to be obtained through the classloading rules set by OSGi as OSGi wires a package import only to precisely one exporter. This gives great predictability (you know exactly where you're getting a package from) but it doesn't work if you need to get the same resource in the same package from multiple bundles. And even if you were able to get all the META-INF/services resources in your system you would still have the issue that the classes listed in there are generally private implementation classes that you don't want to export outside of a bundle. To load and instantiate them from a consuming bundle you'd also have to import these private implementation packages which would be totally horrible (issue 3).<br />
<br />
So how should we do this in OSGi? Clearly the best answer is: use the OSGi service registry. It doesn't suffer from any of the problems ServiceLoader has: no need to expose and import private packges, no need to expose in a single package across multiple bundles. Plus the OSGi Service Registry supports a dynamic service lifecycle: services can come can go, additional suitable services can appear, existing services can get replaced. Furthermore, the OSGi ServiceRegistry supports a rich language of selecting services among possible candidates by using LDAP filters that work over service properties, while ServiceLoader simply gives you a snapshot of all the known service implementations at a particular point in time.<br />
<br />
However - you might be looking at some code that you'd like to use as-is. You might not have the luxury of using the OSGi Service Registry right now. Maybe you don't have the source code so you can't modify it or maybe you're just focusing on other things and don't want to modify the code. You just like to use the same code that works with ServiceLoader outside of OSGi, in OSGi...<br />
<br />
For this use-case I've started looking at a solution. The solution is based on the premise that you don't want or can't modify the code that uses ServiceLoader. Since this code is almost certainly non-OSGi you will have to turn it into an OSGi bundle by either wrapping it (put the original jar unmodified inside an OSGi wrapper bundle) or by modifying the MANIFEST.MF file to add things like Bundle-SymbolicName and Import-Package statements. Since you're modifying this file anyway, it's not a big deal to add an extra header in there to identify the bundle as a ServiceLoader-user. The header is called SPI-Consumer. The simplest form is:<br />
<div style="font-family: "Courier New",Courier,monospace;"> SPI-Consumer: *</div>Which means that this bundle is identified as an SPI consumer with no restrictions. This means that when code such as<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> ServiceLoader<myspiprovider> ldr = ServiceLoader.load(MySPIProvider.class);</myspiprovider></span></div>is encountered, something special will have to be done. What needs to be done is that the TCCL is set to the bundle class loader of the bundle that contains the implementation of MySPIProvider for the duration of the load() call. It needs to be the classloader of the bundle itself, so it has visibility of the private packages which are needed for ServiceLoader to do its business.<br />
<br />
In my implementation, which is available in the <a href="http://aries.apache.org/modules/spi-fly.html">Apache Aries project</a>, the consumer byte-code is slightly modified so that <i>for the duration of the ServiceLoader.load() call</i> the TCCL is set to the classloader of the providing bundle(s).<br />
<br />
How do we know that a bundle is providing an implementation for use with java.util.ServiceLoader? Similar to the consumer side, this code was most likely not written as an OSGi bundle, so it either needs to be wrapped or its MANIFEST.MF needs to be enhanced with Bundle-SymbolicName etc. Here we can identify the bundle as an SPI provider by adding:<br />
<div style="font-family: "Courier New",Courier,monospace;"> SPI-Provider: *</div>This means that all the implementations found in META-INF/services are considered as SPI providers. (You can also provide a subset by enumerating all the SPI names provided, instead of specifying <span style="font-family: "Courier New",Courier,monospace;">*</span>).<br />
<br />
Specifying this header does two things:<br />
<ol><li>It marks the bundle as an SPI provider for use with bundles that are marked as SPI consumer.</li>
<li>It registers the implementations found in META-INF/services in the OSGi Service Registry so that they can be used from there too.</li>
</ol><b>Try it out!</b><br />
Ok - let's try it out. For that you need:<br />
<ul><li>An OSGi Framework - I'm going to use JBoss AS7.</li>
<li>An SPI Consumer, Provider and the SPI itself.</li>
<li>The Aries SPI-Fly project which implements the functionality associated with the SPI-Consumer and SPI-Provider Manifest headers.</li>
</ul><b>Start the OSGi Framework</b><br />
I'm taking JBoss AS7 which contains a fully compliant OSGi 4.2 Core Framework. Get version 7.0.1 from here: <a href="http://www.jboss.org/jbossas/downloads/">http://www.jboss.org/jbossas/downloads/</a><br />
Download and unzip it, then run bin/standalone.sh (or standalone.bat):<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">...</span></span><br />
<div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">11:49:45,016 INFO [org.jboss.as.deployment] (MSC service thread 1-12) Started FileSystemDeploymentService for directory /Users/davidb/jboss/jboss-as-web-7.0.1.Final/standalone/deployments<br />
11:49:45,028 INFO [org.jboss.as] (Controller Boot Thread) JBoss AS 7.0.1.Final "Zap" started in 1671ms - Started 93 of 148 services (55 services are passive or on-demand)</span></div><br />
BTW the AS7 appserver started up in 1671ms on my plain mac!<br />
<b> </b><br />
<b>Get some example bundles</b><br />
I'm going to use some of the example bundles from the Aries SPI-Fly project...<br />
<br />
You need a SPI Consumer. For instance <a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client2-bundle/src/main/java/org/apache/aries/spifly/examples/client2/impl/Activator.java">this one that uses the SPI from the Bundle Activator</a>:<br />
<div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">public class Activator implements BundleActivator {<br />
public void start(BundleContext context) throws Exception {<br />
ServiceLoader<spiprovider> ldr = ServiceLoader.load(SPIProvider.class);<br />
for (SPIProvider spiObject : ldr) {<br />
System.out.println(spiObject.doit()); // invoke the SPI object<br />
}<br />
}<br />
<br />
public void stop(BundleContext context) throws Exception {}</spiprovider></span></div><div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">}</span></div><br />
I have a separate bundle <a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-spi-bundle/src/main/java/org/apache/aries/spifly/mysvc/SPIProvider.java">that contains the SPI itself</a>: <br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">public abstract class SPIProvider {</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> public abstract String doit();</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;">}</span></span><br />
I used an abstract class here instead of an interface as that seems to be common practise with SPIs used with ServiceLoader.<br />
<br />
And I have an SPI Provider bundle <a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider2-bundle/src/main/java/org/apache/aries/spifly/mysvc/impl2/SPIProviderImpl.java">with an implementation</a>:<br />
<div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">package org.apache.aries.spifly.mysvc.impl2;<br />
import org.apache.aries.spifly.mysvc.SPIProvider;<br />
<br />
public class SPIProviderImpl extends SPIProvider {<br />
public String doit() {<br />
return "Doing it too!";<br />
}<br />
}</span></div><div style="font-family: "Courier New",Courier,monospace;"></div><div style="font-family: "Courier New",Courier,monospace;"></div><br />
The provider bundle has the <span style="font-family: "Courier New",Courier,monospace;">SPI-Provider: *</span> Manifest header in it and the consumer bundle has the <span style="font-family: "Courier New",Courier,monospace;">SPI-Consumer: *</span> header. <br />
As the SPI-Fly project hasn't been released yet, these bundles can't be downloaded from Maven yet. You can build them yourself from here: <a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples">http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples</a><br />
You can also download them from these locations where I put my built artifacts:<br />
<ul><li><a href="http://dl.dropbox.com/u/1151396/spi-fly/consumer-bundle.jar">consumer-bundle.jar</a></li>
<li><a href="http://dl.dropbox.com/u/1151396/spi-fly/spi-bundle.jar">spi-bundle.jar</a> </li>
<li><a href="http://dl.dropbox.com/u/1151396/spi-fly/provider-bundle.jar">provider-bundle.jar</a></li>
</ul><b>Add OSGi-ServiceLoader support</b><br />
The Apache Aries SPI Fly component provides two ways to add ServiceLoader support to an OSGi Framework:<br />
<ol><li>Dynamically by using the OSGi 4.3 Weaving Hooks. With this you can install the above bundles as-is in the OSGi Framework and the treatment of the ServiceLoader.load() calls will happen on the fly.</li>
<li>Statically by transforming the bundle Jar file before installing it in the OSGi framework. This mechanism doesn't need the 4.3 Weaving Hooks.</li>
</ol>As not all OSGi frameworks support the Weaving Hooks yet, I'll be showing the static mechanism here. If you want to try the dynamic mechanism see the <a href="http://aries.apache.org/modules/spi-fly.html">Aries SPI-Fly docs</a> for more info.<br />
The static mechanism consists of a tool that transforms your bundles inserting the bytecode needed to set the TCCL when ServiceLoader.load() is called. You can build the tool yourself from here: <a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-static-tool">http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-static-tool</a> or download the copy that I built from here: <a href="http://dl.dropbox.com/u/1151396/spi-fly/spifly-static-tool.jar">spifly-static-tool.jar</a> .<br />
<div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br />
</span></div><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> java -jar spifly-static-tool.jar consumer-bundle.jar </span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> [SPI Fly Static Tool] Processing: consumer-bundle.jar</span></span><br />
When it's finished you'll find a new bundle called <span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">consumer-bundle_spifly.jar</span></span> in the same directory. This bundle has the ServiceLoader.load() calls treated so that the TCCL is set appropriately for it to work.<br />
<br />
<b>Use it at Runtime</b><br />
Our transformed client bundle has an additional dependency on some SPI-fly classes which are used to appropriately set the TCCL once ServiceLoader.load() is called. This bundle can be found here: <a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-static-bundle">http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-static-bundle</a> and I have created a pre-built version as well: <a href="http://dl.dropbox.com/u/1151396/spi-fly/spifly-static-bundle.jar">spifly-static-bundle.jar</a> .<br />
<br />
Deploy them into your OSGi Framework. AS7 supports a variety of deployment mechanisms, <a href="https://docs.jboss.org/author/display/AS7/Helloworld+OSGi+quickstart">via maven</a>, <a href="https://docs.jboss.org/author/display/AS7/Management+Clients">AS7 Web Console or Command Line</a>, <a href="https://docs.jboss.org/author/display/AS7/OSGi+subsystem+configuration">Felix OSGi Console or JMX</a>. For the sake of simplicity I'm using deployment by copying to the <span style="font-family: "Courier New",Courier,monospace;">standalone/deployments</span> folder here. Note that although deployment by copying is extremely simple, it does require attention to the order as the bundles found in the deployments folder are instantly started.<br />
<br />
So I'm deploying:<br />
<ul><li><a href="http://dl.dropbox.com/u/1151396/spi-fly/spifly-static-bundle.jar">spifly-static-bundle.jar</a> (provides runtime support)</li>
<li><a href="http://dl.dropbox.com/u/1151396/spi-fly/spi-bundle.jar">spi-bundle.jar</a> (our SPI class)</li>
<li><a href="http://dl.dropbox.com/u/1151396/spi-fly/provider-bundle.jar">provider-bundle.jar</a> (our implementation)</li>
<li>consumer-bundle_spifly.jar (the one that we created from by running spifly-static-tool above)</li>
</ul>On the console we'll see various log messages appear, including these from our SPI Consumer:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">12:44:27,480 INFO [stdout] (MSC service thread 1-3) *** Result from invoking the SPI directly: <br />
12:44:27,483 INFO [stdout] (MSC service thread 1-3) Doing it too!</span></div><br />
Which shows that our ServiceLoader Consumer bundle is now working property inside OSGi.<br />
<br />
<b>Conclusion </b><br />
This example uses java.util.ServiceLoader provider and consumer implementations as-is in OSGi. Using the OSGi Service Registry is clearly a nicer solution, but you simply don't always have the option to change the code from using ServiceLoader to using the OSGi ServiceRegistry. That's where OSGi ServiceLoader support may come in useful.<br />
<br />
It can already be used today in any compliant OSGi 4.2 Framework, but in that case consumer bundles need to be transformed before they are installed. If you have a 4.3 (or later) framework the transformation can happen on the fly which means that the whole process becomes more user-friendly: just install ServiceLoader support in the Framework and deploy your bundles - no static tool is needed in that case.<br />
<br />
ServiceLoader support is being turned in an OSGi Specification scheduled to be part of the upcoming OSGi Enterprise Release (due early 2012). You can read more details about it in RFC 167 in the Early Access Draft here: <a href="http://www.osgi.org/download/osgi-early-draft-2011-05.pdf">http://www.osgi.org/download/osgi-early-draft-2011-05.pdf</a>David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com15tag:blogger.com,1999:blog-9210566578097047576.post-51090916490381368182010-11-11T07:20:00.000-08:002010-11-11T09:47:07.496-08:00Using OSGi in JBoss AS7Recently the first milestone of JBoss Application Server 7 has been released which now comes with OSGi support built-in!<br />
<br />
<b style="font-family: Arial,Helvetica,sans-serif;">OSGi Support in JBoss AS7 what does that mean? </b><br />
It means that AS7 comes with an OSGi framework inside that you can use to deploy your OSGi bundles in. Besides providing standards-compliant OSGi functionality, the OSGi framework in AS7 will also provide integration with non-OSGi components (such as JavaEE ones) deployed in the Application Server. This is realized by the JBoss OSGi framework that was developed from the ground up with a specific focus to provide the best possible integration with AS7. The framework is mostly the work of Thomas Diesler, although I have also been involved in writing bits of it over the past few months...<br />
<br />
<div style="font-family: Arial,Helvetica,sans-serif;"><b>Let's give it a go!</b></div>So if you're curious, here's a quick demo on how to develop a 'hello world' bundle using Eclipse and deploy it into AS7. But first start by downloading an AS7 build from here: <a href="http://www.jboss.org/jbossas/downloads">http://www.jboss.org/jbossas/downloads</a>. Unzip it somewhere on disk and start it up by running:<br />
<div style="font-family: "Courier New",Courier,monospace;"> bin/standalone.sh </div><br />
<div style="font-family: Arial,Helvetica,sans-serif;"><b>Develop your OSGi bundle</b></div>You can develop your bundle any way you like. For little projects or simply to get started I always use the Eclipse PDE that's part of the <a href="http://www.eclipse.org/downloads/">'classic' distribution</a>. For more serious projects I normally use maven with the <a href="http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html">Felix Bundle Plugin</a>. <br />
<br />
Here I simply follow the PDE wizard to develop my new TestBundle. Go File | New Project | Plug-in Project, specifying a standard OSGi Framework as the target platform:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNvyQShhRVI/AAAAAAAAAR8/oAgnJeq2s4E/s1600/shot1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNvyQShhRVI/AAAAAAAAAR8/oAgnJeq2s4E/s400/shot1.png" width="377" /></a></div><br />
<br />
Select the 'Hello OSGi Bundle' template:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNvyQ4OPk6I/AAAAAAAAASA/VA5-jGRjXC0/s1600/shot2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNvyQ4OPk6I/AAAAAAAAASA/VA5-jGRjXC0/s400/shot2.png" width="378" /></a></div><br />
<br />
And after hitting 'Finish' you've got a simple OSGi bundle, which has an Activator provided by the template:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNvyRukEXDI/AAAAAAAAASE/J8plZAWSmME/s1600/shot3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="371" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNvyRukEXDI/AAAAAAAAASE/J8plZAWSmME/s640/shot3.png" width="640" /></a></div><br />
<br />
For this posting I'm not making any changes to the OSGi bundle code, so let's just simply File | Export it as a deployable plugin:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNvySO1q-2I/AAAAAAAAASI/DlACAh3trpM/s1600/shot4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="301" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNvySO1q-2I/AAAAAAAAASI/DlACAh3trpM/s400/shot4.png" width="400" /></a></div>This puts the OSGi bundle in the directory that you specified in the wizard. In my case the bundle is called <span style="font-family: "Courier New",Courier,monospace;">TestBundle_1.0.0.jar</span>.<br />
<br />
<div style="font-family: Arial,Helvetica,sans-serif;"><b>Now deploy in JBoss AS7!</b></div>I started AS7 somewhere using the <span style="font-family: "Courier New",Courier,monospace;">bin/standalone.sh</span> script and it has started up:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">...</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">13:47:22,633 INFO [org.jboss.as.deployment] (pool-1-thread-2) Started FileSystemDeploymentService for directory /home/davidb/temp/as7/jboss-7.0.0.Alpha1/standalone/deployments</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;">13:47:22,636 INFO [org.jboss.as.server] (pool-1-thread-1) JBoss AS 7.0.0.Alpha1 "Halloween" started in 3627ms. - Services [Total: 150, On-demand: 7. Started: 143]</span></span><br />
Ok, so it has started up. A little bit higher in the log you'll also see:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">13:47:21,007 INFO [osgi] Activating OSGi Subsystem</span></span><br />
<br />
Good - OSGi is there.<br />
Now I copy my <span style="font-family: "Courier New",Courier,monospace;">TestBundle_1.0.0.jar</span> into the <span style="font-family: "Courier New",Courier,monospace;">.../jboss-7.0.0.Alpha1/standalone/deployments</span> folder and a moment later I can see the following message in the log:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">13:52:24,116 INFO [stdout] (pool-1-thread-2) Hello World!!</span></span><br />
This is the message printed by our OSGi Bundle Activator running in AS7!<br />
<br />
<br />
<div style="font-family: Arial,Helvetica,sans-serif;"><b>Control the framework through JMX</b></div>The OSGi Framework in JBoss AS7 does not currently come with a console application - you can control it through JMX. So let's fire up jconsole, use <span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">service:jmx:rmi:///jndi/rmi://127.0.0.1:1090/jmxrmi</span></span> to connect to the Remote Process:<br />
<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNv40AEP9rI/AAAAAAAAASM/6X5mk4gp32U/s1600/shot5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="346" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNv40AEP9rI/AAAAAAAAASM/6X5mk4gp32U/s400/shot5.png" width="400" /></a></div>In the MBean tab you will find the org.osgi tree which allows us to control the OSGi framework through a JMX API standardized in the Enterprise Expert Group. You can find the listBundles() API in the bundleState operations:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNv40tgClZI/AAAAAAAAASQ/GWtJt2FA7tE/s1600/shot6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="208" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNv40tgClZI/AAAAAAAAASQ/GWtJt2FA7tE/s320/shot6.png" width="320" /></a></div>Yes, our TestBundle is there, and it has bundle ID 6.<br />
<br />
Let's now uninstall it using the framework MBean:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_JTmY6gtOOjs/TNv6zWFSbmI/AAAAAAAAASU/h_j2G4RnEh8/s1600/shot7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="275" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/TNv6zWFSbmI/AAAAAAAAASU/h_j2G4RnEh8/s400/shot7.png" width="400" /></a></div>Hit the uninstallBundle JMX API with the bundle ID (6) and you'll see that the bundle gets stopped and then uninstalled.<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">14:14:00,041 INFO [stdout] (RMI TCP Connection(13)-127.0.0.1) Goodbye World!!</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;">14:14:00,057 INFO [org.jboss.osgi.framework.bundle.AbstractBundle] (RMI TCP Connection(13)-127.0.0.1) Bundle uninstalled: TestBundle:1.0.0</span></span><br />
You can also control the OSGi framework through a webconsole, which can currently get installed via a separate installer, we're working on making the webconsole available in the AS7 distributable in a future milestone.<br />
<br />
<div style="font-family: Arial,Helvetica,sans-serif;"><b>Conclusion</b></div>OSGi support is now available from the very first milestone of JBoss AS7. Give it a try!<br />
If you have any questions, reach us on the forums: <a href="http://community.jboss.org/en/jbossosgi">http://community.jboss.org/en/jbossosgi</a> or via JIRA: <a href="https://jira.jboss.org/browse/JBOSGI">https://jira.jboss.org/browse/JBOSGI</a><br />
<br />
More in-depth details can be found in Thomas Diesler's blog post: <a href="http://jbossosgi.blogspot.com/2010/11/jboss-as7-osgi-integration.html">http://jbossosgi.blogspot.com/2010/11/jboss-as7-osgi-integration.html</a>David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com4tag:blogger.com,1999:blog-9210566578097047576.post-16673178331341116542010-06-17T02:34:00.000-07:002010-06-17T02:34:28.915-07:00Eek! Java reflection can cause NoClassDefFoundError after 15 callsNow this came as a bit of a surprise to me. Reading this <a href="http://www.mail-archive.com/users@felix.apache.org/msg07724.html">thread on the Felix mailing list</a>...<br />
<br />
I can't believe this: when using reflection after about 15 calls, the internal behaviour of the Sun JRE changes. With the changed behaviour comes in a package dependency on the sun.reflect package! When running under OSGi you are very much in charge of what comes in via the classloader so a new package that comes in over the same code path after about 15 calls is bad news - you really want to know your package dependencies up front.<br />
The solution is obviously to import that sun.reflect package in your affected bundle but how are you supposed to know? Let's say you have a test suite that properly tests your code and everything is looking nice. I would imagine that a testsuite doesn't test every method call 15 times to make sure its still ok. Moreover, how do we know it's 15? There could be another optimization lurking in the JRE that requires 300 calls in order for it to be exposed.<br />
Another problem with importing sun.reflect is that it's implementation specific. Do other implementations have a similar issue?<br />
<br />
Here's a message to all JVM implementors: optimize all you can but please don't change the package dependency graph after a number of invocations. At the very least make your implementation fail fast, not after <i>x</i> method calls.<br />
<br />
There is a bug for this at sun: <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6265952">http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6265952</a><br />
Accepted in 2005 but still open...David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com2tag:blogger.com,1999:blog-9210566578097047576.post-32871366018589131092009-11-18T03:28:00.000-08:002009-11-18T05:58:42.092-08:00Altering OSGi Service Lookups with Service Registry Hooks<div lang="en-GB">OSGi Services are <i><b>great</b></i><span style="font-style: normal;"><span style="font-weight: normal;">. They can solve many problems very elegantly. Take for instance SPI patterns. SPI patterns are used to make implementations pluggable. This type of pattern outside OSGi often comes with a bunch of external configuration (e.g. in META-INF/services) that makes it hard to manage and gives you a once-off chance to choose your implementation that you're stuck with for the rest of the VM lifecycle. OSGi Services provide a much more elegant way to solve this. </span></span><br />
<span style="font-style: normal;"><span style="font-weight: normal;"> </span></span> <br />
</div>In many cases clients simply want to use some functionality and don't particularly care how that functionality was created. When using OSGi Services the functionality is looked up in the OSGi Service Registry which is a directory of these. Services can be looked up by implemented interface (like a phone book) or by provided attribute (like the yellow pages). How the service got there is not interesting to the client and he's not involved in that. What if the service gets replaced while the client is active? No problem, the OSGi Service Programming model is actually built around this dynamicity. You don't need to stop service consumers when you are replacing or updating the service, they are automatically rebound.<br />
<br />
<span style="font-style: normal;"><span style="font-weight: normal;">The Service Registry also provides us with an attribute based selection mechanism which is very nice if there are multiple implementations to choose from. As an example, take a system where every available printer is represented in the Service Registry as a Service that implements the </span></span><span style="font-family: Courier New,monospace;"><span style="font-style: normal;"><span style="font-weight: normal;">org.acme.Printer</span></span></span><span style="font-style: normal;"><span style="font-weight: normal;"> interface. Each printer will have additional properties registered that help with the selection:</span></span><br />
<br />
<div lang="en-GB" style="font-style: normal; font-weight: normal;"><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SwPR2s5E6sI/AAAAAAAAARc/2HRdqC9jsnk/s1600/img1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SwPR2s5E6sI/AAAAAAAAARc/2HRdqC9jsnk/s400/img1.png" /></a><br />
</div><br />
When looking for a Printer OSGi allows you to use an LDAP-style filter to select the printer you want. So if you want a printer that can do A3 you would use this LDAP filter:<br />
</div><div class="code-western" lang="en-GB"><span style="font-size: small;"> <span style="font-family: "Courier New",Courier,monospace;"> </span></span><br />
<span style="font-size: small;"><span style="font-family: "Courier New",Courier,monospace;">(&(objec</span></span><span style="font-family: Courier New,monospace; font-size: small;"><span style="font-style: normal;"><span style="font-weight: normal;">tClass=org.acme.Printer)(paper-</span></span></span><span style="font-family: "Courier New",Courier,monospace; font-size: small;">size=A3))</span><br />
</div><div lang="en-GB" style="font-style: normal; font-weight: normal;"><br />
or if you want a printer in a location that starts with 'b' you do this:<br />
</div><div class="code-western" lang="en-GB"><span style="font-size: small;"> <span style="font-family: "Courier New",Courier,monospace;"> </span></span><br />
<span style="font-size: small;"><span style="font-family: "Courier New",Courier,monospace;">(&(objectClass=org.acme.Printer)(location=b*))</span></span><br />
</div><div lang="en-GB"><br />
This is all great and you can build your system on this using either plain OSGi code or component frameworks such as OSGi Blueprint or OSGi Declarative Services. However sometimes you may want to tweak the properties at a later date without having to rewrite your system. Assume that in your organisation all of a sudden the office numbering has changed, which would really mean that you'd have to update the 'location' attribute of all the printers. Or maybe you want to add some additional metadata to existing printers that influence the selection process, like their energy rating. <br />
</div><div lang="en-GB">Service Registry Hooks provide the building blocks that make this possible. <span style="font-style: normal;"><span style="font-weight: normal;">I wrote a little bundle called <i><b>ServiceJockey</b></i> that uses these to manipulate service registrations and how they are looked up. BTW you can find details at the end of this posting about getting ServiceJockey, which is open source and available freely under the Apache License.</span></span><br />
<br />
<span style="font-size: large;"><b>Service Registry Hooks: what are they?</b></span><br />
<div lang="en-GB">One of the driving factors for Service Registry Hooks (an OSGi standard introduced in the 4.2 Core specification) was the Remote Services (Distributed OSGi) work done in the OSGi Alliance. One of the things that implementations of the Remote Service spec need to know is what kind of services consumers are looking for so that they can go out to a discovery system to see if it might be available remotely. Eagerly registering all available remote services is clearly not scalable so we need a smarter mechanism to allow for <i>transparent</i><span style="font-style: normal;"> on demand discovery of remote services. This is what the </span><span style="font-style: normal;"><b>ListenerHook </b></span><span style="font-style: normal;"><span style="font-weight: normal;">and </span></span><span style="font-style: normal;"><b>FindHook </b></span><span style="font-style: normal;"><span style="font-weight: normal;">provide us. Together they allow us to find out what service consumers are requesting making us to only look in a remote Discovery system for those that are relevant to the current framework. </span></span><br />
</div><div lang="en-GB"><br />
</div><div lang="en-GB"><span style="font-style: normal;"><span style="font-weight: normal;">Another problem was frequently brought up. What if you have an existing bundle that doesn't know anything about Remote Services? What is the default behaviour? Should all service lookups all of a sudden always include remote services? The Remote Services spec does include a special property that you can add to your filter to influence this (</span></span><span style="font-family: Courier New,monospace; font-size: small;"><span style="font-style: normal;"><span style="font-weight: normal;">service.imported</span></span></span><span style="font-style: normal;"><span style="font-weight: normal;">) but you clearly don't want to break up all your existing service consumers to add this extra property to their lookup filters.</span></span><br />
</div><div lang="en-GB"><span style="font-style: normal;"><span style="font-weight: normal;">It turned out that there wasn't really a valid answer to that question applicable to all cases. If you're working on a system only using Remote Services for a select set of services it may make sense to default all other services consumers to </span></span><i><span style="font-weight: normal;">not</span></i><span style="font-style: normal;"><span style="font-weight: normal;"> use Remote Services. On the other hand, maybe if you're building a Cloud-based infrastructure where services can move freely from one container to another the default behaviour could be that you do allow remote services for just about anything.</span></span><br />
</div><div lang="en-GB"><span style="font-style: normal;"><span style="font-weight: normal;">Since there isn't really a one-size fits-all here the Service Registry Hooks make it possible to provide a </span></span><i><span style="font-weight: normal;">policy</span></i><span style="font-style: normal;"><span style="font-weight: normal;"> for this problem in a separate bundle. You can create </span></span><span style="font-style: normal;"><b>EventHooks </b></span><span style="font-style: normal;"><span style="font-weight: normal;">and </span></span><span style="font-style: normal;"><b>FindHooks </b></span><span style="font-style: normal;"><span style="font-weight: normal;">to influence what services consumers can see. This effectively allows you to add extra conditions to the lookup of service consumers without the need to modify those consumers.</span></span><br />
</div><div lang="en-GB"><br />
</div><div lang="en-GB"><span style="font-style: normal;"><span style="font-weight: normal;">Finally, </span></span><span style="font-style: normal;"><b>EventHooks </b></span><span style="font-style: normal;"><span style="font-weight: normal;">and </span></span><span style="font-style: normal;"><b>FindHooks</b></span><span style="font-style: normal;"><span style="font-weight: normal;">, together with the a </span></span><span style="font-style: normal;"><b>ServiceListener</b></span><span style="font-style: normal;"><span style="font-weight: normal;">, allow you to proxy Service Registrations, where you provide a second registration of the same object with modified properties, hiding the original.</span></span><br />
</div><div lang="en-GB" style="font-style: normal; font-weight: normal;">The ability to provide an alternate registration on the fly and the possibility to impose extra conditions on existing service consumer lookups is what I built ServiceJockey around.<br />
</div><div lang="en-GB" style="font-style: normal; font-weight: normal;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SwPR4vKkr7I/AAAAAAAAARk/jBGn1vJTTRs/s1600/img2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SwPR4vKkr7I/AAAAAAAAARk/jBGn1vJTTRs/s640/img2.png" /></a><br />
</div><div lang="en-GB" style="font-style: normal; font-weight: normal;"><br />
</div><div lang="en-GB" style="font-style: normal; font-weight: normal;">ServiceJockey doesn't replace the Service Object, it only effectively replaces the registration in the Service Registry plus it can put additional constraints on client lookups. But you could go further. If you wanted to actually replace or shadow the real service object and do some work when somebody uses it (maybe log it or something) you could put an additional object between the consumer and the original service object.<br />
</div><div lang="en-GB" style="font-style: normal; font-weight: normal;"><br />
</div><div lang="en-GB" style="font-style: normal; font-weight: normal;">Because the Service Registry Hooks effectively modify some basic behaviour of the framework (the visibility of Services) they should really be started early in the Framework lifecycle. You can achieve that by giving them a low start level. <br />
</div><div lang="en-GB" style="font-style: normal; font-weight: normal;">The Service Registry Hooks are available in Felix 1.8.0 and Equinox 3.5 and newer versions of these.<br />
</div><b><span style="font-size: large;"><br />
Altering Service Registrations</span></b> <br />
<div lang="en-GB">Let's start with a bundle that consumes (uses) a Printer. It isn't interested in which printer, as long as it can print A4:<br />
</div><div lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;"> BundleContext context = ... </span><span style="font-size: small;"><span style="color: #3f7f5f;">// from Activator.start()</span></span><br />
</div><div lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;"> Filter filter = context.createFilter(</span><br />
</div><div lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;"><span style="color: #2a00ff;"> "(&(objectClass=org.acme.Printer)(paper-size=A4))"</span><span style="color: black;">); </span></span> <br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: #0000c0;">st</span><span style="color: black;"> = </span><span style="color: #7f0055;"><b>new</b></span><span style="color: black;"> ServiceTracker(context, filter, </span><span style="color: #7f0055;"><b>null</b></span><span style="color: black;">) {</span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> </span><span style="font-size: small;"><span style="color: #7f0055;"><b>public</b></span><span style="color: black;"> Object addingService(ServiceReference ref) {</span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> </span><span style="font-size: small;"><span style="color: #3f7f5f;">// print out some information on the printer</span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: #3f7f5f;"> // or use the printer</span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> </span><span style="font-size: small;"><span style="color: #7f0055;"><b>return</b></span><span style="color: black;"> </span><span style="color: #7f0055;"><b>super</b></span><span style="color: black;">.addingService(ref);</span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;"> } </span> <br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;"> };</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;"> </span><span style="font-size: small;"><span style="color: #0000c0;">st</span><span style="color: black;">.open();</span></span><br />
</div><div lang="en-GB"><br />
</div><div lang="en-GB">When I run this it's reporting both printers that I have in my system:<br />
</div><div lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">Printer:</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">objectClass: [org.acme.Printer]</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">service.id: 27</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">name: p1</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">location: b283</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">capabilities: [Double-sided]</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">paper-size: [A3, A4]</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><br />
</span> <br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">Printer:</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">objectClass: [org.acme.Printer]</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">service.id: 28</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">name: p7</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">location: a12</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">capabilities: [Colour, Staple]</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="color: black; font-size: small;">paper-size: [A4, Letter]</span><br />
</div><div align="left" lang="en-GB" style="margin-bottom: 0in;"><br />
<br />
</div><div lang="en-GB">So the client has arbitrary access to both of them. Now lets see if you can modify the client behaviour so that it only uses a printer with an Energy Rating < 50. <br />
</div><div lang="en-GB">But hold on, we don't even know about energy ratings yet! We need to add this information to the Printer registration <b>without changing the bundle(s) that register the Printers.</b><span style="font-weight: normal;"> Let's user Service Jockey to do this.</span><br />
</div><div lang="en-GB"><br />
</div><div lang="en-GB">Service Jockey uses the OSGi Extender Model. This means that it is driven from a data file in a bundle. <br />
</div><div lang="en-GB"><span style="font-size: small;">So I've got a bundle (called PrinterJockey) that contains META-INF/sj.xml:</span><br />
</div><div lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: teal;"><</span><span style="color: #3f7f7f;">service-jockey</span><span style="color: teal;"> </span><span style="color: #7f007f;">xmlns</span><span style="color: black;">=</span><span style="color: #2a00ff;"><i>"http://www.coderthoughts.org/schemas/sj/v1.0.0"</i></span><span style="color: teal;">></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: teal;"> <</span><span style="color: #3f7f7f;">proxy-registration</span><span style="color: teal;">></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> </span><span style="font-size: small;"><span style="color: teal;"><</span><span style="color: #3f7f7f;">rule</span><span style="color: teal;">></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> </span><span style="font-size: small;"><span style="color: teal;"><</span><span style="color: #3f7f7f;">service-filter</span><span style="color: teal;">></span><span style="color: black;">(</span><span style="color: #2a00ff;">&</span><span style="color: black;">(objectClass=org.acme.Printer)(name=p1))</span></span><span style="font-size: small;"><span style="color: teal;"><</span></span><span style="font-size: small;"><span style="color: teal;"><span style="color: #3f7f7f;">/service-filter</span><span style="color: teal;">></span></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> <span style="color: teal;"><</span><span style="color: #3f7f7f;">add-property</span> <span style="color: #7f007f;">key</span><span style="color: black;">=</span><span style="color: #2a00ff;"><i>"energy-rating"</i></span><span style="color: teal;">></span><span style="color: black;">75</span></span><span style="font-size: small;"><span style="color: teal;"><</span></span><span style="font-size: small;"><span style="color: teal;"><span style="color: #3f7f7f;">/add-property</span><span style="color: teal;">></span></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> <span style="color: teal;"><span style="color: #3f7f7f;"></rule</span><span style="color: teal;">></span></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> <span style="color: teal;"><</span><span style="color: #3f7f7f;">rule</span><span style="color: teal;">></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> <span style="color: teal;"><</span><span style="color: #3f7f7f;">service-filter</span><span style="color: teal;">></span><span style="color: black;">(</span><span style="color: #2a00ff;">&</span><span style="color: black;">(objectClass=org.acme.Printer)(name=p7))</span></span><span style="font-size: small;"><span style="color: teal;"><</span></span><span style="font-size: small;"><span style="color: teal;"><span style="color: #3f7f7f;">/service-filter</span><span style="color: teal;">></span></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;"> </span><span style="color: teal;"><</span><span style="color: #3f7f7f;">add-property</span> <span style="color: #7f007f;">key</span><span style="color: black;">=</span><span style="color: #2a00ff;"><i>"energy-rating"</i></span><span style="color: teal;">></span><span style="color: black;">50</span></span><span style="font-size: small;"><span style="color: teal;"></</span></span><span style="font-size: small;"><span style="color: teal;"><span style="color: #3f7f7f;">add-property</span><span style="color: teal;">></span></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> </span><span style="font-size: small;"><span style="color: teal;"><</span></span><span style="font-size: small;"><span style="color: teal;"><span style="color: #3f7f7f;">/rule</span><span style="color: teal;">></span></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> </span><span style="font-size: small;"><span style="color: teal;"><</span></span><span style="font-size: small;"><span style="color: teal;"><span style="color: #3f7f7f;">/proxy-registration</span><span style="color: teal;">></span></span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: teal;"></</span></span><span style="font-size: small;"><span style="color: teal;"><span style="color: #3f7f7f;">service-jockey</span><span style="color: teal;">></span></span></span><br />
</div><div lang="en-GB" style="margin-bottom: 0in;"><span style="font-size: small;"><br />
</span><br />
</div><div lang="en-GB"><span style="font-size: small;">I will have to tell the Service-Jockey that my bundle contains configuration for it, for that I'm adding a header to the Manifest:</span><br />
</div><div lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: maroon;">Service-Jockey</span><span style="color: black;">: META-INF/sj.xml</span></span><br />
</div><div lang="en-GB"><span style="font-size: small;"><br />
</span><br />
</div><div lang="en-GB"><span style="font-size: small;">Lets run the Printer Consumer bundle together with Service Jockey and my 'Printer Jockey' configuration bundle that contains the sj.xml file:</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">id State Level Bundle</span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">0 ACTIVE 0 org.eclipse.osgi_3.5.1.R35x_v20090827</span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">1 ACTIVE 1 org.coderthoughts.servicejockey_0.0.1</span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">2 ACTIVE 1 PrinterJockey_1.0.0</span></span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">3 ACTIVE 10 Printers_1.0.0</span></span><br />
</div><div lang="en-GB"><span style="font-size: small;"><br />
</span><br />
</div><div lang="en-GB"><span style="font-size: small;">In my case the Printers bundle both registers and consumes the Printers, but that's because its a little test bundle. It's not really relevant. Note that the Printers bundle has a higher start level than the Jockey ones and therefore starts later.</span><br />
</div><div lang="en-GB"><span style="font-size: small;"><br />
</span><br />
</div><div lang="en-GB"><span style="font-size: small;">When I run my system again I can see that it's doing some work. Now my client reports these services:</span><br />
</div><div lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">Printer:</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">objectClass: [org.acme.Printer]</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">service.id: 30</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">name: p1</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">location: b283</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">capabilities: [Double-sided]</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">paper-size: [A3, A4]</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">energy-rating: 75</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">.ServiceJockey: Proxied</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><br />
</span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">Printer:</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">objectClass: [org.acme.Printer]</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">service.id: 32</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">name: p7</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">location: a12</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">capabilities: [Colour, Staple]</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">paper-size: [A4, Letter]</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">energy-rating: 50</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">.ServiceJockey: Proxied</span></span><br />
</div><div align="left" lang="en-GB" style="margin-bottom: 0in;"><span style="font-size: small;"><br />
</span><br />
</div><div lang="en-GB" style="margin-bottom: 0in;"><span style="font-size: small;">Aha! I've got my energy rating in there! That's good. Remember that these are alternative registrations. The original ones haven't changed, they're just hidden.</span><br />
</div><span style="font-size: small;"><b><br />
<span style="font-size: large;"> Altering Client Side Lookups </span><br />
</b> </span><br />
<div lang="en-GB"><span style="font-size: small;">Now I want my client to only use the one that is rated <= 50. </span><br />
</div><div lang="en-GB"><span style="font-size: small;">That's done by adding another rule to the Service Jockey configuration. A rule that adds an extra filter to any matching lookup in a consumer.</span><br />
</div><div style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: teal;"><span lang="en-GB"><</span></span><span style="color: #3f7f7f;"><span lang="en-GB">service-jockey</span></span><span lang="en-GB"> </span><span style="color: #7f007f;"><span lang="en-GB">xmlns</span></span><span style="color: black;"><span lang="en-GB">=</span></span><span style="color: #2a00ff;"><span lang="en-GB"><i>"http://www.coderthoughts.org/schemas/sj/v1.0.0"</i></span></span><span style="color: teal;"><span lang="en-GB">></span></span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> <b><span style="color: teal;"><</span><span style="color: #3f7f7f;">restrict-visibility</span><span style="color: teal;">></span></b></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;"> </span><b><span style="color: teal;"><</span><span style="color: #3f7f7f;">rule</span><span style="color: teal;">></span></b></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> <b><span style="color: teal;"><</span><span style="color: #3f7f7f;">bsn</span><span style="color: teal;">></span><span style="color: black;">.*</span></b></span><span style="font-size: small;"><b><span style="color: teal;"><span style="color: #3f7f7f;"></</span></span></b></span><span style="font-size: small;"><b><span style="color: teal;"><span style="color: #3f7f7f;">bsn</span><span style="color: teal;">></span></span></b></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> <b><span style="color: teal;"><</span><span style="color: #3f7f7f;">service-filter</span><span style="color: teal;">></span><span style="color: black;">(objectClass=org.acme.Printer)</span></b></span><span style="font-size: small;"><b><span style="color: teal;"><span style="color: #3f7f7f;"></</span></span></b></span><span style="font-size: small;"><b><span style="color: teal;"><span style="color: #3f7f7f;">service-filter</span><span style="color: teal;">></span><span style="color: black;"> </span></span></b> </span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> <b><span style="color: teal;"><</span><span style="color: #3f7f7f;">add-filter</span><span style="color: teal;">></span><span style="color: black;">(energy-rating</span><span style="color: #2a00ff;"><</span><span style="color: black;">=50)</span></b></span><span style="font-size: small;"><b><span style="color: teal;"><span style="color: #3f7f7f;"></</span></span></b></span><span style="font-size: small;"><b><span style="color: teal;"><span style="color: #3f7f7f;">add-filter</span><span style="color: teal;">></span></span></b></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> <b><span style="color: teal;"><span style="color: #3f7f7f;"></rule</span><span style="color: teal;">></span></span></b></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: teal;"><b> </b></span></span><span style="font-size: small;"><span style="color: teal;"><</span></span><span style="font-size: small;"><b><span style="color: teal;"><span style="color: #3f7f7f;">/restrict-visibility</span><span style="color: teal;">></span></span></b></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;"> </span><span style="color: teal;"><</span><span style="color: #3f7f7f;">proxy-registration</span><span style="color: teal;">></span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: teal;"> ...</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"> </span><span style="font-size: small;"><span style="color: teal;"></</span></span><span style="font-size: small;"><span style="color: teal;"><span style="color: #3f7f7f;">proxy-registration</span><span style="color: teal;">></span></span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: teal;"><</span></span><span style="font-size: small;"><span style="color: teal;"><span style="color: #3f7f7f;">/service-jockey</span><span style="color: teal;">></span></span></span><br />
</div><div lang="en-GB"><span style="font-size: small;"><br />
</span><br />
</div><div lang="en-GB"><span style="font-size: small;">You can specify a regular expression to say what the Bundle Symbolic Name of the consumer bundle(s) is that the rule applies to. In my case .* matches anything. So it applies to any bundle that has OSGi Service consumers. By the way, there's also a <span style="font-family: Courier New,monospace;"><bver></bver></span> tag.</span><br />
</div><div align="left" style="margin-bottom: 0in;"><span style="font-size: small;"><br />
</span><br />
</div><div align="left" style="margin-bottom: 0in;"><span lang="en-GB" style="font-size: small;">You specify to what services the additional filter applies with the </span><span style="font-size: small;"><span style="color: black;"><span style="font-family: Courier New,monospace;"><span lang="en-GB"><service-filter> </service-filter></span></span></span><span lang="en-GB">tag and you specify the additional filter in </span><span style="color: black;"><span style="font-family: Courier New,monospace;"><span lang="en-GB"><add-filter></add-filter></span></span></span><span lang="en-GB">. In my case I'm adding the </span><span style="color: black;"><span style="font-family: Courier New,monospace;"><span lang="en-GB">(energy-rating<=50) </span></span></span><span lang="en-GB">condition to any Printer Service returned to a consumer (the condition looks a bit dodgy because of the XML escaping of the '<' sign - you could use a CDATA section instead...).</span></span><br />
</div><div align="left" lang="en-GB" style="margin-bottom: 0in;"><span style="font-size: small;">Note that the Service Filter applies to any service returned to a service consumer, regardless of the filter used by the client itself looks like. </span><br />
</div><div align="left" lang="en-GB" style="margin-bottom: 0in;"><span style="font-size: small;"><br />
</span><br />
</div><div align="left" lang="en-GB" style="margin-bottom: 0in;"><span style="font-size: small;">Let's run the client again:</span><br />
</div><div align="left" lang="en-GB" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">Printer:</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">objectClass: [org.acme.Printer]</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">service.id: 32</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">name: p7</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">capabilities: [Colour, Staple]</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">location: a12</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">paper-size: [A4, Letter]</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">energy-rating: 50</span></span><br />
</div><div align="left" style="font-family: "Courier New",Courier,monospace; margin-bottom: 0in;"><span style="font-size: small;"><span style="color: black;">.ServiceJockey: Proxied</span></span><br />
</div><div align="left" lang="en-GB" style="margin-bottom: 0in;"><span style="font-size: small;"><br />
</span><br />
</div><div align="left" lang="en-GB" style="margin-bottom: 0in;"><span style="font-size: small;">Bingo - it only selects the service I wanted it to select. Based on additional properties and criteria listed in my Service Jockey configuration file. I did not have to modify the Printer Service registration bundle or the Consumer bundle...</span><br />
</div><span style="font-size: large;"><b><br />
Is it overkill?</b> </span><br />
<div lang="en-GB"><span style="font-size: small;">Seems like a lot of work for just adding a service property, is there not a lighter way to at least add values to Service Registrations? Well at least that was my initial impression. However looking at the ServiceJockey bundle, it's only 14kb. Besides that there is currently no other way to achieve this... </span><br />
</div><span style="font-size: small;"><b><br />
<span style="font-size: large;"> Service Jockey - ride your service interactions</span></b> </span><br />
<div lang="en-GB"><span style="font-size: small;">I didn't show any of the code to actually use the EventHook and the FindHook. Have a look at the <a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/ServiceJockey/src/org/coderthoughts/servicejockey/HidingEventHook.java">HidingEventHook</a> and <a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/ServiceJockey/src/org/coderthoughts/servicejockey/HidingFindHook.java">HidingFindHook</a> source code for that.</span><br />
<br />
<span style="font-size: small;">You can check out the source (Apache License) as an Eclipse Project <a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/ServiceJockey">from SVN here</a>. If you fancy modifying the code, I would recommend you also <a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/ServiceJockeyTest">get the tests project</a> and add new ones. The test bundle depend on the <a href="http://code.google.com/p/mockito/">Mockito bundle</a> for its mock objects...</span><br />
</div><div lang="en-GB"><span style="font-size: small;"> </span><br />
</div><div lang="en-GB"><span style="font-size: small;">I'm sure the Service Jockey isn't perfect, because I only wrote it during an airplane flight so feel free to send patches :)<br />
</span><br />
</div><div lang="en-GB"><span style="font-size: small;">I also put a <a href="http://coderthoughts.googlecode.com/files/org.coderthoughts.servicejockey_0.0.1.jar">binary download of ServiceJockey here</a>.</span><br />
</div><div lang="en-GB"><span style="font-size: small;">Also in the source tree, Eclipse projects for the <a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/Printers">Printers</a> and <a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/PrinterJockey">PrinterJockey</a> example bundles. </span><br />
</div></div><div lang="en-GB"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-style: normal;"><span style="font-weight: normal;"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span><br />
</div><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: large;"><b></b></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>David Bosschaerthttp://www.blogger.com/profile/13786738766478890804noreply@blogger.com1