Skip to Content

Important

Use the technique described in this post at your own risk. It’s just an experiment. Pease, test it first on a small WDJ project. It recreates .wdmodel and .wdmodelclass files of your project so that if not careful you may loose your models.

Requirements:

  • NWDS 7.0 WDJ development
  • Ant
  • XDoclet (custom temlpating)

Java Bean Model in WDJ

When working with Web Dynpro Java projects on NW 7.0, one realizes quickly the power of using model driven development. One can create (import) a model bind it through the component controller to a visual controller using NWDS wizards, which create all the necessary node and attributes structure for us in one drag-and-drop move. The attributes can then be bound to the properties of WD UI components of a view, and all that is left is to bind the appropriate component context node with an instantiated model instance.

One can create (import) three types of models in WDJ: Adaptive RFC, Adaptive Web Services, and Java Bean Model. I prefer to work with Java Bean model since it is nothing else than a collection of POJOs which could easily be modified to depending to the needs of an application. Consider a simple domain model which consists of a Person bean which has a one-to-one relation to a Job bean and a one-to-many relation to an Address bean (contacts). Creating the necessary Java beans with getters and setters for the attributes we get the following collection of classes:

public class Job implements Serializable {   
    private String title;
     //getters and setters
}
public class Address implements Serializable {
    private String street;
    private String zipcode;
    //getters and setters
}
public class Person implements Serializable {
    private String firstName;
    private String lastName;
    private int age;
    //each Person has a Job
    private Job job;
    //each Person has many Address
    private Collection contacts;   
     //getters and setters       
}

Using Import Java Bean Model we can create a model for this domain structure which will look something like this:

Note that when importing the model the only thing we needed to do is to specify the target role of the Contacts relationship, since NWDS needs to know what model class this (Address) is hidden behind Collection.

We can also create a collection of simple Dictionary Data types for each model attribute. We can then assign the dictionary types to the corresponding properties of each model class, which is a customary WDJ development technique.

Let’s look at the details of the relationship Person-Job.

We have a source role (Person) and a target role (Job), since it’s a one-to-one relationship we have cardinalities 0:1 set at both roles. The navigation is from Person to Job only which reflects Java beans relationship given above. Let’s look at the details of the Person-Address relationship which corresponds to the getContacts() method of Person bean returning a Collection of Address objects.

The difference here is in the target role cardinality (since we have a Collection of instances of the target model class Address), which is 0:n. The source role is also marked as an aggregation. The interesting thing to do is to look at how NWDS stores this model information. For this we can open a generated .wdmodelclass objects using Open With, Text Editor from the Navigator tab. Here are the contents of Person.wdmodelclass

<!-- MetaDataAPI generated on: Friday, May 25, 2007 3:26:31 PM CEST -->
<ModelClass xmlns="http://xml.sap.com/2002/10/metamodel/webdynpro" xmlns:IDX="urn:sap.com:WebDynpro.ModelClass:2.0" mmRelease="6.30" mmVersion="2.0" mmTimestamp="1180099591892" name="Person" package="ch.unil.tmp.wd.domain" masterLanguage="en">
    <ModelClass.Parent>
        <Core.Reference package="ch.unil.tmp.wd.model" name="PersonJbm" type="Model"/>
    </ModelClass.Parent>
    <ModelClass.Properties>
        <ModelClassProperty name="age">
            <Property.Type>
                <Core.ForeignReference modelName="DtDictionary" package="ch.unil.tmp.wd.ddic.types.simple" name="Age" type="DtSimpleType"/>
            </Property.Type>
        </ModelClassProperty>
        <ModelClassProperty name="firstName">
            <Property.Type>
                <Core.ForeignReference modelName="DtDictionary" package="ch.unil.tmp.wd.ddic.types.simple" name="FirstName" type="DtSimpleType"/>
            </Property.Type>
        </ModelClassProperty>
        <ModelClassProperty name="lastName">
            <Property.Type>
                <Core.ForeignReference modelName="DtDictionary" package="ch.unil.tmp.wd.ddic.types.simple" name="LastName" type="DtSimpleType"/>
            </Property.Type>
        </ModelClassProperty>
    </ModelClass.Properties>
</ModelClass>

And here is Job.wdmodelclass

<!-- MetaDataAPI generated on: Friday, May 25, 2007 3:26:31 PM CEST -->
<ModelClass xmlns="http://xml.sap.com/2002/10/metamodel/webdynpro" xmlns:IDX="urn:sap.com:WebDynpro.ModelClass:2.0" mmRelease="6.30" mmVersion="2.0" mmTimestamp="1180099591892" name="Job" package="ch.unil.tmp.wd.domain" masterLanguage="en">
    <ModelClass.Parent>
        <Core.Reference package="ch.unil.tmp.wd.model" name="PersonJbm" type="Model"/>
    </ModelClass.Parent>
    <ModelClass.Properties>
        <ModelClassProperty name="title">
            <Property.Type>
                <Core.ForeignReference modelName="DtDictionary" package="ch.unil.tmp.wd.ddic.types.simple" name="Title" type="DtSimpleType"/>
            </Property.Type>
        </ModelClassProperty>
    </ModelClass.Properties>
</ModelClass>

To see how the relationships are modeled we have to look at the generated PersonJbm.wdmodel file.

<!-- MetaDataAPI generated on: Friday, May 25, 2007 3:26:31 PM CEST -->
<Model xmlns="http://xml.sap.com/2002/10/metamodel/webdynpro" xmlns:IDX="urn:sap.com:WebDynpro.Model:2.0" mmRelease="6.30" mmVersion="2.0" mmTimestamp="1180099591892" name="PersonJbm" package="ch.unil.tmp.wd.model" masterLanguage="en">
    <Model.ModelClasses>
        <Core.Reference package="ch.unil.tmp.wd.domain" name="Address" type="ModelClass"/>
        <Core.Reference package="ch.unil.tmp.wd.domain" name="Job" type="ModelClass"/>
        <Core.Reference package="ch.unil.tmp.wd.domain" name="Person" type="ModelClass"/>
    </Model.ModelClasses>
    <Model.ModelRelations>
        <ModelRelation name="Person.Contacts">
            <ModelRelation.ModelRelationRoles>
                <ModelRelationRole aggregate="true" name="Person.Contacts.untitled" navigable="false">
                    <ModelRelationRole.ModelClass>
                        <Core.Reference package="ch.unil.tmp.wd.domain" name="Person" type="ModelClass"/>
                    </ModelRelationRole.ModelClass>
                </ModelRelationRole>
                <ModelRelationRole cardinality="_0_n" name="Contacts">
                    <ModelRelationRole.ModelClass>
                        <Core.Reference package="ch.unil.tmp.wd.domain" name="Address" type="ModelClass"/>
                    </ModelRelationRole.ModelClass>
                </ModelRelationRole>
            </ModelRelation.ModelRelationRoles>
        </ModelRelation>
        <ModelRelation name="Person.Job">
            <ModelRelation.ModelRelationRoles>
                <ModelRelationRole name="Person.Job.untitled" navigable="false">
                    <ModelRelationRole.ModelClass>
                        <Core.Reference package="ch.unil.tmp.wd.domain" name="Person" type="ModelClass"/>
                    </ModelRelationRole.ModelClass>
                </ModelRelationRole>
                <ModelRelationRole name="Job">
                    <ModelRelationRole.ModelClass>
                        <Core.Reference package="ch.unil.tmp.wd.domain" name="Job" type="ModelClass"/>
                    </ModelRelationRole.ModelClass>
                </ModelRelationRole>
            </ModelRelation.ModelRelationRoles>
        </ModelRelation>
    </Model.ModelRelations>
    <Model.Settings>
        <ModelSetting name="requiresCodeGeneration" value="false">
            <ModelSetting.SettingDefinition>
                <Core.Reference package="com.sap.ide.webdynpro.modeltypes" name="JavaBean" type="ModelType" path="ModelSettingDefinition:requiresCodeGeneration"/>
            </ModelSetting.SettingDefinition>
        </ModelSetting>
    </Model.Settings>
    <Model.ModelType>
        <Core.Reference package="com.sap.ide.webdynpro.modeltypes" name="JavaBean" type="ModelType"/>
    </Model.ModelType>
</Model>

For each relationship in our model we have a ModelRelation element in the .wdmodel file which contains all relevant information for each role of the relationship: it’s cardinality, is is an aggregate role, navigable role, and the relevant model class. And in each model class we have the information about the package and class name of the related Java bean, as well as, the information about the dictionary types used for the model properties.

Using XDoclet to regenerate (reimport) the model structure

Now what happens if we change our Java beans structure: add or remove attributes (with their corresponding dictionary types), add or remove relations to new Java beans, or change relation cardinality? Well, you say, nothing is simpler, just right-click on our model and…

Wait… why is the Reimport Model option grayed out?! Do I really have to create all of the model classes by hand, assign dictionary types to all of the attributes, and create all of the relations setting appropriate cardinalities, aggregate and navigable markers? Well, you do have to recreate all of this by hand for Java Bean models. This is an old issue with NW 7.0, and should be corrected in NW 7.3 (I have not checked yet). But if you have to work with NW 7.0 you might like a solution I came up a while ago.

Since all of the information is there in the plain XML files (.wdmodel and .wdmodelclass), it should not be difficult to create an Ant script with some XDoclet magic which reconstructs the model by parsing all of the domain classes reading some custom class and method-level markers which describe each class model attributes and relations.

For example annotated Person.java bean will look like this

/**
 * Model class for Person domain object,
 * a Person has 1:n Address,
 * a Person has 1:1 Job
 *
 * @wdmodelclass
 */
public class Person implements Serializable {
    /**
     * @wdmodelproperty type="FirstName"
     */
    private String firstName;
    /**
     * @wdmodelproperty type="LastName"
     */
    private String lastName;
    /**
     * @wdmodelproperty type="Age"
     */
    private int age;
    /**
     * @wdmodelrelation
     */
    private Job job;
    /**
     * @wdmodelrelation oneToMany="true" modelClass="Address"
     */
    private Collection contacts;   
//getters and setters
}

Class-level @wdmodelclass marker tells XDoclet to consider the bean as a candidate for a model class. Attribute-level @wdmodelproperty markers mark a model attribute with optional type attribute specifying the name of the dictionary type we want to use for this property. Finally, @wdmodelrelation is an attribute-level marker signalling to XDoclet that we need to create a model relation. In the last case, if the attribute modelClass is present XDoclet will use it’s value for the name of the target role’s model class, otherwise it will use the name of the attribute type (Job). For the one-to-many relations (contacts) we need a oneToMany attribute set to true to distinguish it from the one-to-one relations (job).

In order to achieve this I needed to parse .wdmodel class to be able to recuperate some necessary information: model name, package and path, model classes package and path, master language, etc… I did it with a small Java XML parser, WdModelParser.java. Then I created two XDoclet template files wdmodel.xdt and wdmodelclass.xdt for creating .wdmodel and .wdmodelclasses respectively. To put it all together, I created a small Ant build.xml script which does all the work by calling a custom XDoclet task.

Here is a structure of the WDJ project with all Ant and XDoclet relevant resources in ant folder and build.xml script with ant.build.properties file:

As you can see, you need some JARs to make this work. It works with Ant 1.7.0 and later versiond and Java 1.4.2 (since it’s the only version of Java we can use with NW 7.0). Here is the code for the model paser ant.utils.WdModelParser.java

public class WdModelParser {   
    private static Node findFirstChild(NodeList ns, String tagName){
        Node firstNode = null;
        for (int i=0; i<ns.getLength(); i++){
            Node nextNode = ns.item(i);
            if (ns.item(i).getNodeName().equals(tagName)){
                firstNode = nextNode;
                continue;
            }
        }
        return firstNode;
    }
    public static void main(String[] args) {
        if (args.length>0){
            System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.crimson.jaxp.DocumentBuilderFactoryImpl");
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            try {
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document doc = builder.parse(new File(args[0]));
                NodeList ns = doc.getElementsByTagName("Model");
                Node node = ns.item(0);
                String output = "";
                output += "model.name=" + node.getAttributes().getNamedItem("name").getNodeValue();
                output += ";";
                output += "model.package=" + node.getAttributes().getNamedItem("package").getNodeValue();
                output += ";";
                output += "master.language=" + node.getAttributes().getNamedItem("masterLanguage").getNodeValue();
                output += ";";
                ns = doc.getElementsByTagName("Model.ModelClasses");
                node = findFirstChild(ns.item(0).getChildNodes(), "Core.Reference");
                output += "model.classes.package=" + node.getAttributes().getNamedItem("package").getNodeValue();
                output += ";";
                System.out.println(output);
            } catch (ParserConfigurationException e) {
                System.exit(-1);
            } catch (SAXException e) {
                System.exit(-1);
            } catch (IOException e) {
                System.exit(-1);
            }
        }
    }
}

Here is XDoclet template for creating .wdmodelclass files, wdmodelclass.xdt:

<XDtCollection:create name="buitInTypes"/>
<XDtCollection:put name="buitInTypes" value="binary"/>
<XDtCollection:put name="buitInTypes" value="boolean"/>
<XDtCollection:put name="buitInTypes" value="date"/>
<XDtCollection:put name="buitInTypes" value="decimal"/>
<XDtCollection:put name="buitInTypes" value="double"/>
<XDtCollection:put name="buitInTypes" value="float"/>
<XDtCollection:put name="buitInTypes" value="integer"/>
<XDtCollection:put name="buitInTypes" value="long"/>
<XDtCollection:put name="buitInTypes" value="short"/>
<XDtCollection:put name="buitInTypes" value="string"/>
<XDtCollection:put name="buitInTypes" value="time"/>
<XDtCollection:put name="buitInTypes" value="timestamp"/>
<!-- MetaDataAPI generated on: Friday, May 25, 2007 3:26:31 PM CEST -->
<XDtClass:ifHasClassTag tagName="wdmodelclass">
<ModelClass xmlns="http://xml.sap.com/2002/10/metamodel/webdynpro" xmlns:IDX="urn:sap.com:WebDynpro.ModelClass:2.0" mmRelease="6.30" mmVersion="2.0" mmTimestamp="1180099591892" name="<XDtClass:className/>" package="<XDtPackage:packageName/>" masterLanguage="<XDtConfig:configParameterValue paramName="masterLanguage"/>">
    <ModelClass.Parent>
        <Core.Reference package="<XDtConfig:configParameterValue paramName="modelPackage"/>" name="<XDtConfig:configParameterValue paramName="modelName"/>" type="Model"/>
    </ModelClass.Parent>
    <ModelClass.Properties>
        <XDtField:forAllFields sort="true">
            <XDtField:ifHasFieldTag tagName="wdmodelproperty">
                <ModelClassProperty name="<XDtField:fieldName/>"
                    <XDtField:ifFieldTagValueEquals tagName="wdmodelproperty" paramName="readonly" value="true">readonly="true"</XDtField:ifFieldTagValueEquals>
                >
                    <Property.Type>
                        <Core.ForeignReference modelName="DtDictionary"
                            <XDtField:ifHasFieldTag tagName="wdmodelproperty" paramName="type">
                                <XDtCollection:ifContains name="buitInTypes" value="<XDtField:fieldTagValue tagName='wdmodelproperty' paramName='type'/>">
                                    package="com.sap.dictionary"
                                </XDtCollection:ifContains>
                                <XDtCollection:ifDoesntContain name="buitInTypes" value="<XDtField:fieldTagValue tagName='wdmodelproperty' paramName='type'/>">
                                    package="<XDtConfig:configParameterValue paramName="ddicTypesPackage"/>"
                                </XDtCollection:ifDoesntContain>
                                name="<XDtField:fieldTagValue tagName="wdmodelproperty" paramName="type"/>"                           
                            </XDtField:ifHasFieldTag>
                            <XDtField:ifDoesntHaveFieldTag tagName="wdmodelproperty" paramName="type">
                                package="<XDtConfig:configParameterValue paramName="ddicTypesPackage"/>"
                                name="<XDtField:getCapitalizedFieldName/>"                               
                            </XDtField:ifDoesntHaveFieldTag>
                            type="DtSimpleType" />
                    </Property.Type>
                </ModelClassProperty>
            </XDtField:ifHasFieldTag>
        </XDtField:forAllFields>
        <XDtMethod:forAllMethods sort="true">
            <XDtMethod:ifIsPublic>       
                <XDtMethod:ifIsGetter>
                    <XDtMethod:ifHasMethodTag tagName="wdmodelproperty">
                        <ModelClassProperty name="<XDtMethod:propertyName/>"
                            <XDtMethod:ifMethodTagValueEquals tagName="wdmodelproperty" paramName="readonly" value="true">readonly="true"</XDtMethod:ifMethodTagValueEquals>
                        >
                            <Property.Type>
                                <Core.ForeignReference modelName="DtDictionary"                                    
                                    <XDtMethod:ifHasMethodTag tagName="wdmodelproperty" paramName="type">
                                        <XDtCollection:ifContains name="buitInTypes" value="<XDtMethod:methodTagValue tagName='wdmodelproperty' paramName='type'/>">
                                            package="com.sap.dictionary"
                                        </XDtCollection:ifContains>
                                        <XDtCollection:ifDoesntContain name="buitInTypes" value="<XDtMethod:methodTagValue tagName='wdmodelproperty' paramName='type'/>">
                                            package="<XDtConfig:configParameterValue paramName="ddicTypesPackage"/>"
                                        </XDtCollection:ifDoesntContain>
                                        name="<XDtMethod:methodTagValue tagName="wdmodelproperty" paramName="type"/>"                           
                                    </XDtMethod:ifHasMethodTag>
                                    <XDtMethod:ifDoesntHaveMethodTag tagName="wdmodelproperty" paramName="type">
                                        package="<XDtConfig:configParameterValue paramName="ddicTypesPackage"/>"
                                        name="<XDtMethod:methodNameWithoutPrefix/>"                               
                                    </XDtMethod:ifDoesntHaveMethodTag>
                                    type="DtSimpleType" />
                            </Property.Type>
                        </ModelClassProperty>                       
                    </XDtMethod:ifHasMethodTag>
                </XDtMethod:ifIsGetter>
            </XDtMethod:ifIsPublic>
        </XDtMethod:forAllMethods>
            </ModelClass.Properties>
</ModelClass>
</XDtClass:ifHasClassTag>
<XDtCollection:destroy name="buitInTypes"/>

Here is the XDoclet template for generating the .wdmodel file, wdmodel.xdt:

<!-- MetaDataAPI generated on: Friday, May 25, 2007 3:26:31 PM CEST -->
<Model xmlns="http://xml.sap.com/2002/10/metamodel/webdynpro" xmlns:IDX="urn:sap.com:WebDynpro.Model:2.0" mmRelease="6.30" mmVersion="2.0" mmTimestamp="1180099591892" name="<XDtConfig:configParameterValue paramName="modelName"/>" package="<XDtConfig:configParameterValue paramName="modelPackage"/>" masterLanguage="<XDtConfig:configParameterValue paramName="masterLanguage"/>">
    <Model.ModelClasses>
        <XDtClass:forAllClasses>
            <XDtClass:ifHasClassTag tagName="wdmodelclass">
                <Core.Reference package="<XDtPackage:packageName/>" name="<XDtClass:className/>" type="ModelClass"/>
            </XDtClass:ifHasClassTag>
        </XDtClass:forAllClasses>
    </Model.ModelClasses>
    <Model.ModelRelations>
        <XDtClass:forAllClasses>
            <XDtField:forAllFields>
                <XDtField:ifHasFieldTag tagName="wdmodelrelation">       
                    <ModelRelation name="<XDtClass:className/>.<XDtField:getCapitalizedFieldName/>">
                        <ModelRelation.ModelRelationRoles>
                            <XDtComment:comment><!-- Source role --></XDtComment:comment>
                            <ModelRelationRole name="<XDtClass:className/>.<XDtField:getCapitalizedFieldName/>.untitled"
                                <XDtField:ifFieldTagValueEquals tagName="wdmodelrelation" paramName="oneToMany" value="true">
                                    aggregate="true"
                                </XDtField:ifFieldTagValueEquals>
                                    cardinality="_0_1"
                                    navigable="false">
                                <ModelRelationRole.ModelClass>
                                    <Core.Reference package="<XDtPackage:packageName/>" name="<XDtClass:className/>" type="ModelClass"/>
                                </ModelRelationRole.ModelClass>
                            </ModelRelationRole>
                            <XDtComment:comment><!-- Target role --></XDtComment:comment>
                            <ModelRelationRole name="<XDtField:getCapitalizedFieldName/>"
                                    aggregate="false"
                                <XDtField:ifHasFieldTag tagName="wdmodelrelation" paramName="oneToMany">
                                    <XDtField:ifFieldTagValueEquals tagName="wdmodelrelation" paramName="oneToMany" value="true">
                                        cardinality="_0_n"
                                    </XDtField:ifFieldTagValueEquals>
                                    <XDtField:ifFieldTagValueEquals tagName="wdmodelrelation" paramName="oneToMany" value="false">
                                        cardinality="_0_n"
                                    </XDtField:ifFieldTagValueEquals>
                                </XDtField:ifHasFieldTag>
                                <XDtField:ifDoesntHaveFieldTag tagName="wdmodelrelation" paramName="oneToMany">
                                    cardinality="_0_1"
                                </XDtField:ifDoesntHaveFieldTag>
                                    navigable="true"
                            >
                                <ModelRelationRole.ModelClass>
                                    <Core.Reference package="<XDtPackage:packageName/>"
                                    <XDtField:ifHasFieldTag tagName="wdmodelrelation" paramName="oneToMany">
                                        <XDtField:ifFieldTagValueEquals tagName="wdmodelrelation" paramName="oneToMany" value="true">
                                            name="<XDtField:fieldTagValue tagName="wdmodelrelation" paramName="modelClass"/>"
                                        </XDtField:ifFieldTagValueEquals>
                                        <XDtField:ifFieldTagValueEquals tagName="wdmodelrelation" paramName="oneToMany" value="false">
                                            name="<XDtClass:classOf><XDtField:fieldType/></XDtClass:classOf>"
                                        </XDtField:ifFieldTagValueEquals>
                                    </XDtField:ifHasFieldTag>
                                    <XDtField:ifDoesntHaveFieldTag tagName="wdmodelrelation" paramName="oneToMany">
                                            name="<XDtClass:classOf><XDtField:fieldType/></XDtClass:classOf>"
                                    </XDtField:ifDoesntHaveFieldTag>                        
                                    type="ModelClass"/>
                                </ModelRelationRole.ModelClass>
                            </ModelRelationRole>
                        </ModelRelation.ModelRelationRoles>
                    </ModelRelation>       
                </XDtField:ifHasFieldTag>
            </XDtField:forAllFields>           
            <XDtMethod:forAllMethods>
                <XDtMethod:ifIsPublic>       
                    <XDtMethod:ifIsGetter>
                        <XDtMethod:ifHasMethodTag tagName="wdmodelrelation">
                            <ModelRelation name="<XDtClass:className/>.<XDtMethod:methodNameWithoutPrefix/>">
                                <ModelRelation.ModelRelationRoles>
                                    <XDtComment:comment><!-- Source role --></XDtComment:comment>
                                    <ModelRelationRole name="<XDtClass:className/>.<XDtMethod:methodNameWithoutPrefix/>.untitled"
                                        <XDtMethod:ifMethodTagValueEquals tagName="wdmodelrelation" paramName="oneToMany" value="true">
                                            aggregate="true"
                                        </XDtMethod:ifMethodTagValueEquals>
                                            cardinality="_0_1"
                                            navigable="false">
                                        <ModelRelationRole.ModelClass>
                                            <Core.Reference package="<XDtPackage:packageName/>" name="<XDtClass:className/>" type="ModelClass"/>
                                        </ModelRelationRole.ModelClass>
                                    </ModelRelationRole>
                                    <XDtComment:comment><!-- Target role --></XDtComment:comment>
                                    <ModelRelationRole name="<XDtMethod:methodNameWithoutPrefix/>"
                                            aggregate="false"
                                        <XDtMethod:ifHasMethodTag tagName="wdmodelrelation" paramName="oneToMany">
                                            <XDtMethod:ifMethodTagValueEquals tagName="wdmodelrelation" paramName="oneToMany" value="true">
                                                cardinality="_0_n"
                                            </XDtMethod:ifMethodTagValueEquals>
                                            <XDtMethod:ifMethodTagValueEquals tagName="wdmodelrelation" paramName="oneToMany" value="false">
                                                cardinality="_0_n"
                                            </XDtMethod:ifMethodTagValueEquals>
                                        </XDtMethod:ifHasMethodTag>
                                        <XDtMethod:ifDoesntHaveMethodTag tagName="wdmodelrelation" paramName="oneToMany">
                                            cardinality="_0_1"
                                        </XDtMethod:ifDoesntHaveMethodTag>
                                            navigable="true"
                                    >
                                        <ModelRelationRole.ModelClass>
                                            <Core.Reference package="<XDtPackage:packageName/>"
                                            <XDtMethod:ifHasMethodTag tagName="wdmodelrelation" paramName="oneToMany">
                                                <XDtMethod:ifMethodTagValueEquals tagName="wdmodelrelation" paramName="oneToMany" value="true">
                                                    name="<XDtMethod:methodTagValue tagName="wdmodelrelation" paramName="modelClass"/>"
                                                </XDtMethod:ifMethodTagValueEquals>
                                                <XDtMethod:ifMethodTagValueEquals tagName="wdmodelrelation" paramName="oneToMany" value="false">
                                                    name="<XDtClass:classOf><XDtMethod:methodType/></XDtClass:classOf>"
                                                </XDtMethod:ifMethodTagValueEquals>
                                            </XDtMethod:ifHasMethodTag>
                                            <XDtMethod:ifDoesntHaveMethodTag tagName="wdmodelrelation" paramName="oneToMany">
                                                    name="<XDtClass:classOf><XDtMethod:methodType/></XDtClass:classOf>"
                                            </XDtMethod:ifDoesntHaveMethodTag>                        
                                            type="ModelClass"/>
                                        </ModelRelationRole.ModelClass>
                                    </ModelRelationRole>
                                </ModelRelation.ModelRelationRoles>
                            </ModelRelation>
                        </XDtMethod:ifHasMethodTag>
                    </XDtMethod:ifIsGetter>
                </XDtMethod:ifIsPublic>
            </XDtMethod:forAllMethods>
        </XDtClass:forAllClasses>       
    </Model.ModelRelations>
    <Model.Settings>
        <ModelSetting name="requiresCodeGeneration" value="false">
            <ModelSetting.SettingDefinition>
                <Core.Reference package="com.sap.ide.webdynpro.modeltypes" name="JavaBean" type="ModelType" path="ModelSettingDefinition:requiresCodeGeneration"/>
            </ModelSetting.SettingDefinition>
        </ModelSetting>
    </Model.Settings>
    <Model.ModelType>
        <Core.Reference package="com.sap.ide.webdynpro.modeltypes" name="JavaBean" type="ModelType"/>
    </Model.ModelType>
</Model>

And here is a build.xml script with ant.build.properties file which needs to be modified according to the structure of your project:

ant.build.properties

#path to Java 1.4 home directory
java.home.dir=C:\\j2sdk1.4.2_19
#path to the root source folder where the Java domain objects reside, can be absolute
src.dir=src/packages
#path to the root source folder where to recreate the model classes, can be absolute
dest.dir=src/packages
#package for dictionary types
ddic.types.package=ch.unil.tmp.wd.ddic.types.simple

build.xml

<project name="xdoclet" basedir=".">
<property file="ant.build.properties"></property>
<property name="env.CLASSPATH" value="${java.home.dir}/lib"></property>
<property environment="env"></property>
<property name="lib" location="ant/lib"></property>
<!--
Needed to load ant-contrib tasks.
-->
<taskdef resource="net/sf/antcontrib/antlib.xml">
  <classpath>
    <pathelement location="${lib}/ant-contrib-1.0b3.jar"/>
  </classpath>
</taskdef>
<!--
Needed to run xdoclet.
-->
<path id="xdoclet.lib.path">
    <fileset dir="${lib}">
        <include name="**/*.jar"/>
        <exclude name="**/ant-contrib-1.0b3.jar"/>
    </fileset>
</path>
<!--
    Checks if the command line parameter model.file is present.
-->
<target name="init">
    <available property="model.file.exists" file="${model.file}"></available>
    <if>
        <not>
            <equals arg1="${model.file.exists}" arg2="true"/>
        </not>
        <then>
            <fail message="Model file ${model.file} does not exist. Specify model file with -Dmodel.file command line parameter."/>
        </then>
    </if>
    <available property="wdmodel.parser.exists" file="ant/utils/WdModelParser.class"></available>
    <condition property="xdoclet.templates.exist">
        <and>
            <available file="ant/xdoclet/wdmodel.xdt"></available>
            <available file="ant/xdoclet/wdmodelclass.xdt"></available>   
        </and>
    </condition>
</target>
<!--
    Prepares the WdModelParser.class if needed.
-->
<target name="prepare.wdmodel.parser" unless="wdmodel.parser.exists">
    <echo>Preparing WdModelParser.class</echo>
    <javac srcdir="ant/utils"></javac>
</target>
<!--
Runs xdoclet to recreate wdmodel classes and wdmodel file.
-->
<target name="xdoclet" depends="init,prepare.wdmodel.parser">
    <echo>+---------------------------------------------+</echo>
    <echo>|              Running XDoclet                |</echo>
    <echo>+---------------------------------------------+</echo>
    <echo>Parsing model file ${model.file}</echo>
    <java classname="ant.utils.WdModelParser" classpath=".;${java.home.dir}/jre/lib/rt.jar" outputproperty="wdmodel.parser.output">
        <arg line="${model.file}" />
    </java>
    <propertyregex property="model.name" input="${wdmodel.parser.output}" regexp="model\.name=(\w+);" select="\1"/>
    <propertyregex property="model.package" input="${wdmodel.parser.output}" regexp="model\.package=([\w\.]+);" select="\1"/>
    <propertyregex property="master.language" input="${wdmodel.parser.output}" regexp="master\.language=(\w+)" select="\1"/>
    <propertyregex property="model.classes.package" input="${wdmodel.parser.output}" regexp="model\.classes\.package=([\w\.]+);" select="\1"/>
    <echo>Model name: ${model.name}</echo>
    <echo>Model package: ${model.package}</echo>
    <echo>Master language: ${master.language}</echo>
    <echo>Model classes package: ${model.classes.package}</echo>
    <propertyregex property="model.path" input="${model.package}" regexp="\." replace="/"/>
    <propertyregex property="model.classes.path" input="${model.classes.package}" regexp="\." replace="/"/>
    <echo>Model path: ${model.path}</echo>   
    <echo>Model classes path: ${model.classes.path}</echo>   
    <taskdef name="wdModelClassDoclet" classname="xdoclet.DocletTask" classpathref="xdoclet.lib.path"/>
    <wdModelClassDoclet
        verbose="true"
        destdir="${dest.dir}"
        excludedtags="@version,@author,@see">
        <configParam name="modelName" value="${model.name}"/>
        <configParam name="modelPackage" value="${model.package}"/>
        <configParam name="masterLanguage" value="${master.language}"/>       
        <configParam name="ddicTypesPackage" value="${ddic.types.package}"/>
        <fileset dir="${src.dir}">
            <include name="${model.classes.path}/*.java"/>
        </fileset>
        <template
            templateFile="ant/xdoclet/wdmodelclass.xdt"
            destinationfile="{0}.wdmodelclass"
        />
    </wdModelClassDoclet>
        <taskdef name="wdModelDoclet" classname="xdoclet.DocletTask" classpathref="xdoclet.lib.path"/>
    <wdModelDoclet
        verbose="true"
        destdir="${dest.dir}"
        excludedtags="@version,@author,@see">
        <configParam name="masterLanguage" value="${master.language}"/>       
        <configParam name="modelPackage" value="${model.package}"/>
        <configParam name="modelName" value="${model.name}"/>
        <configParam name="ddicTypesPackage" value="${ddic.types.package}"/>
        <fileset dir="${src.dir}">
            <include name="**/domain/*.java"/>
        </fileset>
        <template
            templateFile="ant/xdoclet/wdmodel.xdt"
            destinationfile="${model.path}/${model.name}.wdmodel"
        />
    </wdModelDoclet>   
</target>
</project>

To run the script one should use an external Ant (I could not get it running with the one used by NWDS itself). Below there is an example of setting up the External Tools program to run the script from NWDS. Other than the properties in the ant.build.properties file it requires a .wdmodel file to be selected during the launch (note the variable used in the Arguments section).

If everything goes well, you can delete the old .wdmodelclass files, make the necessary adjustments to the Java beans and the script should recreate the model for you.

Buildfile: C:\workspace\sap\nwds70_tmp\TmpModelReimportWd\build.xml
init:
prepare.wdmodel.parser:
xdoclet:
     [echo] +---------------------------------------------+
     [echo] |              Running XDoclet                |
     [echo] +---------------------------------------------+
     [echo] Parsing model file C:\workspace\sap\nwds70_tmp\TmpModelReimportWd\src\packages\ch\unil\tmp\wd\model\PersonJbm.wdmodel
     [echo] Model name: PersonJbm
     [echo] Model package: ch.unil.tmp.wd.model
     [echo] Master language: en
     [echo] Model classes package: ch.unil.tmp.wd.domain
     [echo] Model path: ch/unil/tmp/wd/model
     [echo] Model classes path: ch/unil/tmp/wd/domain
[wdModelClassDoclet] 3 mai 2012 15:28:10 xdoclet.XDocletMain start
[wdModelClassDoclet] INFO: Running <template/>
[wdModelClassDoclet] 3 mai 2012 15:28:10 xdoclet.TemplateSubTask engineStarted
[wdModelClassDoclet] INFO: Generating output for 'ch.unil.tmp.wd.domain.Person' using template file 'file:/C:/workspace/sap/nwds70_tmp/TmpModelReimportWd/ant/xdoclet/wdmodelclass.xdt'.
[wdModelClassDoclet] 3 mai 2012 15:28:10 xdoclet.TemplateSubTask engineStarted
[wdModelClassDoclet] INFO: Generating output for 'ch.unil.tmp.wd.domain.Address' using template file 'file:/C:/workspace/sap/nwds70_tmp/TmpModelReimportWd/ant/xdoclet/wdmodelclass.xdt'.
[wdModelClassDoclet] 3 mai 2012 15:28:10 xdoclet.TemplateSubTask engineStarted
[wdModelClassDoclet] INFO: Generating output for 'ch.unil.tmp.wd.domain.Job' using template file 'file:/C:/workspace/sap/nwds70_tmp/TmpModelReimportWd/ant/xdoclet/wdmodelclass.xdt'.
[wdModelDoclet] 3 mai 2012 15:28:10 xdoclet.XDocletMain start
[wdModelDoclet] INFO: Running <template/>
[wdModelDoclet] 3 mai 2012 15:28:10 xdoclet.TemplateSubTask engineStarted
[wdModelDoclet] INFO: Generating output 'ch/unil/tmp/wd/model/PersonJbm.wdmodel' using template file 'file:/C:/workspace/sap/nwds70_tmp/TmpModelReimportWd/ant/xdoclet/wdmodel.xdt'.
BUILD SUCCESSFUL
Total time: 1 second

Once the script finishes, do not forget to Reload the WDJ project to keep WDJ happy. For now the script only works with one way one-to-one and one-to-many relations, but the XDoclet templates could be easily extended to cover the other types of relations.

Useful links:

XDoclet Invocation through Ant

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply