Tag: JSF

Guice and JSF 2

As I’m sure you can tell from previous entries, I’ve been playing with JSF 2 in Google App Engine.  I decided to give Google’s Guice a try for dependency injection instead of Spring.  Why?  This is really just a learning app and why not try something new.

So how do you get Guice to play nicely with JSF?  One solution I saw online was to write a custom ELResolver for JSF that will do the injection.  I found this resource http://snippets.dzone.com/posts/show/7171 that had a full ELResolver implementation.  I had to modify it slightly to get it to work.  Namely, I had to move the Guice Injector creation out of getValue and into a private static variable.  I suspect I could make it not static, but I never gave it a try.  Here’s the source just in case that link dies at some point.  Again, I take no credit for this code other than the small modification mentioned.

/**
 * Bulk of the code taken from
 * http://snippets.dzone.com/posts/show/7171
 **/
package jota.soc.guice;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.ServletModule;
import java.beans.FeatureDescriptor;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.el.ELContext;
import javax.el.ELResolver;

/**
 */
public class GuiceResolver extends ELResolver
{

    Injector injector = Guice.createInjector(
            new ServletModule(),
            new SocGuiceModule() );

    //We only need to see the currently processed Objects in our Thread, that
    //prevents multithread issues without synchronization
    private static ThreadLocal currentlyProcessedThreadLocal = new ThreadLocal()
    {
        @Override
        protected Object initialValue()
        {
            return new LinkedList();
        }
    };

    //Im not sure if the synchronized lists seriously slow down the whole EL
    //resolving process
    private static List<WeakReference> alreadyInjectedObjects = Collections.
            synchronizedList( new LinkedList() );

        @Override
    public Object getValue( ELContext context, Object base, Object property )
    {

        //if the list of currently processed property objects doesnt exist for this
        //thread, create it
        List<Object> currentlyProcessedPropertyObjects = (List<Object>) currentlyProcessedThreadLocal.
                get();

        //Handle only root inquiries, we wont handle property resolving
        if ( base != null )
        {
            return null;
        }

        //checking if this property is currently processed, if so ignore it -> prevent
        //endless loop
        if ( checkIfObjectIsContained( property,
                currentlyProcessedPropertyObjects ) )
        {
            return null;
        }

        //add the to-be-resolved object to the currently processed list
        currentlyProcessedPropertyObjects.add( property );

        //now we can savely invoke the getValue() Method of the composite EL
        //resolver, we wont process it again
        Object resolvedObj = context.getELResolver().getValue( context, base,
                property );

        //ok, we got our result, remove the object from the currently processed list
        removeObject( property, currentlyProcessedPropertyObjects );

        if ( resolvedObj == null )
        {
            return null;
        }

        //ok we got an object
        context.setPropertyResolved( true );

        //check if the object was already injected
        if ( !checkIfObjectIsContainedWeak( resolvedObj, alreadyInjectedObjects ) )
        {
            injector.injectMembers( resolvedObj );
            //prevent a second injection by adding it as weakreference to our list
            alreadyInjectedObjects.add( new WeakReference( resolvedObj ) );
        }

        return resolvedObj;
    }

    /**
     * This method will search for an object in a Weak List. If there are any
     * WeakReferences on the way that were removed by the garbage collection
     * we will remove them from this list
     * @param object
     * @param list
     * @return
     */
    private boolean checkIfObjectIsContainedWeak( Object object,
                                                  List<WeakReference> list )
    {
        for ( int i = 0; i < list.size(); i++ )
        {
            WeakReference curReference = list.get( i );
            Object curObject = curReference.get();
            if ( curObject == null )
            {
                //ok, there is are slight chance that could go wrong, if you
                //have to prevent a double injection by all means, you might
                //want to add a synchronized block here
                list.remove( i );
                i--;
            }
            else
            {
                if ( curObject == object )
                {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * checks if an object is contained in a collection (really the same object '==' not equals)
     * @param object
     * @param collection
     * @return
     */
    private boolean checkIfObjectIsContained( Object object,
                                              Collection collection )
    {
        for ( Object curObject : collection )
        {
            if ( object == curObject )
            {
                return true;
            }
        }
        return false;
    }

    /**
     * removes an object from a list. really removes the given instance, not an other
     * object that fits equals
     * @param object
     * @param list
     */
    private void removeObject( Object object, List list )
    {
        for ( int i = 0; i < list.size(); i++ )
        {
            if ( list.get( i ) == object )
            {
                list.remove( i );
            }
        }
    }

    @Override
    public Class<?> getType( ELContext context, Object base, Object property )
    {
        return null;
    }

    @Override
    public void setValue( ELContext context, Object base, Object property,
                          Object value )
    {
    }

    @Override
    public boolean isReadOnly( ELContext context, Object base, Object property )
    {
        return false;
    }

    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors( ELContext context,
                                                              Object base )
    {
        return null;
    }

    @Override
    public Class<?> getCommonPropertyType( ELContext context, Object base )
    {
        return null;
    }
}

That sure looks like a lot of code to me. I know Spring also provides a custom ELResolver for dependency injection. I found two links talking about resource injection here and here and thought I would try writing my own resource injector. Here’s how it ended up:

package jota.soc.guice;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.ServletModule;

import com.sun.faces.spi.InjectionProvider;
import com.sun.faces.spi.InjectionProviderException;
import com.sun.faces.vendor.WebContainerInjectionProvider;

/**
 * JSF injection provider for Guice.
 * @author Joel Weight
 */
public class GuiceInjectionProvider implements InjectionProvider
{
    public GuiceInjectionProvider()
    {
        System.out.println( "creating guice injection provider" );
    }

    /**
     * default injector provided by the web container.
     */
    private static final WebContainerInjectionProvider con =
                                                       new WebContainerInjectionProvider();

    /**
     * Custom guice injector that will load our modules.
     */
    private static final Injector injector = Guice.createInjector(
            new ServletModule(),
            new SocGuiceModule() );

    @Override
    public void inject( Object managedBean ) throws InjectionProviderException
    {
        // allow the default injector to inject the bean.
        con.inject( managedBean );
        // then inject with the google injector.
        injector.injectMembers( managedBean );
    }

    @Override
    public void invokePostConstruct( Object managedBean )
            throws InjectionProviderException
    {
        // don't do anything here for guice, just let the default do its thing
        con.invokePostConstruct( managedBean );
    }

    @Override
    public void invokePreDestroy( Object managedBean ) throws
            InjectionProviderException
    {
        con.invokePreDestroy( managedBean );
    }
}

It’s a very simple implementation. It delegates everything to the default container InjectionProvider, and then adds a touch of Guice. I then added this to web.xml:

    <context-param>
        <param-name>com.sun.faces.injectionProvider</param-name>
        <param-value>jota.soc.guice.GuiceInjectionProvider</param-value>
    </context-param>

And all my guice injections are occurring as expected. I am no longer using the custom ELResolver implementation. Obviously it’s not a general solution since I am instantiating my guice modules right in the provider rather than configuring it somehow, but it was enough for me right now.

If I run into any problems with this solution I’ll let you know, otherwise I would love to hear from any gurus as to how this could be problematic.

Advertisements

JSF 2 Ajax Submission Exceptions

Tonight I was messing around with my pet project.  I have a composite component that I am trying to use in three different places in a page.  I am passing an action into the component, and that action is called via ajax on a command button.  In the first instance on the page, that action was working just fine.  In the second and third it was failing with a strange error message in a javascript popup.

serverError: class javax.faces.el.EvaluationException
blah blah blah with my source file followed by
Property 'myAction' not found on type MyType

MyType is the bean, and myAction is the action method I was attempting to call from the composite component.  I checked, and double checked and triple checked that everything was spelled correctly and everything.  The autocomplete in Netbeans was even showing that the action method was there.  I was especially surprised that it would work in one instance on the page, but not in the others, especially since the usage was identical other than the action method.

I have a co-worker who’s favorite saying is “Read the Error, Fix the Error” and in 99% of the cases, it works great, but not in this case.

The problem ended up being that some code in my action was causing a runtime exception that I wasn’t catching.  Once I resolved that, the error went away.  The reason it took me so long to figure that out is because neither the server log, nor the javascript popup gave me any clue as to the real cause of the error.

So, if you are seeing this strange error and KNOW that your action is defined correctly, set a break point in the action, or add some trace level logging so you can figure out what’s going on much faster than I did.

JSF 2, JSR-303, and you

In JSF 2, they added some nifty stuff.  One of the items they added was that JSF will seamlessly validate input values using a JSR-303 implementation if one is available.  “That’s nifty!” you say, “But how do I get an implementation of JSR-303 into my webapp?”

The reference implementation of JSR-303 is in hibernate validator version 4.  What if you don’t want to use hibernate?  That’s fine, because the validator project stands alone.  You don’t need to use any other parts of hibernate if you don’t want to.  Here’s what you should put in your maven pom in order to get hibernate 4 validators.

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>4.0.2.GA</version>
        </dependency>

Now you should be able to add JSR-303 validators to your project. Something like this should do:

    @NotNull
    @Size(min=3, max=10)
    private String username;

Now, when you tie an input control to that field in the bean, the value will be validated with the NotNull and Size constraints.

Obviously, the next thought is, “Awesome, now I can easily do multi-field constraints.”  For instance, you have a New Password form. You want the user to enter the password, and then to confirm the password. The simple answer is to write a validator that checks that the values of the two fields are equal and place the constraint on the Type, not on the Field. Something like the FieldMatch validator in the answer to this question on stackoverflow would be perfect.

So you implement the FieldMatch validator, and place it on your type. You then deploy your fancy JSF application and test the validator. Strangely your validator is never run. After reading more closely, you discover that JSF does “Field Level” validation using JSR-303 validators. Well, that should be easily enough overcome, right?

Next, you add an f:event tag to the page containing the bean to be validated. The event is preValidate, and you call a method on your bean containing the values. The method you call runs the validator manually. Perfect, right? More testing reveals that the your validator returns true every time, and even more than that, the values it’s testing are both null (which is why they are also equal).

Alright, let’s figure out what’s really going on.

After a half day of reading and testing, you finally go back to JSF basics and remember the JSF lifecycle. Alright, the phases are:

  1. Restore View
  2. Apply Request Values
  3. Process Validations
  4. Update Model Values
  5. Invoke Application
  6. Render Response

Notice that process validations comes before update model values. Well, no wonder putting the type constraint on the bean wasn’t validating the values that were set in the input controls. The values from the controls are validated before they are set on the bean. That explains why JSF 2 only does field level validation. They can take those validators, and apply them to the values that are set on the input controls before they are set in the bean.

So, what’s the answer to this problem? I haven’t decided yet. In this particular case, there are already JSF validators available for just this kind of validation, but what about the general case of type level validators. If I figure anything out, I’ll let you know. If you have any great ideas, chime in on the comments.

Spring Roo and JSF 2?

In my latest project we’ve been playing with Spring Roo.  You can read all about the advantages of it on that site, but the gist is that it creates your entities and JPA persistence code for them.  It will also configure your database connection, generate unit tests, generate a JSP/JSTL based UI with all of the Spring MVC controllers, and selenium tests for it.  It will also optionally integrate Spring Web Flow into the mix.  It’s basically a web based CRUD application in a box.

I spent the last 3 days trying to figure out how to use JSF 2 in Glassfish 3 with a Roo generated application.  Much of this pain may have been caused by my lack of experience with JPA and Spring, but here are a few of the difficulties I faced.

  • JSF 2 is not well supported in eclipse yet, whereas Netbeans 6.8 has excellent JSF 2 support.
  • All of the entities created by Roo just have properties.  All of the getters, setters, and other methods are all included in aspect files and compiled into the entity through AspectJ.  The AspectJ plugin for eclipse takes care of this if you have weaving enabled.  Guess what… There’s no aspect based plugin available for Netbeans, so you get a ton of compile problems in your Roo generated project if you try to open it within Netbeans.
  • The Roo projects are all maven based.  I have yet to see really good success with any maven integration within Eclipse.  Netbeans maven integration is spot on.  For the past 2.5 years I’ve been developing within Eclipse and simply doing all of the maven stuff from the command line because it’s much simpler than dealing with Maven in Eclipse.
  • JSF can’t be used to call Spring MVC controllers.  The Spring MVC controllers generated by Roo are nice.  They are simple and clean, and having them managed by Roo is just that much better.  Unfortunately, Spring MVC is stateless, whereas JSF is stateful, so calling any Spring MVC controller from JSF without using Spring’s Web Flow just doesn’t work.
  • JSF 2 can’t be used in the current version of Spring Web Flow.  Even though a sub component of Web Flow is Spring Faces, the current version of Spring Faces only supports JSF 1.x.  As soon as I put Spring Faces in the classpath, my JSF 2 app wouldn’t render any .xhtml files for me.  Removing Spring Faces from the classpath allowed me to render .xhtml with JSF 2.
  • The transaction type specified in the Roo generated persistence unit is RESOURCE_LOCAL.  Glassfish 3 told me I had to use JTA.  I believe Roo can generate a jndi based data source, which I haven’t tried yet.  I ended up manually modifying the persistence unit (after a lot of time on Google) to be jndi based and have the JTA transaction type.  Unfortunately, this means that all of the Roo generated unit tests fail.  I suspect if I had Roo generate the jndi data source then the unit tests would also pass, so hopefully this issue was self inflicted.  Update: I created a simple project with a jndi datasource.  Unfortunately, all of the unit tests fail.   There is some information in the (closed as won’t fix) bug at https://jira.springsource.org/browse/ROO-311 It would be nice if the unit tests that are created for Roo would use the Spring SimpleNamingContext (and I may be just making stuff up now because I don’t know a lot about Spring/JNDI stuff) or something to allow the unit tests to run if the persistence setup uses jndi.

So why would I continue in my endeavor to use JSF 2 with my Roo project, and where do I go from here?

We are trying to make development as easy as possible for the designers.  They have next to no Java experience at best and just want to tweak styles, layout, and images.  I believe JSF can do a great job of giving us that separation.  I can get the controls working in the form, and they can move them around and do whatever they want with them.  Also, with the composite components in JSF 2, we should be able to make some very nice reusable components for the designers to use in our application.  Finally, many of those components we should be able to share with other projects in the company.

So what is the solution to all of the above problems?

  • Education.  Obviously learning more about JPA and Spring will make the whole journey a little easier for me.
  • Separate projects.  If we have all of the domain model in one maven project, and have the web based UI in another (which is how it really ought to be anyway) then the java developers can use Eclipse or STS for their IDE and get the AspectJ support, and the web designers can use Netbeans 6.8 for the JSF 2 project and get the great support there.  Because the domain model will simply be a jar dependency for the web project all of the aspect based methods will be compiled into the class, meaning we won’t need any Aspect support within Netbeans.

What would it take to get a JSF 2 UI generator into Roo?

  • The Roo team has released a nearly 100 page document about Roo.  Unfortunately, all of the parts about how to implement a new addon for Roo are marked as TBC.  There’s precious little detail on the web too.  Unfortunately, I’m not quite ready to dive into the source code to try to figure it out.  So, a simple Roo addon isn’t quite there.  Update: The only reference I found regarding writing your own Roo addon is at http://www.slideshare.net/desmax74/spring-roo-internals-javaday-iv and the slides leave a lot to the imagination of the reader.
  • Rumors in the Spring forums are that Spring Faces is going to be a new top level project, freed from the grasp of Spring Web Flow, and that the next version (due in June 2010?) will support JSF 2.  I was quite excited to use Web Flow with JSF 2 for the bean management relating to flows.  For instance, a new whatsit wizard that spans multiple pages and manages the scope of the new whatsit bean.
  • Because JSF doesn’t work so well with Spring MVC without Web Flow, and Web Flow won’t support JSF 2 until Spring Faces is split out and both Web Flow and Spring Faces release their next version I believe it’ll be months at best before we see any JSF 2 capabilities within Roo.  The only way it could happen sooner is if someone dives into the Roo source code and creates a third-party addon that will generate JSF 2 controllers and xhtml pages.

So, there it is.  At least 3 days of pain, trial and error, and research all wrapped up in one blog post.

JSF 2 Custom Converter

I ran into a very strange problem in my little Google App Engine application. I’m using JSF 2 as the presentation layer. I’m using Google’s low level api for accessing the data store and have written some little utilities to convert from my objects to Google’s Entity objects.

I thought I had the mapping working OK, but had only tried setting simple String properties on my data objects. I finally tried to map a simple association, where one object would have a list of keys of other objects. In my case I have Tables. Each table can be associated with zero or more Genres. So, my Table data object has a List of Keys for the genres that it is a part of, like this:

public class Table
        extends DataObjectAbstract
        implements Serializable
{
    @Persistent
    private List<Key> genres;

    // other private attributes here

    public List<Key> getGenres()
    {
        return genres;
    }

    public void setGenres( List<Key> genres )
    {
        this.genres = genres;
    }

    // other public getters and setters and other methods here
}

You can safely ignore DataObjectAbstract and the @Persistent annotation. They aren’t part of this blog post.

The strange behavior I was seeing was that when I would get the genre list from the data store, it would be a List<String> instead of the List<Key> I was expecting. After some debugging, I finally discovered that I was setting it as a List<String> because Strings are what I was getting from my JSF page, and thanks to Java’s type erasure with generics, my code didn’t know any better. So, the solution is a custom converter, which will allow me to pass the Keys to and from the browser.

Fortunately, in JSF 2, custom converters are even easier than they are in JSF 1.x. Here’s my simple converter class for the com.google.appengine.api.datastore.Key class. Making this even more simple is the fact that Google offers a KeyFactory that takes care of giving a URL safe representation of their Key object.

package jota.soc.ui.converter;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

/**
 * Converter for Google Key.
 * @author Joel.Weight
 */
@FacesConverter( value="keyConverter" )
public class KeyConverter implements Converter {

    /**
     * converts the String representation of the key back to the Object
     */
    public Object getAsObject( FacesContext context, UIComponent component,
                               String value )
    {
        // will throw new IllegalArgumentException if it can't parse.
        return KeyFactory.stringToKey( value );
    }

    /**
     * converts the Key object into its String representation.
     */
    public String getAsString( FacesContext context, UIComponent component,
                               Object value )
    {
        if ( value instanceof Key )
        {
            return KeyFactory.keyToString( (Key)value );
        }
        else
        {
            throw new IllegalArgumentException( "Cannot convert non-key object in KeyConverter" );
        }
    }
}

With the @FacesConverter annotation, you don’t have to register the converter in faces-config or anything. You can simply reference it using the id.

Here’s the code that uses the converter.

<h:selectManyListbox id="genresBox" value="#{createTable.table.genres}">
    <f:converter converterId="keyConverter" />
    <f:selectItems value="#{createTable.genres}"
        var="item"
        itemValue="#{item.key}"
        itemLabel="#{item.name}" />
</h:selectManyListbox>

And just like that, I’m storing Keys in the data store instead of Strings.

JSF 2.0.2 and Google App Engine

I spent a couple hours today trying to get a JSF 2 based webapp up in app-engine.  Of course, it wasn’t as easy as simply following the instructions in

configuring jsf 2.0 to run on the google app engine using netbeans

I ran into the problem detailed here  ( JNDI problem in jsf 2.0 and google app engine ) right off the bat.  I tried to use his packaged jar, but app-engine kept giving me an exception that it couldn’t load the zip file. Perhaps it was corrupted when I downloaded it or something.

If you follow his link to the WebConfiguration.java file, you will see that he created a new WebConfiguration.java file within the same package as the existing one and copied the implementation, commenting out the offending code.  I decided to try a different approach. I’m sure there is nothing wrong with Josh’s approach, and if it weren’t for his blog and all the ground work he had already done in finding the offending code, I probably wouldn’t have even bothered going further with JSF 2 in app engine. In any case, here’s what I did.

I downloaded the JSF 2.0.2 source from here ( jsf 2.0.2 source ) and expanded it.  I then found the WebConfiguration.java file and decided to modify it.  Rather than comment out all the source, I added a new config enumeration value to the BooleanWebContextInitParameter enumerations as follows

// around line 1071 in the finished file, after the previously final enum value AllowTextChildren
EnableJNDI(
"com.sun.faces.enableJNDI",
true
);

Then, I check for the value of that parameter in private constructor, around line 113

        // add the isOptionEnabled call to this conditional statement
        if ( isOptionEnabled( BooleanWebContextInitParameter.EnableJNDI ) && canProcessJndiEntries()) {
            processJndiEntries(contextName);
        }

When that is done, simply compile, and you have a new jsf-api/build/lib/jsf-api.jar and jsf-ri/build/lib/jsf-impl.jar that you can use in your project. For my compile, I simply copied the build.properties.glassfish.orig to build.properties and set the jsf.build.home property to the directory in which the source was checked out.

Finally, put a new context-param in web.xml with the name com.sun.faces.enableJNDI and a value of false.

All I’ve got so far is the hello world, so we’ll see if I run into other difficulties as I use further functionality.

Sharing JSF 2 Composite Components

Let’s say you have created some awesome composite components in JSF 2 that you want to share between one webapp and another.  One option would be to simply copy all of the .xhtml files from one webapp to the other.  Yuck.  Then you have two versions of each file.  Changes in one are not reflected in the other, and what happens when you are up to 6 webapps that all want to share your awesome components?  That’s ugly.

A better solution is to jar them all up into a new component library.  Sounds complicated huh?  Not really.

Each of your composite components should be in a resources directory.  In order to redistribute those components, you simply add them to a jar under META-INF/resources, and then make sure that your META-INF directory also contains a faces-config.xml file.

The faces-config.xml file ensures that JSF scans the jars for components and annotated classes.  The following ant target, which I added to the build.xml file in a netbeans project automatically takes the resources directory, and creates an empty faces-config.xml file and plops them into a jar.  Now, you simply include that jar as a library in your new webapp.

    <target name="create-lib-jar">
        <property name="stage-dir" value="create-lib-jar-temp" />
        <property name="dist.dir" value="." />
        <property name="lib.jar" value="${dist.dir}/jsfLibrary.jar" />
        <mkdir dir="${stage-dir}" />

        <echo file="${stage-dir}/faces-config.xml" ><![CDATA[<?xml version='1.0' encoding='UTF-8'?>
<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
    version="2.0">
</faces-config>]]></echo>

        <jar destfile="${lib.jar}">
            <metainf dir="${stage-dir}">
                <include name="faces-config.xml" />
            </metainf>

            <metainf dir="web" includes="resources/">
            </metainf>

        </jar>

        <delete dir="create-lib-jar-temp" />

    </target>

Think of the possibilities.  Now, you have a simple project in which to build your core, re-usable composite components, all running in their own little webapp, testable with junit for the java classes and selenium for the web portions.  Then, you package them up for use in all your webapps.  That’s awesome sauce.

This build target doesn’t get any java class files.  When I get to that part of my project, I’ll update it.

Make sure to modify the properties to fit your environment so it is pulling from the correct directories and all that jazz.

<target name=”create-lib-jar”>
<property name=”stage-dir” value=”create-lib-jar-temp” />
<property name=”dist.dir” value=”.” />
<mkdir dir=”${stage-dir}” /> 

<echo file=”${stage-dir}/faces-config.xml” ><![CDATA[<?xml version=’1.0′ encoding=’UTF-8′?>
<faces-config
xmlns=”http://java.sun.com/xml/ns/javaee&#8221;
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221;
xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd&#8221;
version=”2.0″>
</faces-config>]]></echo>

<jar destfile=”${dist.dir}/jsfLibrary.jar”>
<metainf dir=”${stage-dir}”>
<include name=”faces-config.xml” />
</metainf>

<metainf dir=”web” includes=”resources/”>
</metainf>

</jar>

<delete dir=”create-lib-jar-temp” />

</target>