1 2 Previous Next 24 Replies Latest reply: Jun 13, 2010 6:02 PM by Dan Allen RSS

ShrinkWrap - Descriptors

Aslak Knutsen Master

I played around with how we could impl descriptors into the archives last night..

Basicly;
- a WebArchiveDescriptor IF with methods like addFilter, addServlet etc.
- a JAXB based WebArchiveDescriptorAsset that handles the web.xml struct.
- the WebContainer extends WebArchiveDescriptor
- the WebArchiveDescriptorAsset is added to path WEB-INF/web.xml in the constructor of WebContainerBase.
- WebContainerBase impls WebArchiveDescriptor as a 'double' delegate, delegate the servlet/filter/listener classes to the Archive and the configuration to the WebArchiveDescriptorAsset.

Have look at the impl, http://shrinkwrap.pastebin.com/m37e1ccc6.

A example usage testcase, http://shrinkwrap.pastebin.com/m19df310e.

And the web.xml output: http://shrinkwrap.pastebin.com/m55b0fec3.


What you think?

(ps: and yes, I do realize that this is probably not a priority for v. 0.1 :o)

  • 1. Re: ShrinkWrap - Descriptors
    Emmanuel Bernard Master

    Looks nice. A few remarks (in separate messages)

    addFilter(..., Map....) looks quite non fluent.

    why not have

    descriptor.addFilter(SecureFilter.class, "/secure/*")
     .addParameter("foo", "bar")
     .addParameter("foo", "bar")
     .addFilter(MyFilter.class, "/free/*")



  • 2. Re: ShrinkWrap - Descriptors
    Andrew Rubinger Master

    Just a note that I'll reply here in a bit. :)

  • 3. Re: ShrinkWrap - Descriptors
    Emmanuel Bernard Master

    Another remark is that you can't compose multiple time
    (also I dont' understand why you have so many classes liek descriptor container, asserts etc)

    Could not you have something like

    Archive archive = ...;
    WebArchive webArchive =
    archive.specializes(WebArchive.class)
     .setDisplayName("test-app")
     .addContextParameter("test", "yes")
     .addServlet(this.getClass(), "*.jsp")
     .addClass(IOUtil.class)
     .addListener(ZipOutputStream.class)
     .addFilter(ZipExporter.class, "*")
     .addLibrary("org/jboss/shrinkwrap/impl/base/importer/test.zip");
     .specializes(PersistenceArchive.class)
     .setPersistenceProvider(HibernatePersistence.class);


    A PersistenceArchive received the archive as a param and can build upon the basic APIs like addInputStreamAsFile etc)




  • 4. Re: ShrinkWrap - Descriptors
    Aslak Knutsen Master

     

    "epbernard" wrote:
    Another remark is that you can't compose multiple time
    (also I dont' understand why you have so many classes liek descriptor container, asserts etc)


    You can and will be able to compose multiple times, it just not implemented at the moment..

    The reason for all the different Containers is that the different Archive extensions like WebArchive and EnterpriseArchive all consist of types of Containers but not necessarily the same. ie, a WebArchive is a MetadataContainer + a ResourceContainer + a ClassContainer, while a EnterpriseArchive is a MetadataContainer + a ResourceContainer but does not support the ClassContainer..

    A Asset is more or less just a internal abstraction over how to get/open the Stream of data to be stored at a specific path.

    The Descriptor name just came in now, because i made it on the 'outside', this could in theory be a part of the WebContainer interface.

    They all 'share' the same base Archive impl as a backing, so there is no problem having code like this:

    WebArchive war = new WebArchiveImpl(new MemoryMapArchiveImpl());
    EnterpriseArchive ear = new EnterpriseArchiveImpl(war.getInternalArchive());
    

    or
    EnterpriseArchive ear = new EnterpriseArchiveImpl(new MemoryMapArchiveImpl());
    WebArchive war = ear.getWebModule("my.war");
    



    This should of course be hidden away in some 'specializes' like api, but the point is the same.

    The 'external' user api is still not 100%, that's why you see new WebArchiveImpl(new MemoryMapArchiveImpl()) etc.. :)

  • 5. Re: ShrinkWrap - Descriptors
    Aslak Knutsen Master

     

    "epbernard" wrote:
    Looks nice. A few remarks (in separate messages)

    addFilter(..., Map....) looks quite non fluent.

    why not have

    descriptor.addFilter(SecureFilter.class, "/secure/*")
     .addParameter("foo", "bar")
     .addParameter("foo", "bar")
     .addFilter(MyFilter.class, "/free/*")



    Hmm.. yea, good point. I'm gonna see if i manage to fit that in there somehow..

  • 6. Re: ShrinkWrap - Descriptors
    Emmanuel Bernard Master

     

    "aslak" wrote:

    They all 'share' the same base Archive impl as a backing, so there is no problem having code like this:

    WebArchive war = new WebArchiveImpl(new MemoryMapArchiveImpl());
    EnterpriseArchive ear = new EnterpriseArchiveImpl(war.getInternalArchive());
    



    well this code is fugly, that's a huge problem :)


    EnterpriseArchive ear = new EnterpriseArchiveImpl(new MemoryMapArchiveImpl());
    WebArchive war = ear.getWebModule("my.war");
    


    That's a different scenario here, you are creating a new archive so a dedicated method makes sense.


    This should of course be hidden away in some 'specializes' like api, but the point is the same.

    Still it seems to me that delegation is less heavy on the specializer implementation and subclassing the core archive impl does not give you much.

  • 7. Re: ShrinkWrap - Descriptors
    Aslak Knutsen Master

     

    "epbernard" wrote:

    "aslak" wrote:

    They all 'share' the same base Archive impl as a backing, so there is no problem having code like this:

    WebArchive war = new WebArchiveImpl(new MemoryMapArchiveImpl());
    EnterpriseArchive ear = new EnterpriseArchiveImpl(war.getInternalArchive());
    



    well this code is fugly, that's a huge problem :)


    Hehe, yes, that is fugly, but it was just to illustrate that the WebArchives backing can easily be moved to run as the EnterpriseArchives backing.

    "epbernard" wrote:

    "aslak" wrote:

    EnterpriseArchive ear = new EnterpriseArchiveImpl(new MemoryMapArchiveImpl());
    WebArchive war = ear.getWebModule("my.war");
    


    That's a different scenario here, you are creating a new archive so a dedicated method makes sense.

    True, but the getWebModule() impl would use the specializer api internally was the point.

    "epbernard" wrote:

    Still it seems to me that delegation is less heavy on the specializer implementation and subclassing the core archive impl does not give you much.

    I'm not sure I understand. The way it is today is as following:
    interface WebArchive extends ResourceContainer, ClassContainer....
    WebArchiveImpl extends ContainerBase implements WebArchive -> has a Archive

    WebArchiveImpl is a pure delegator to the Archive, but with some fixed prefix Paths etc to follow the War spec, ie ClassContainer mappes to /WEB-INF/classes/.

    There is no subclassing of the backing Archive(MemoryMapArchiveImpl) in WebArchiveImpl.

    The different archive extensions(WarArchive, EnterpriseArchive etc) extend a common ContainerBase class, but that is only as a convenient way of implementing the diff containers.

    I see no problem with adding a spercializes method to the Archive interface and having a common impl in the ContainerBase(or even ArchiveBase) to load the spercialized impl. Something like.

    public class ContainerBase {
    ...
     T specializes(Class<T extends Archive<T>> clazz) {
     return specializes(clazz, new BasicPath("/"))
     }
    
     T specializes(Class<T extends Archive<T>> clazz, Path basePath) {
     pesudo:
     lookup delegation implementation of clazz, ie WebArchiveImpl
     return new WebArchiveImpl(basePath, this.backingArchive);
     }
    ...
    }
    

    This will open up for:
    EnterpriseArchive ear = ...
    WarArchive war = ear.specializes(WebArchive.class, "my.war");
    
    war = ear.getWebModule("my.war") {
     return this.specializes(WebArchive.class, "my.war");
    };
    
    JavaArchive jar = ...
    WebArchive war = jar.specializes(WebArchive.class);
    
    


    The 'Descriptor' part is a bit different since it operates on the content of a Asset, but if we turn them around to be pretty much the same as the archive extensions ie:
    WebArchiveDescriptor has a Archive.
    and WebArchive no long impls WebArchiveDescriptor

    we could do
    WebArchive war =
    WebArchiveDescriptor desc = war.specializes(WebArchiveDescriptor.class);
    desc.addServlet()...
    


    Which is what you're referring to I guess..

  • 8. Re: ShrinkWrap - Descriptors
    Emmanuel Bernard Master

    OK cool, sorry I did not know. So ContainerBase is really a facility class and youa re already doping what I was describing.

    BTW I dont' think specializes(Class<T extends Archive> clazz, Path basePath) should be here, what does it do to a real user, what's the use case?

    WarArchive war = ear.specializes(WebArchive.class, "my.war");
    should raise an exception IMO as we do not specialize, we actually add a new archive inside an archive. An EAR contains a WAR, it is not a WAR.

    So if I sumamrize, a descriptor is a super interface of archive that describes a given configuration file like web.xml. Why separate it from the WebArchive interface exactly?

    Also remember that some configuration files can be at disfferent places and be multiple. For example in JPA, orm.xmls can be named as it pleases you and you can add many of them.

  • 9. Re: ShrinkWrap - Descriptors
    Aslak Knutsen Master

     

    "epbernard" wrote:

    BTW I dont' think specializes(Class<T extends Archive<T>> clazz, Path basePath) should be here, what does it do to a real user, what's the use case?

    WarArchive war = ear.specializes(WebArchive.class, "my.war");
    should raise an exception IMO as we do not specialize, we actually add a new archive inside an archive. An EAR contains a WAR, it is not a WAR.

    We mostly create/add, but we have the Importer API as well, meaning you can import an existing war/jar/ear etc. In this case the API might not know what type of file is actually in there(here we could do some file extensions checks etc, but that's not always a fit. especially when it comes to descriptors).. So you want the Resource at Path X in the imported Archive 'specialized' as a WebArchive so that you can manipulate the classpath or web.xml.


    "epbernard" wrote:

    So if I sumamrize, a descriptor is a super interface of archive that describes a given configuration file like web.xml. Why separate it from the WebArchive interface exactly?

    Also remember that some configuration files can be at disfferent places and be multiple. For example in JPA, orm.xmls can be named as it pleases you and you can add many of them.

    I Agree, I think most of the Descriptors(they are suppose to describe the content after all) are so tightly coupled with the Archive itself, that WebArchive implements WebArchiveDescriptor makes sense.

    The reason for having it as a separate Interface is to be able to have a Asset(file) implement the same, so that we can support adding descriptor files to different paths. ie:
    WebArchive war = ..
    war.addWebResource("my-web.xml", new WebArchiveDescriptorAsset());
    

    Having the specializes(class, path) also helps out in your orm.xml example i guess..
    JavaArchive jar = ..
    PersistenceDescriptor desc = jar.specializes(PersistenceDescriptor.class, "my-orm.xml-path")
    


    The name specializes might not be the correct wording in these cases. maybe something like "get as", "convert to" or "re map".

    Another interesting 'issue' is how should we handle for instance the jboss-web.xml. In some cases it is all standalone, but in other it uses the same names as in web.xml.. descriptor pipeline ?




  • 10. Re: ShrinkWrap - Descriptors
    Emmanuel Bernard Master

     

    "aslak" wrote:
    "epbernard" wrote:

    BTW I dont' think specializes(Class<T extends Archive<T>> clazz, Path basePath) should be here, what does it do to a real user, what's the use case?

    WarArchive war = ear.specializes(WebArchive.class, "my.war");
    should raise an exception IMO as we do not specialize, we actually add a new archive inside an archive. An EAR contains a WAR, it is not a WAR.

    We mostly create/add, but we have the Importer API as well, meaning you can import an existing war/jar/ear etc. In this case the API might not know what type of file is actually in there(here we could do some file extensions checks etc, but that's not always a fit. especially when it comes to descriptors).. So you want the Resource at Path X in the imported Archive 'specialized' as a WebArchive so that you can manipulate the classpath or web.xml.


    I imagine that
    ear.getModule("my.war").specializes(WebArchive.class) is a bit clearer, marginally more verbose and more consistent. WDYT?


    "epbernard" wrote:

    So if I sumamrize, a descriptor is a super interface of archive that describes a given configuration file like web.xml. Why separate it from the WebArchive interface exactly?

    Also remember that some configuration files can be at disfferent places and be multiple. For example in JPA, orm.xmls can be named as it pleases you and you can add many of them.

    I Agree, I think most of the Descriptors(they are suppose to describe the content after all) are so tightly coupled with the Archive itself, that WebArchive implements WebArchiveDescriptor makes sense.

    The reason for having it as a separate Interface is to be able to have a Asset(file) implement the same, so that we can support adding descriptor files to different paths. ie:
    WebArchive war = ..
    war.addWebResource("my-web.xml", new WebArchiveDescriptorAsset());
    



    Well war.addWebResource returns WebArchive and that forces the user to break the fluent API to build the asset.

    Trying idea. What if we merge somehow XArchive and ZAsset from the user PoV. The only difference would be the method that provides it. For example.
    archive.specializes(WebArchive.class)
     .displayName("Example")
     .addWebResource( "my-web.xml", WebArchive.class)
     .displayName("My Example")
     .addWebResource("my-web2.xml", WebArchive.class)
     // ...
     .addClass(User.class)
     .specializes(PersistenceArchive.class)
     .persistenceProvider(HibernatePersistence.class);
    
    //BTW displayName is better than setDisplayName for fluent APIs and tells
    //BTW2 addWebResource, couldn't it be addResource with a specialized logic when it's a web archive?
    the user that it's not a Java setter.
    

    Better names still have to be found but the API flows more easily and the diff between an "asset" / file and an archive / zip is addXResource vs specializes.
    Better names could be

    specializes(WebDescriptor.class) => asArchive(WebDescriptor.class)
    addWebResource => add(Web)Resource


    Having the specializes(class, path) also helps out in your orm.xml example i guess..
    JavaArchive jar = ..
    PersistenceDescriptor desc = jar.specializes(PersistenceDescriptor.class, "my-orm.xml-path")
    


    But that's a resource in this case.


    The name specializes might not be the correct wording in these cases. maybe something like "get as", "convert to" or "re map".

    Exactly see above.


    Another interesting 'issue' is how should we handle for instance the jboss-web.xml. In some cases it is all standalone, but in other it uses the same names as in web.xml.. descriptor pipeline ?

    I don't quite understand this one but I am not familiar with jboss-web. WDYM by pipeline? If jboss-web extends the structure of web.xml, you can subclass WebDescriptor.class to JBossWebDescriptor.class

  • 11. Re: ShrinkWrap - Descriptors
    Aslak Knutsen Master

     

    "epbernard" wrote:

    I don't quite understand this one but I am not familiar with jboss-web. WDYM by pipeline? If jboss-web extends the structure of web.xml, you can subclass WebDescriptor.class to JBossWebDescriptor.class


    Have a look at https://jira.jboss.org/jira/browse/SHRINKWRAP-47

  • 12. Re: ShrinkWrap - Descriptors
    Andrew Rubinger Master

    OK, after the recent Jewish holiday I've had some time to reflect on this stuff. :)

    Generally speaking, domain-specific extensions were always part of the plan so I'm pretty psyched that Aslak's already started to dig in here. I've had some concerns about their implementation however (Aslak's addressed many so far).

    1) Keep DSL stuff decoupled from the Archive stuff

    I view these as two completely separate concerns. DSL is an API for generating descriptors. Basically an object model from which we can parse into (from a template web.xml from example) or serialize out from (marshalling). As such I'd like to be sure we keep this all cleanly away from the rest of the Archive stuff.

    Aslak's already done a pretty good job here by introducing the notion of a "Descriptor". One sticky point is that the descriptor is put into place by default to be this object model thing; if the user calls "setWebXml" then we lose the facility of "addFilter" etc.

    I'd instead like to see the addition of a "DescriptorAsset" which may be backed by any descriptor. Then we may have an overloaded WebArchive.setWebXml(WebXmlDescriptorAsset) method. In this setup things like "addServlet" are removed from the Container/Archive classes and kept only in the descriptor.

    2) Don't bring in any external dependencies

    Currently not an issue in the prototypes on stage. JDK6 JAXB is used, so ... awesome. ShrinkWrap still requires nothing except the JDK.

    3) Reinventing the wheel

    A sticky point is that we've already done this before, and it's called jboss-metadata. http://anonsvn.jboss.org/repos/jbossas/projects/metadata/trunk/ The problem here is that jboss-metadata:

    * Has too many dependencies (jboss-logging, JBossXB, etc)
    * Performs actual business logic (more than an object model should)

    Just something to be aware of. We can also copy/steal a bunch of code from this project to save us time, and retrofit it as needed.

    4) Don't leak this into the user API until it's done

    This is more a timing issue. We're gearing up for a release of ShrinkWrap to the community very soon, and the DSL stuff isn't a required feature. So We have to be careful to keep it all hidden until it's complete/working from the user API.

    This can be accomplished by keeping API stuff under the "spi" project, and doing a bulk move later.

    Also I need to read/understand more about the latest comments in this Thread. My post here is really just taking Aslak's initial post and code samples into account. For instance I'm *still* not seeing the need/purpose behind "specialize". ;)

    S,
    ALR

  • 13. Re: ShrinkWrap - Descriptors
    Emmanuel Bernard Master

    @ALR
    Well yes, if you think the descriptor DSL should be entirely decoupled from the archive DSL, then you mostly don't need specializes (or it's new name).

    That being said, I think that not providing a unified experience for archive building + configuration building is a bad idea as it would make the "configuration" code look like unreadable code rather than readable configuration.

  • 14. Re: ShrinkWrap - Descriptors
    Andrew Rubinger Master

     

    "epbernard" wrote:
    That being said, I think that not providing a unified experience for archive building + configuration building is a bad idea as it would make the "configuration" code look like unreadable code rather than readable configuration.


    Yep, let's mull over this a bit. I rank "intuition" highly as a priority for usability.

    But take the case of web.xml>"name" element. We can't make a "setName" on WebArchive as this would conflict with the name of the archive. So it'd have to be setWebXmlName or something. This doesn't match up with "addServlet" name forms. So I keep coming back to separating out the descriptor.

    S,
    ALR

1 2 Previous Next