Flying Backwards – Soundcloud Song

I just felt like posting some of my songs on SoundCloud.com here.

Veröffentlicht unter Allgemein | Hinterlasse einen Kommentar

CSV to XLS converter – How to quickly convert CSV files online without coding in your browser

Today I had to create a CSV (comma separated value) file containing product offers and convert it into another spreadsheet file which required slightly different columns and some modifications to columns.

I could have done all this in Excel or Open Office or write my self a little script e.g. in PHP or Java. But I found a cool web-based tool called Transformy which allowed me to do just that without any coding or hacking in Excel. In this article I want to do a little review of this still young tool.

Transformy works pretty simple. You upload your CSV or Excel file, it automatically detects your file delimiter and shows your data in a table. Then you just click here and there to modify your columns. You can remove, add or rename columns, change the order of columns as required. You can apply different various functions like Search&Replace, simple math calculations or date format conversions to each column to modify the content. The Quick-Start Example is pretty nice to get started even without an own file.

The UI comes pretty clean, but it also contains lots of useful advanced features covered behind the Settings button. It has pretty good support for all the low level stuff like delimiters, text qualifiers, line endings (Unix, Windows) and character encoding. All that can be configured for the source and for the target file.

In a perfect world all systems would use UTF-8 for encoding, but as most of us know file encodings often can be a real pain and properly converting between different encodings is necessary. In e-commerce webshops, marketplaces or price comparison sites exchange product data, prices and stock data using Comma-separated files but each system usually requires a different encoding. This tool makes it pretty easy to convert between the most common encodings with a few clicks.

Another nice effect is that it is pretty tolerant even to invalid CSV-files. A frequent problem when people create those files are double quotes and line breaks in descriptions. In order to let CSV-parsers recognize line breaks you have to enclose your content with a text-qualifier, usually double quotes (“your content”). Doing that allows you to also have line-breaks  in description fields. The problem is when your description also contains a double quote character as in 22″ monitor. Tor circumvent the problem you have to double the quote character like this 22″” monitor to still let the parser parse your file. Transformy seemed to even handle those cases where the doubling was not done properly which is often reality.

Another nice feature is the built-in group by functionality. It allows you to group by one column and apply aggregate-functions to the other columns.

In the screenshot I grouped by the column pricecurrency and applied two aggregate functions to the identifier and name column. The identifier column has the Concat distinct values aggregate function applied which concatenates all grouped values together using a specified delimiter. The name column just has the Count rows aggregate function applied which shows the number of grouped rows. This grouping and aggregate is pretty handy when you want to quickly analyse e.g. a file with product data to see how many distinct categories are contained in the category column.

The sorting option lets you sort the complete file by a specified column in ascending or descending order.

Of course, most of the stuff can be done with Excel or a database like MySQL. But this tool is web-based and just allows to do it quickly online without requiring you to do coding, mastering Excel or a MySQL database.

 

If you are all done mass-editing, crunching and converting and you see the final result you can simply download the target file using your specified delimiter, text-qualifier and encoding or alternatively download it as a real Excel (.xls) file. If you are working on large files and just want to test the resulting file, you can also use the Download preview file which just downloads a file containing the rows you see in the preview, which is much smaller than the actual final file. I especially liked the Excel download option, because it gives me a easy way to convert a txt file to Excel (.xls) online. This is great because Excel always has problems with line-breaks in columns of CSV files, but if I first open it in Transformy and then convert it to Excel I don’t have problems with line breaks anymore.

That’s it for my little review of Transformy. If you are working with CSV files and spreadsheets and often need to manipulate and crunch the data or even just need to quickly convert between those formats or export your Magento product data to a different format then maybe it also is a tool for you. A german description of the tool can be found here.

Veröffentlicht unter Software-Development | Verschlagwortet mit | 136 Kommentare

Great resources on solving OSGi “Uses” constraint violations

I recently had the “famous” “uses” constraint violation when building my OSGI application with PDE Build. It was complaining about:

Package uses conflict: Import-Package: javax.mail; version=”1.4.1″

The following resources have helped me locating the root cause and understanding the problem:

http://njbartlett.name/2011/02/09/uses-constraints.html
http://blog.springsource.com/2008/10/20/understanding-the-osgi-uses-directive/
http://blog.springsource.org/2008/11/22/diagnosing-osgi-uses-conflicts/

In my case the culprit was the following effect:

  • one bundle was doing a Require-Bundle: org.eclipse.osgi which is also referred to as the “OSGI system” bundle. According to the first article in Java 6 it automatically exports some core packages of the JDK / JRE into the classpath like javax.activation
  • The problem was that in our OSGI target platform we also had the jar com.springsource.javax.activation-1.1.1.jar which also exports javax.activation
  • The bundle which caused the problem seems to need something from javax.activation (most likely through the import-package of javax.mail and OSGI was not able to resolve this dependency because javax.activation was exported from two sources (JDK because of Require-bundle: org.eclipse.osgi and the jar com.springsource.javax.activation-1.1.1.jar)

Solution:

  • Modify MANIFEST.MF
  • replace Require-Bundle: org.eclipse.osgi with Import-package: org.osgi.framework

After that change the build was successful again.


 

Veröffentlicht unter Software-Development | 14 Kommentare

SVN: Shell script to get a modified files report of all sub-directories which are all a separate SVN folder

For our automated build we wanted a simple way to generate a report of all files which have changed between two SVN revisions.
‘svn log -v’ usually gives you such an output.

But in our scenario we have a folder /source which contains about 100 sub-directories. The way svn in this project was organized, each sub-folder had to be checked out separately , thus we could not just do a simple ‘svn log’ on /source, because /source itself was not under version control.

We wanted to execute svn log -v for each sub-folder and write the results into a file. In addition only write the output for sub-folders which really had changes between the two svn revisions.

Here is our little script svnlogreport.sh which does the trick:

#put this into a file svnlogreport.sh
output1=$(svn log -v -r $2:$3 $1)
 
if [[ "$output1" == *"line"* ]]
then
 echo "Changes in $1 for revision $2 to $3 (command: svn log -v -r $2:$3 $1)"
 echo "$output1"
fi

This script also checks if the output contains the word ‘line’ which is an indicator that there are changes in this revision. This has been tested with SVN Version 1.6.15 (r1038135).

Now go to the folder with all those sub-folders and execute the script:

cd /source
find . -maxdepth 1 -type d -exec svnlogreport.sh {} 6654 6694  \; > svnlog.txt

6654 is the start revision and 6694 is the target revision.

This command basically executes the script for each folder.

Hope that helps somebody.

Veröffentlicht unter Software-Development | Verschlagwortet mit , , , , | Kommentare deaktiviert

Using Hibernate EventListeners to encrypt / decrypt properties of an entity based on other properties

Update 2012/07/24:
It turns out my approach below is NOT GOOD! What I wanted to do is conditional encryption which is also asked in this Jasypt forum, where they say one should implement your own UserType.
http://forum.jasypt.org/Hibernate-and-Conditional-encryption-td5586032.html

The serious problem of my approach is decribed at the end. It is that the onLoad Listener calls a setter of the entity for decryption and thus marks it as dirty which causes Hibernate to persist the changed state to DB again, which is not what you want. Seems the EventListener approach is too late in the chain and UserTypes is the way to go. Unfortunatelly it is lots of code which I would need to borrow from Jasypt in this case just for a tiny extension. Let’s see if I can maybe contribute it back to Jasypt.

Here is the original entry:
We are currenlty introducing the Jasypt library in a project @Synesty in order to encrypt sensitive values in .properties files and more important for sensitive user-content in the database. Jasypt brings stuff to make this easier. But (I think) we need to do something which goes beyond the stuff what is super-simple in Jasypt.

What we have:

We have an entity JobProperty(String key, String value, boolean isAlreadyEncrypted)

What we want to do when the entity is saved/updated:

prop.setValue(encryper.encrypt(prop.getValue());
prop.setIsAlreadyEncrypted(true);

What we want to do when the entity is loaded:

if(prop.isAlreadyEncrypted == true) {
prop.setValue(encryper.decrypt(prop.getValue());
}

Basically what we want to do is encrypting a property based on one (or more) other properties, in this case depending on the property ‘isAlreadyEncoded’, which is false for legacy data before encryption was introduced. Those legacy entities are not encrypted yet, thus the code needs to know that those values cannot be decrypted.
Maybe in the future we are adding more stuff e.g. we want the user to be able to decide whether or not to encrypt, so there could be another property which we are checking in order to decide whether or not to encrypt. For those readers who might wonder why we are not using the Hibernate Custom UserType approach by Jasypt: The reason is, that this approach does not support our scenario where encryption should depend on other properties of the entity. It works great though if you always want to encrypt some property. In that case the described Hibernate UserType approach works great. But we need more flexibility and also backwards compatibility for existing non-encrypted data and also for our planned support for seemless key rotation via key-profiles.

Our approach: Hibernate Event Listeners (SaveOrUpdateEventListener, PostLoadEventListener)

We are basically using the Hibernate Event System so that we can hook into the different phases of Hibernates Entity lifecycle (a full list of Events can be found here or examples here). This approach can also be used for another common pattern, which is to maintain an “created or last-updated timestamp” field on all entities every time the entity gets updated.

We are basically hooking into the “save-update” (to encrypt before persisting the entity) and “post-load” (to decrypt after loading the entity) event.

The problem

One problem we had was the onPostLoad event, which was persisting the data again to the database after decrypting the value. This is not what we wanted.
What we wanted was that the decrypted value is just like a transient value for displaying it in the UI. The underlying database value should still be encrypted.

The solution

Our current solution for this problem is the org.hibernate.Session.setReadOnly(entity, true) method.

/**
* Set an unmodified persistent object to read only mode, or a read only
* object to modifiable mode. In read only mode, no snapshot is maintained
* and the instance is never dirty checked.
*
* @see Query#setReadOnly(boolean)
*/
public void setReadOnly(Object entity, boolean readOnly);

So basically what we are doing in pseudo-code is:

if(prop.isAlreadyEncrypted == true) {
event.getSession().setReadOnly(prop, true);  // mark the object as readonly for the current session, because otherwise hibernate will persist the decrypted value to db again.
prop.setValue(encryper.decrypt(prop.getValue());
}

Show me a full example

In our case the listener looks like this:

 

public class MyLoadInsertUpdateListener extends DefaultSaveOrUpdateEventListener implements PostLoadEventListener, SaveOrUpdateEventListener{
 
	@Override
	public void onSaveOrUpdate(SaveOrUpdateEvent arg0) {
		if(arg0.getObject() instanceof Jobsproperties){
			Jobsproperties p = (Jobsproperties) arg0.getObject();
 
				EncryptionService encService = Activator.getServiceFactory().getService(EncryptionService.class);
				if(p.getJpvalue() != null){
					p.setJpvalue(encService.encrypt(p.getJpvalue()));
					p.setIsEncrypted(1);
				}
 
		}
 
		if(arg0.getObject() instanceof JobStepProperties){
			JobStepProperties p = (JobStepProperties) arg0.getObject();
 
				EncryptionService encService = Activator.getServiceFactory().getService(EncryptionService.class);
				if(p.getJspvalue() != null){
					p.setJspvalue(encService.encrypt(p.getJspvalue()));
					p.setIsEncrypted(1);
				}
 
		}
 
		super.onSaveOrUpdate(arg0);
	}
 
	@Override
	public void onPostLoad(PostLoadEvent arg0) {
 
		if(arg0.getEntity() instanceof Jobsproperties){
			Jobsproperties p = (Jobsproperties) arg0.getEntity();
			if(p.getIsEncrypted() == 1){
				if(p.getJpvalue() != null){
					EncryptionService encService = Activator.getServiceFactory().getService(EncryptionService.class);
 
					// workaround: mark the object as readonly, because otherwise hibernate will persist the decrypted value to db again.
					arg0.getSession().setReadOnly(p, true);
 
					p.setJpvalue(encService.decrypt(p.getJpvalue()));
 
				}
			}
		}
 
		if(arg0.getEntity() instanceof JobStepProperties){
			JobStepProperties p = (JobStepProperties) arg0.getEntity();
			if(p.getIsEncrypted() == 1){
				if(p.getJspvalue() != null){
					EncryptionService encService = Activator.getServiceFactory().getService(EncryptionService.class);
 
					// workaround: mark the object as readonly, because otherwise hibernate will persist the decrypted value to db again.
					arg0.getSession().setReadOnly(p, true);
					p.setJspvalue(encService.decrypt(p.getJspvalue()));
 
				}
			}
		}
 
	}
 
}

The used EncryptionService is just an internal interface implementation which provides an encrypt/decrypt method, but it is not important in this post.

As we are using Spring in combination with Hibernate it might be also interesting to see how our listener is registered, because there are also some pitfalls:

			.... our classes
 
				... hibernate properties
<map>
</map>

If you are wondering why we registed the same class 4 times as 4 beans? Hibernates doc states:

Listeners registered declaratively cannot share instances. If the same class name is used in multiple elements, each reference will result in a separate instance of that class.

With this approach so far we managed to solve our requirement but we are still testing it. One proplem we are seeing with this approach is that the Session.setReadOnly() method does have some code smell and is maybe a hack. I think in cases where we are just loading an entity for display purposes it works fine. But in cases where we are loading the entity, performing some update and then persisting the entity we could run into problems because of the read-only mode. But so far this concern has not settled yet although we clearly have load-update-persist patterns in the code.

Alternatives

What alternatives did we have and why did we go this way?

I think the alternative would have been to maybe also implement a custom Hibernate UserType. Our first idea was to extend the Jasypt’s org.jasypt.hibernate3.type.AbstractEncryptedAsStringType but unfortunatelly all the interesting methods are declared final so the only option would be to copy & paste the whole class…. but this should be our last resort if there is no other way.

If anybody has suggestions for a better solution please comment or get in touch with me.

I hope this post is somehow helpful for you to either go down the same road or get inspiration for different approaches and the related problems around it.

Veröffentlicht unter Software-Development | Verschlagwortet mit , , , , | Kommentare deaktiviert

New Google Analytics Dashboard Layout: the opposite of a dashboard?

New Dashboard – less useful

Old Dashboard – more useful

This question in the Google Analytics help forum with the title “New Layout: the opposite of a dashboard?” nails down my opinion about the new Google Analytics. While I generally like the new glossy design which google rolls out throughout all of their apps I feel that the new Analytics dashboard is a huge step backwards especially in terms of usability. It seems Google in this specific case has not understood how people use analytics.

While researching I also came across this article where they say that this feature was ‘lost’ and they are ‘working on it’ (my favorite quote of every company’s support crew ;) ).

To summarize: I really hope that the old dashboard will make it into the new Analytics, otherwise I will use the old version as long as possible. If you feels so too, please tweet, vote or take part in the discussions to make GA useful again.

 

 

Veröffentlicht unter Allgemein, Software-Development | Verschlagwortet mit , , | Kommentare deaktiviert

What are pros and cons for url structure of a SaaS multi tenant web-app regarding scalability / load-balancing?

I just posted the question above on Quora in hope to get some answers.

I would like to know the pros and cons of choosing a URL structure for a multi tenant web-application. I also think that this could impact scalability of the site when traffic grows.
The dimensions of the application I see are two main things:
- mini-apps
- customers can register per application
- the load/traffic hotspots can be for both (e.g. there could be one customer using very many resources and there could be also a mini-app which uses much more resources than other mini-apps. So we need to be able to route traffic to different machines / parts of the cluster, depending on the traffic patterns of either users and/or apps.

I thought about the following:

Approach 1:

Main domains for each app, for guest users / anonymous:
http://apps.nameofourwebapp.com/nameOfMiniApp1
http://apps.nameofourwebapp.com/nameOfMiniApp2

Once the customer is registered I thought about:
http://yourcompanyxyz.apps.nameofourwebapp.com/nameOfMiniApp1
http://yourcompanyxyz.apps.nameofourwebapp.com/nameOfMiniApp2

Approach 2:

The other way round I could also imagine:
http://nameOfMiniApp1.nameofourwebapp.com/
http://nameOfMiniApp1.nameofourwebapp.com/yourcompanyxyz

What are the advantages and disadvantages of both approaches especially with regards to scaling / load-balancing?

Does allow Approach 1 easier routing of traffic to different machines per customer (e.g. via Amazon Route53) than Approach 2?

If you have any thoughts on this question feel free to add an answer on this Quora question. Thanks.

Veröffentlicht unter Software-Development | Verschlagwortet mit , , , , | Kommentare deaktiviert

How to use OAuth 2.0 authentication with Google’s GDATA Client Java Client Library

This article is for users who are using the Google Data Java Client Library (a.k.a. “gdata-java-client”) together with the Google API Client Library for Java (a.k.a. “google-api-java-client”).

For our app we started the  integration with Google Spreadsheets today and we were running into a problem related to authentication. The issue is that the Spreadsheet API is still based on the “older” GDATA API but we wanted to to already use OAuth 2.0 for authentication which is part of Google’s new Client API for JAVA. Both APIs can co-exist besides each other as you can read in this migration guide.

The problem we faced was that the “older” GDATA JAVA API for the SpreadSheet API has only methods for OAuth 1.0 or at least I wasn’t able to find any documentation and source code examples which showed how to use it with OAuth 2.0. I could only find examples of OAuth 1.0. The difference between OAuth 1.0 and OAuth 2.0 is that OAuth 2.0 only returns you an accessToken and rely fully on SSL, which frees you from signing and encryption stuff, while OAuth 1.0 returns you also a token_secret . In OAuth 1.0 you need to pass both things to each API call.

The problem was that the API does not seem to have any methods which only takes the accessToken. All methods I found require you to  pass things like OAuthParameters and OAuthSigner objects which are OAuth 1.0 related.
Thus the question was:
How can I tell my SpreadsheetService object to use my previously obtained OAuth 2.0 accessToken.

Solution:
I found the solution a bit between the lines here. It says that you need can set a special HTTP-Header (Authorization: Bearer ACCESS_TOKEN)

Java Code Example:

import com.google.gdata.client.spreadsheet.SpreadsheetService;

SpreadsheetService service = new SpreadsheetService("yourAppName");
service.setHeader("Authorization", "Bearer " + accessToken);
URL metafeedUrl = new URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full");
SpreadsheetFeed feed = service.getFeed(metafeedUrl, SpreadsheetFeed.class);

List spreadsheets = feed.getEntries();
for (int i = 0; i < spreadsheets.size(); i++) {
SpreadsheetEntry entry = spreadsheets.get(i);
System.out.println("\t" + entry.getTitle().getPlainText());
}

Note: This code snippet assumes you received your accessToken using the new Google Java API as shown in this example.

After doing this the API call went through without problems and returned me a list of spreadsheets.

I hope that helps some people until google makes this more clear in their documentation or adds examples. Right now I found the documentation a bit confusion with all the different authentication mechanisms possible with the APIs. I also posted my solution here on StackOverflow.

Veröffentlicht unter Software-Development | Verschlagwortet mit , , , , , , , | Kommentare deaktiviert

Weihnachtsgeschenkidee 2011 – Ein Ticayo zum ticken.

Jedes Jahr die Frage nach einem ausgefallenen Weihnachtsgeschenk für Freunde und Familie oder auch für Mitarbeiter und Kunden.

Dieses Jahr könnte die Antwort lauten: Ein Ticayo. Ticayo?
Das Ticayo ist ein nachhaltig im Erzgebirge produziertes Geschicklichkeitsspielzeug aus Holz. Dabei geht es darum, die an einem Strick befestigte Holzkugel so zu schwingen, dass sie wieder auf dem Holzstab landet bzw. sie wieder mit dem Holzstab aufgefangen werden kann.

Ausgedacht von guten Freunden von Tictoys, ist es seit diesem Jahr auf dem Markt und wartet darauf von euch “getickt” zu werden. Ich habe selber 2 Ticayos und es entfacht eine regelrechte Sucht, immer besser zu werden. Auf Youtube kann man sich auch diverse Tricks auf Video ansehen und nachmachen.
Wem es gefällt, bitte rebloggen, und weitersagen oder selbst verschenken :)

Ticayo - rot

 

Jetzt auch in einer limitierten Winteredition verfügbar.

 

Veröffentlicht unter Allgemein | Verschlagwortet mit , , | Kommentare deaktiviert

How to add new gems to jruby in an Equinox OSGI Environment (or how to set jruby.home)

After I burned the midnight oil yesterday to debug into a problem I was having with getting the fastercsv gem to work with JRuby in my OSGI environment I decided to write a post about it and how I finally got it working.The solution is not super clean but it works and at least it helped me to understand some of the inner workings of how JRuby determines file locations and classpaths and uses classloaders to load files….and that all that is different in an OSGI Environment.

The problem

  • i want to use the fastercsv gem using require ‘fastercsv’ in the jruby script i am calling using a ‘new ScriptingContainer(…)‘ instance
  • jruby always errored out telling me that it can’t find the required gem
  • I have read JRuby 1.1.6: Gems-in-a-jar and lots of other blogs about it but it didn’t help so far

My Setup

  • custom web-application build from multiple OSGI-bundles and using the embedded Jetty server
  • there is one ‘scripting’ bundle where all code related to Java-scripting is included (e.g. JRuby)
  • in this ‘scripting’ bundle there is a lib-folder containing my jRuby-complete.jar which is added to the local classpath of that bundle (jruby-complete is NOT in my targetplatform and is not started as an OSGI-bundle here, but I use it inside my own OSGI bundle.
  • my OSGI Container is Eclipse Equinox (Equinox SDK Version 3.5m4)

The solution

The reason why all existing information out there didn’t really work for me was because I am using JRuby in an OSGI environment and the main difference is that classloading in OSGI environments is different, as each bundle has its own classloader. It seems that JRuby uses the wrong classloader when using ScriptingContainer so that it determines a wrong path of jruby.home environment.

Also the way I added the fastercsv gem to my system is a bit unusual and hacky, but I will improve this once I find a better way which is my next step. I have create installed the gem as described here (i didn’t create a jar) but I have copied the gem in my jruby-complete.jar (rename jar to zip, unpack, put fastercsv into the gems folder, zip it and rename it to jar again). That is hacky but I did that because JRuby is trying to find the gems in jruby.home which is by default in /META-INF/jruby.home inside the jruby-complete.jar.

In my opinion that would be enough actually so that JRuby can determine my new gem, but because of my OSGI container it was using the wrong classloader to load the file so JRuby ended up detemining a wrong ‘jruby.home’ path. You can test this yourself by setting a breakpoint and debugging the method org.jruby.runtime.load.LoadService.init(List). There is a line

String canonNormal = new File(jrubyHome).getCanonicalPath();

In my case this path resolved to the current directory which was my Eclipse installation directory of the eclipse binary, but basically it was the wrong path. The path which was constructed there doesn’t even exist, and that’s the reason why my fastercsv gem could not be found.

We don’t need the path of the current directory – we need the the path of the current bundle which contains the jruby-complete.jar.

One solution was to set an environment variable (-Djruby.home=”/absolute/path/to/my/jruby-installation”) but I didn’t want to do that as I also had to unpack the jruby jar for that purpose, and I also don’t want to maintain another environment variable for each different server.

Later I found out that there is OSGiScriptingContainer which should take care of that and resolves jruby.home for the current bundle. But unfortunately I am running an old version of Eclipse Equinox and I got a NoClassDefFoundException as the class BundleReference was missing. An update of Eclipse Equinox in my target platform would have helped probably but I don’t want to upgrade now. So I had to find another solution.

My solution was to check the source of  OSGiScriptingContainer to find out what is actually going on there and I saw that they have a special way to determine the ‘jruby.home‘ in order to get an absolute path to the actual jruby-complete.jar which is used as a basis for all other lookups where relative paths are used (e.g. path like classpath:/…). The code which I have borrowed comes from OSGiFileLocator.java.

Long story short:
In order to let my ScriptingContainer use the correct jruby.home in my environment I have used the following code:

// SETTING JRUBY.HOME path
// Copyright notice:Code partly borrowed from org.jruby.embed.osgi.utils.OSGiFileLocator.getFileURL(URL)
// pretty hacky way abusing reflection
// might only work and is only required for Eclipse Equinox OSGI Environment
// we do it because we cannot use jruby's OSGiScriptingContainer.
// we couldn't use it because we are probably running on a old
// Equinox version which doesn't have the class BundleReference yet
// TODO upgrade Equinox to be able to use OSGiScriptingContainer to hopefully remove the code below
 
URL url = Activator.getContext().getBundle().getResource("/META-INF/jruby.home");
URLConnection conn = url.openConnection();
Method method = conn.getClass().getMethod("getFileURL");
method.setAccessible(true);
URL url2 = (URL)method.invoke(conn);
String path = url2.getPath();
if (path.endsWith("/")) path = path.substring(0, path.length() - 1); // remove trailing slash
 
System.setProperty("jruby.home", path);
 
container =  new ScriptingContainer(LocalContextScope.THREADSAFE, LocalVariableBehavior.GLOBAL);

After that change jruby got the correct path to jruby.home which was point to the real absolute path of my jruby-complete.jar so that all lookups for the gems where using the correct path.

e.g. /Users/me/development/workspace/.metadata/.plugins/org.eclipse.pde.core/synesty/org.eclipse.osgi/bundles/463/1/.cp/META-INF/jruby.home

Note, that you should be aware that this code is very specific for equinox osgi, a very hacky approach. It should only serve for educational purposes and should give you a hint in which direction to investigate if you are also having problems with jruby being unable to find your gems even though you are sure that it is there and you have already banged your head on the wall too many times ;)

Using OSGIScriptingContainer is much cleaner, but as I spent hours figuring this out here, I am sharing this with you anyway. Once I have a better solution I’ll try to update this post.

My next step will be to figure out how I could use the gems-in-a-jar approach to just add new gems to the classpath instead of repackaging the jruby-complete.jar.

Veröffentlicht unter Software-Development | Verschlagwortet mit , , , , , , , | 94 Kommentare