NoClassDefFoundError when running routine using an external library

One Star

NoClassDefFoundError when running routine using an external library

I've been experimenting with all the different ways you can call a method from an external java library from within a Talend job.  I was able to create a Routine that calls a method from the Apache Commons Lang library.  As a test, I wanted to use the StringUtils.capitalize method to capitalize Strings in a variety of use cases.  I figured a Routine would be most appropriate.

I added the Apache Commons Lang jar as a new library in the User Libraries screen.  Then, I created a new Routine called ApacheStringUtils.  Within it, I created a method called capitalize which called the StringUtils.capitalize method in the Apache Commons Lang library.  The Routine looks like this:

package routines;

import org.apache.commons.lang3.StringUtils;

public class ApacheStringUtils {


    /**
     * capitalize: returns a String with a capitalized first character
     *
     *
     * {talendTypes} String
     *
     * {Category} User Defined
     *
     * {param} string str: The string needing to be capitalized.
     */
    public static String capitalize(String str) {
        return StringUtils.capitalize(str);
    }
   
    /**
     * uncapitalize: returns a String with an uncapitalized first character
     *
     *
     * {talendTypes} String
     *
     * {Category} User Defined
     *
     * {param} string str: The string needing to be uncapitalized.
     */
    public static String uncapitalize(String str) {
        return StringUtils.uncapitalize(str);
    }
}


This Routine showed no errors.

To test, I successfully ran the Routine in a tJava component with the following code:

  System.out.println(ApacheStringUtils.capitalize("word"));



This job printed out "Word" as expected.

However, when I tried to use this same routine in a tMap expression, it failed due to the following NoClassDefFoundError exception:

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/lang3/StringUtils
    at routines.ApacheStringUtils.uncapitalize(ApacheStringUtils.java:33)
    at test_talend.manipulatecsvdatatest_0_1.ManipulateCSVDataTest.tFileInputDelimited_1Process(ManipulateCSVDataTest.java:2242)
    at test_talend.manipulatecsvdatatest_0_1.ManipulateCSVDataTest.runJobInTOS(ManipulateCSVDataTest.java:2846)
    at test_talend.manipulatecsvdatatest_0_1.ManipulateCSVDataTest.main(ManipulateCSVDataTest.java:2680)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.StringUtils
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    ... 4 more


This didn't make much sense to me, as it was running successfully in a tJava component.  Why not here?  When I pressed Ctrl+Space when filling in my expression, I was even able to see my ApacheStringUtils methods.  In fact, the methods from the imported library's StringUtils class showed up as well.  I tried using those directly, but received an error that those methods were "undefined for type StringUtils."

So, my question is: Why can I access an imported library via a routine in a tJava component but not a tMap expression?

Community Manager

Re: NoClassDefFoundError when running routine using an external library

Hello
I added the Apache Commons Lang jar as a new library in the User Libraries screen.  Then, I created a new Routine called ApacheStringUtils

I don't understand where do you put the user library, however, please follows the steps to add external libraries:
create your custom routine first, and then right click on it and select 'Edit Routine Libraries' item to add your user libraries.

Regards
Shong
----------------------------------------------------------
Talend | Data Agility for Modern Business
One Star

Re: NoClassDefFoundError when running routine using an external library

Well first, I had added the Apache Commons Lang jar as a library under Preferences -> Java -> Build Path -> User Libraries.  After I realized that didn't work, I did exactly what you had suggested.  I right-clicked on the routine I had created, clicked "Edit Routine Libraries," added the Commons Lang jar, and clicked Finish. 

I tried running the job once more and received the same NoClassDefFoundError as before. 

I'd like to point out once more that the capitalize method in the routine works fine when called from a tJava component.  When I try to use it in a tMap component, however, I get the NoClassDefFoundError exception.  Any other ideas on what might be wrong?
One Star

Re: NoClassDefFoundError when running routine using an external library

While I wouldn't call this approach a resolution, I found that I could get my Routine to run within a tMap component by including a tLibraryLoad component to load the Apache Commons library at runtime.

However, I still do not need to do this when calling my routine from a tJava component.  I don't know why this is the case - I thought defining a routine and specifying the library required by the Routine meant that I wouldn't need to load the required library in each job the Routine is called from.  It's definitely inconvenient for others who wish to use my Routine since they likely won't be aware of the required library when using it.  Their job will fail and they will need to look at the code to understand which library it needs.  This would especially be a hassle for non-developers who wish to use a custom Routine but don't know much about Java and library dependencies!

I feel as though I'm not doing something correctly, so I will leave as unresolved until I receive an answer.
Community Manager

Re: NoClassDefFoundError when running routine using an external library

Hello 
The code should work both on tMap and tJava component if the library is loaded correctly. I have downloaded the external library commons-lang3-3.1.jar from internet and tested your code, it works on tJava. I am using version 6.1.1. Which version are you using? 

Regards
Shong
----------------------------------------------------------
Talend | Data Agility for Modern Business
One Star

Re: NoClassDefFoundError when running routine using an external library

The code only works with the tJava component.  I can't get it to work in the tMap component unless I add in an additional tLibraryLoad component to load the Apache library again.

I am also using 6.1.1.  Could you share with me what steps you took to import the Apache commons library such that it will work in the tMap component?

2019 GARNER MAGIC QUADRANT FOR DATA INTEGRATION TOOL

Talend named a Leader.

Get your copy

OPEN STUDIO FOR DATA INTEGRATION

Kickstart your first data integration and ETL projects.

Download now

What’s New for Talend Summer ’19

Watch the recorded webinar!

Watch Now

Best Practices for Using Context Variables with Talend – Part 4

Pick up some tips and tricks with Context Variables

Blog

How Media Organizations Achieved Success with Data Integration

Learn how media organizations have achieved success with Data Integration

Read

Tutorial

Introduction to Talend Open Studio for Data Integration.

Watch