JAVA exPress > Archiwum > Numer 4 (2009-06-01) > J2ME: Serializacja obiektów, cz. II

J2ME: Serializacja obiektów, cz. II

Przygotowanie środowiska i stworzenie projektu

Narzędziem, którego będziemy używać do budowania projektu będzie oczywiście Maven 2.x [1]. Aby było ciekawiej w fazie generowania źródeł (generate-sources), użyjemy plugin'u (maven-antrun-plugin). Zadaniem Ant'a [2] będzie generowanie kodu wykorzystując klasę:

org.castor.anttask.CastorCodeGenTask

Przed dalszą częścią artykułu radzę o zaznajomienie się ze świetnym artykułem na temat Maven'a z drugiego wydania Java Express, Grudzień 2008 (Maven 2 - jak ułatwić sobie pracę, cz. I, Rafał Kotusiewicz).

Stworzymy dwa projekty:

1. CastorSourceGenerator

2. CastorMsgFactoryME

Projekty możemy stworzyć albo wydając komendy z linii poleceń albo poprzez wtyczke do Eclipse (M2 [3]). Ja wykorzystam polecenia:

        mvn archetype:create -DgroupId=com.sonic.gen -DartifactId=CastorSourceGenerator
        mvn archetype:create -DgroupId=com.sonic.factory -DartifactId=CastorMsgFactoryME
        

Maven stworzy nam 2 katalogi z nazwami takimi jak podaliśmy w atrybutach artifactId. Każdy katlog zawiera podkatalogi wraz z plikami typu "HelloWorld":

src/main/java/com/sonic/gen/App.java

src/test/java/com/sonic/gen/TestApp.java

oraz plik POM (Project Object Model), którego z przykładową zawartością:

 
            <project xmlns="http://maven.apache.org/POM/4.0.0"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.sonic.factory</groupId> <artifactId>CastorMsgFactoryME</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>CastorMsgFactoryME</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>  

Jeżeli chcemy zaimportować utworzone szablony jako projekt w Eclipse/IntelliJ, to w każdym z katalogów (CastorSourceGenerator, CastorMsgFactoryME, CastorTester) wywołujemy komendę:

            mvn eclipse:eclipse
        

lub

            mvn idea:idea
        

Jeżeli wybraliśmy Eclipse to zostaną dodane 2 pliki: .project oraz .claspath.

Zostanie utworzony także katalog target a w nim plik mvn-eclipse-cache.properties.

Po zaimportowaniu szablonów jako projekt java w eclipse (File -> New -> Java Project... -> Create project from existing source) powinniśmy otrzymać strukturę przedstawioną na rys. 1

Rys. 1. Widok w Eclipse 3.5M3

Najpierw zajmiemy się projektem CastorSourceGenerator. Utworzymy klasę o nazwie MySourceGenerator, która przejmie kontorolę nad domyślną generacją kodu wynikowego. Jej w pełni kwalifikowaną nazwę podamy jako wartość atrybutu:

org.exolab.castor.builder.jclassPrinterTypes

w pliku castorbuilder.properties.

W klasie tej zaimplementujemy interfejs org.exolab.castor.builder.printing.JClassPrinter. Przykład jego użycia znajdziemy w klasie org.exolab.castor.builder.printing.WriterJClassPrinter (listing 2).

Klasa WriterJClassPrinter posłuży nam jako szablon, na jej bazie zbudujemy własną implementację.

 
            package org.exolab.castor.builder.printing;
 
            import org.exolab.javasource.JClass;
            import org.exolab.javasource.JComment;
 
            public class WriterJClassPrinter implements JClassPrinter {
 
                public void printClass(final JClass jClass, final String outputDir,
                        final String lineSeparator, final String header) {
 
                    // hack for the moment
                    // to avoid the compiler complaining with java.util.Date
                    jClass.removeImport("org.exolab.castor.types.Date");
 
                    // add header
                    JComment comment = new JComment(JComment.HEADER_STYLE);
                    comment.appendComment(header);
                    jClass.setHeader(comment);
 
                    // print
                    jClass.print(outputDir, lineSeparator);
                }
            }
 

1. Zamieniamy nazwę klasy App.java na MySourceGenerator.java, usuwamy wszystkie zbędne rzeczy i implementujemy interfejs JclassPrinter. Klasy, których głównie będziemy używać, znajdują się w pakiecie: org.exolab.javasource. Ich nazwy tak naprawdę odzwierciedlają to co chcemy zrobić. Czyli chcąc stworzyć metodę, tworzymy obiekt JMethod, natomiast chcąc dodać parametr do metody, utworzymy obiekt Jparameter.

2. Na razie nie przejmujemy się tym, że projekt się nie buduje, ściągamy źródła Castor'a (uwaga, musi to być wersja 1.2!!!) [4] i przechodzimy do katalogu codegen. Radzę zrobić sobie kopie tego katalogu, kopiując jego zawartość np. do katalogu codegen-me. Musimy trochę pogrzebać w klasach. Zmianie ulegną 3 pliki: Jtype.java, CollectionMemberAndAccessorFactory.java oraz SourceFactory.java.

Pamiętając o sygnaturach metod do serializacji/deserializacji:

 
            public void write(final java.io.DataOutputStream dos) throws java.io.IOException
            public void read(final java.io.DataInputStream dis) throws java.io.IOException
 

w klasie org.exolab.javasource.JType dodajemy następujące rzeczy:

 
            /** JType instance for a void (void). */
            public static final JPrimitiveType VOID =
                new JPrimitiveType("void", "void");
 
             /** JType for a DataInputStream. */
            public static final JPrimitiveType DATA_INPUT_STREAM =
                new JPrimitiveType("java.io.DataInputStream",
                        "java.io.DataInputStream");
 
            /** JType instance for a DataOutputStream. */
            public static final JPrimitiveType DATA_OUTPUT_STREAM =
                new JPrimitiveType("java.io.DataOutputStream",
                        "java.io.DataOutputStream");
 

3. Szukamy klasy org.exolab.castor.builder.factory.CollectionMemberAndAccessorFactory.

Musimy zadbać, aby kod wygenerowany dla kolekcji takich jak np. java.util.Vector i java.util.Hashtable wołał tylko metody zgodne z J2ME API [5]. Prawie wszystkie metody dostepne w Java 1.1 [6] są zawarte w CLDC 1.1 (JSR 139). Nie znajdziemy tam tylko metody public synchronized Object clone();

Musimy zadbać aby w kodzie wynikowym istniały odwołania tylko do takich metod jak:

 
            void addElement(Object obj)
            Object elementAt(int index)
            void insertElementAt(Object obj, int index)
            Enumeration elements()
            void removeAllElements()
            boolean removeElement(Object obj)
            void removeElementAt(int index)
            void setElementAt(Object obj, int index)
 

3.1 Pierwsza zmiana nastąpi w metodzie:

 
            private void createGetAsArrayMethod(final CollectionInfo fieldInfo,
                final JClass jClass, final boolean useJava50,
                AnnotationBuilder[] annotationBuilders) { ... }
 

Według JSR 139 w klasie java.util.Vector nie mamy takich metod jak Object[] toArray() oraz Object[] toArray(Object[] a).

W takim wypadku albo usuwamy ciało metody createGetAsArrayMethod(...) albo szukamy metody:

 
            private void createGetAndSetMethods(final CollectionInfo fieldInfo,
                final JClass jClass, final boolean useJava50,
                final AnnotationBuilder[] annotationBuilders) { ... }
 

i usuwamy w niej linijkę:

 
            this.createGetAsArrayMethod(fieldInfo, jClass, useJava50, annotationBuilders);
 

3.2 Ta sama operację powtarzamy w przypadku metod:

 
            private void createGetAsReferenceMethod(final CollectionInfo fieldInfo,
                final JClass jClass) { ... }
 
            private void createSetAsReferenceMethod(final CollectionInfo fieldInfo,
                final JClass jClass, final boolean useJava50) { ... }
 
            private void createSetAsCopyMethod(final CollectionInfo fieldInfo,
                final JClass jClass) { ... }
 

W moim przypadku wszystkie implementacje metod zostały usunięte, a metoda createGetAndSetMethods wygląda jak poniżej.

 
            private void createGetAndSetMethods(final CollectionInfo fieldInfo,
                final JClass jClass, final boolean useJava50,
                final AnnotationBuilder[] annotationBuilders) {
                    this.createGetByIndexMethod(fieldInfo, jClass);
                    this.createSetByIndexMethod(fieldInfo, jClass);
            }
 

3.3 Kolejna zmiana nastąpi w metodzie:

 
            protected void createGetByIndexMethod(final CollectionInfo fieldInfo,
                final JClass jClass) { ... }
 

w linijce:

 
            String value = fieldInfo.getName() + ".get(index)";
 

słowo get zamieniamy na elementAt. Czyli otrzymamy:

 
            String value = fieldInfo.getName() + ".elementAt(index)";
 

Czyli zmieniamy Object get(int index); na Object elementAt(int index);

3.4 Szukamy metody:

 
        protected void createAddByIndexMethod(final CollectionInfo fieldInfo,
            final JClass jClass) { ... )
 

Kawałek kodu:

 
            sourceCode.append(fieldInfo.getName());
            sourceCode.append(".add(index, ");
            sourceCode.append(fieldInfo.getContentType().
                    createToJavaObjectCode(parameter.getName()));
            sourceCode.append(");");
            void add(int index, Object element)
 

zamieniamy na:

 
            sourceCode.append(fieldInfo.getName());
            sourceCode.append(".insertElementAt(");
            sourceCode.append(fieldInfo.getContentType().
                    createToJavaObjectCode(parameter.getName()));
            sourceCode.append(", index);");
            void insertElementAt(Object obj, int index)
 

3.5 Usuwamy ciało metody:

 
            protected void createIteratorMethod(final CollectionInfo fieldInfo,
                final JClass jClass, final boolean useJava50) { ... }
 

A fragment implementacji metody:

 
            private void createRemoveAllMethod(final CollectionInfo fieldInfo,
                final JClass jClass) { ... }
            sourceCode.append(".clear();");
 

zamieniamy na sourceCode.append(".removeAllElements();");

Czyli void clear(); na void removeAllElements();

3.6 W nastepnej metodzie zmieniamy ciało:

 
            protected void createRemoveByIndexMethod(final CollectionInfo fieldInfo,
                    final JClass jClass) { ... }
 

Z:

 
            JMethod method = new JMethod("remove" + fieldInfo.getMethodSuffix() + "At",
                fieldInfo.getContentType().getJType(),
                "the element removed from the collection");
            method.addParameter(new JParameter(JType.INT, "index"));
            JSourceCode sourceCode = method.getSourceCode();
            sourceCode.add("java.lang.Object obj = this.");
            sourceCode.append(fieldInfo.getName());
            sourceCode.append(".remove(index);");
            if (fieldInfo.isBound()) this.createBoundPropertyCode(fieldInfo, sourceCode);
 
            sourceCode.add("return ");
            if (fieldInfo.getContentType().getType() == XSType.CLASS) {
                sourceCode.append("(");
                sourceCode.append(method.getReturnType().getName());
                sourceCode.append(") obj;");
            } else {
                sourceCode.append(fieldInfo.getContentType().createFromJavaObjectCode("obj"));
                sourceCode.append(";");
            }
            jClass.addMethod(method);
 

Na:

 
            JMethod method = new JMethod("remove" + fieldInfo.getMethodSuffix()
                    + "At", Jtype.VOID, "the element removed from the collection");
            method.addException(SGTypes.INDEX_OUT_OF_BOUNDS_EXCEPTION,
                "if the index given is outside the bounds of the collection");
            method.addParameter(new JParameter(JType.INT, "index"));
            JSourceCode sourceCode = method.getSourceCode();
            this.addIndexCheck(fieldInfo, sourceCode, method.getName());
            sourceCode.add("this.");
            sourceCode.append(fieldInfo.getName());
            sourceCode.append(".removeElementAt(index);");
            if (fieldInfo.isBound()) this.createBoundPropertyCode(fieldInfo, sourceCode);
            jClass.addMethod(method);
 

czyli po krótce Object remove(int index) na void removeElementAt(int index)

Metoda this.addIndexCheck(...) doklei do naszego kodu coś takiego:

 
            // check bounds for index
            if (index < 0 || index >= this.VECTOR .size()) {
                    throw new IndexOutOfBoundsException("getElement: Index value '" +
                            index + "' not in range [0.." +
                            (this.VECTOR.size() - 1) + "]");
            }
 

3.7 Kolejna zmiana dotyczy metody:

 
            private void createRemoveObjectMethod(final CollectionInfo fieldInfo,
                final JClass jClass) { ... }
 

Podmieniamy kod sourceCode.append(".remove("); na sourceCode.append(".removeElement(");

3.8 Usuwamy ciało metody

 
            private void createSetAsArrayMethod(final CollectionInfo fieldInfo,
                final JClass jClass, final boolean useJava50) { ... }
 

3.9 ostatnia zmiana będzie dotyczyła metody:

 
            protected void createSetByIndexMethod(final CollectionInfo fieldInfo,
                final JClass jClass) { ... }
 

Zamieniamy metodę Object set(int index, Object element) na void setElementAt(Object obj, int index) podmieniając kod:

 
            JMethod method = new JMethod("set" + fieldInfo.getMethodSuffix());
            method.addException(SGTypes.INDEX_OUT_OF_BOUNDS_EXCEPTION,
                "if the index given is outside the bounds of the collection");
            method.addParameter(new JParameter(JType.INT, "index"));
            method.addParameter(new Jparameter(fieldInfo.getContentType().getJType(),
                    fieldInfo.getContentName()));
            JSourceCode sourceCode = method.getSourceCode();
            this.addIndexCheck(fieldInfo, sourceCode, method.getName());
            sourceCode.add("this.");
            sourceCode.append(fieldInfo.getName());
            sourceCode.append(".set(index, ");
            sourceCode.append(fieldInfo.getContentType().createToJavaObjectCode(
                    fieldInfo.getContentName())
            );
            sourceCode.append(");");
            if (fieldInfo.isBound()) this.createBoundPropertyCode(fieldInfo, sourceCode);
            jClass.addMethod(method);
 

na:

 
            JMethod method = new JMethod("set" + fieldInfo.getMethodSuffix(), JType.VOID,
                "the element added to the collection");
            method.addException(SGTypes.INDEX_OUT_OF_BOUNDS_EXCEPTION,
                "if the index given is outside the bounds of the collection");
            method.addParameter(new JParameter(JType.INT, "index"));
            method.addParameter(new JParameter(fieldInfo.getContentType().getJType(),
            fieldInfo.getContentName()));
            JSourceCode sourceCode = method.getSourceCode();
            this.addIndexCheck(fieldInfo, sourceCode, method.getName());
            sourceCode.add("this.");
            sourceCode.append(fieldInfo.getName());
            sourceCode.append(".setElementAt(");
            sourceCode.append(fieldInfo.getContentType().createToJavaObjectCode(
                    fieldInfo.getContentName())
            );
            sourceCode.append(", index);");
            if (fieldInfo.isBound()) this.createBoundPropertyCode(fieldInfo, sourceCode);
            jClass.addMethod(method);
 

3.10 Krótkie wyjaśnienie

Spis klas, których możemy użyć do generowania kodu źródłowego można znaleźć tutaj:

http://www.castor.org/1.3/javadoc/org/exolab/javasource/package-summary.html

Szukamy klasy org.exolab.castor.builder.factory.Sourcefactory, a w niej metody private void initialize(final JClass jClass) { ... }. Musimy wykomentować tylko jedną linijkę jClass.addInterface("java.io.Serializable");

Nie chcemy aby ten interfejs domyślnie był doklejany do każdej nowo wygenerowanej klasy, ponieważ jak już wcześniej wspominałem interfejs ten nie istnieje w Java ME. Zamiast niego użyjemy de.enough.polish.io.Externalizable

..i to już koniec "podmianek" :)

Edytujemy plik pom.xml i zmieniamy nazwę artefaktu na castor-codegen i dodajemy wersję: <version>1.2.1</version>. Wydajemy polecenie mvn clean install. Artefakt zostanie zainstalowany w naszym lokalnym repozytorium w katalogu:

M2_REPO/repository/org/codehaus/castor/castor-codegen/1.2.1/

Tak stworzony artefakt dodajemy jako zależność w pliku pom.xml:

 
            <dependency>
                <groupId>org.codehaus.castor</groupId>
                <artifactId>castor-codegen</artifactId>
                <version>1.2.1</version>
            </dependency>
 

Jeżeli mamy zainstalowany plugin M2 to klikamy na projekcie prawym przyciskiem myszki i wybieramy Enable Dependency Managment. Wtedy wszystkie zależności, które dodaliśmy w pliku pom.xml znajdą się automatycznie w naszym classpath (rys. 2)

Rys. 2. Java Build Path

Teraz już projekt powinien się budować, a instalacja w lokalnym repozytorium musi zakończy się pomyślnie :)

4. Czas teraz przejść do implementacji metody:

 
            public void printClass(final JClass jClass, final String outputDir,
                final String lineSeparator, final String header) { ... }
 

która doda do wygenerowanej klasy (obiekt JClass) dodatkowy kod (metodę) modifiedClass.addMethod(someMethod) oraz np. jakiś komentarz modifiedClass.setHeader(someComment). Do wygenerowania mamy następujący kod:

 
            public void read(DataInputStream dis) throws IOException {
                    this.name = dis.readUTF();
                    this.myObject2 = (MyObject2)de.enough.polish.io.Serializer.deserialize(dis);
            }
 

Krok 1. Tworzymy sygnaturę metody:

 
            JMethod readMethod = new JMethod("read");
            JModifiers readMethodModifiers = new JModifiers();
            readMethodModifiers.makePublic();
            readMethod.setModifiers(readMethodModifiers);
            JParameter readMethoParameter = new JParameter(JType.DATA_INPUT_STREAM, "dis");
            readMethod.addParameter(readMethoParameter);
            readMethod.addException(new JClass("java.io.IOException"), "");
 

...zostawiam to bez komentarza :)

Krok 2. Tworzymy ciało metody:

 
            JField[] fields = modifiedClass.getFields();
                    if( fields.length > 0) {
                            for(JField field : fields) {
                                    readSourceCode.append(returnProperReadMethod(field));
                                    readSourceCode.append("\n");
                          }
                          readMethod.setSourceCode(readSourceCode.toString());
                    } else {
                          readMethod.setSourceCode("super.read(dis);");
                    }
 

...czyli iterujemy po wszystkich polach w klasie i tworzymy kod :)

Przykładowe implementacja metody returnProperReadMethod(...)

 
            private String returnProperReadMethod(JField field) {
                    final String name = field.getName();
                    final String type = field.getType().getName();
                    if(type.compareTo("java.lang.String") == 0) {
                            return "this." + name + " = dis.readUTF();";
                    } else if(type.compareTo("int") == 0
                            || type.compareTo("java.lang.Integer") == 0) {
                        return "this." + name + " = dis.readInt();";
                    } else if(type.compareTo("boolean") == 0
                            || type.compareTo("java.lang.Boolean") == 0) {
                        return "this." + name + " = dis.readBoolean();";
                    } else if(type.compareTo("java.util.Date") == 0) {
                        return "this." + name + " = new java.util.Date(dis.readLong());";
                    } else if(type.compareTo("double") == 0
                            || type.compareTo("java.lang.Double") == 0) {
                        return "this." + name + " = dis.readDouble();";
                    } else {
                        return "this." + name + " = (" + type +
                                ")Serializer.deserialize( dis );";
                    }
            }
 

tak stworzony kod dodajemy do naszej klasy modifiedClass.addMethod(readMethod);

Na koniec dodam, że należy pamiętać o dodaniu wpisu o imporcie klasy modifiedClass.addImport("de.enough.polish.io.Serializer") i implementacji metody write(...) !!!

Zagadka :) Czy projekt się zbuduje? :) Przeanalizujcie uważnie powyższe lintningi. Chodzi o linijkę:

 
            for(JField field : fields)
 

próba kompilacji zakończy się takim błędem (rys. 3):

Rys. 3

Na stronie Maven'a [7] możemy dowiedzieć się:

"...The default source setting is 1.3 and the default target setting is 1.1, independently of the JDK you run Maven with..."

Przechodzimy więc do konfiguracji wtyczki do Mavena, Compiler Plugin, edytujemy plik pom.xml i dodajemy:

 
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                            <configuration>
                                <source>1.5</source>
                                <target>1.5</target>
                            </configuration>
                    </plugin>
                </plugins>
            </build>
 

Należy pamiętać o właściwym ustawieniu w pliku castorbuilder.properties odpowiednich parametrów:

            org.exolab.castor.builder.javaVersion=1.4
            org.exolab.castor.builder.jclassPrinterTypes=\
                    com.sonic.gen.MySourceGenerator,\
                    org.exolab.castor.builder.printing.TemplateJClassPrinter
        

Nadszedł czas przetestowania naszej biblioteki, ale zanim to nastąpi, musimy mieć jakieś dane testowe. Musimy stworzyć przykładowy plik Schema i zaimplementować automat, który będzie nam generował gotowy kod. Zatem do dzieła:

Przechodzimy do projektu CastorMsgFactoryME i tworzymy katalog resources tu umieszczamy plik element.xsd, następnie tworzymy katalog types i tam umieszczamy plik sub-element.xsd (rys. 4). Przy edycji plików schema warto mieć zainstalowany XML Schema Editor w Eclipse [8]. Na kolejnych listingach przedstawię przykładową zawartość obu plików.

Rys. 4. Widok w Eclipse 3.5M3

 
            <?xml version="1.0" encoding="UTF-8"?>
            <xsd:schema attributeFormDefault="unqualified"
                elementFormDefault="qualified"
                version="1.0"
                xmlns="http://com.sonic/element"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:xmime="http://www.w3.org/2005/05/xmlmime"
                xmlns:es="http://com.sonic/types/complexTypes"
                targetNamespace="http://com.sonic/element">
              <xsd:import
                namespace="http://com.sonic/types/complexTypes"
                schemaLocation="types/sub-element.xsd"
              />
            <xsd:element name="MyElement">
              <xsd:complexType>
                <xsd:sequence>
                  <xsd:element maxOccurs="unbounded"
                    name="MySubElement"
                    type="es:SubElementType" />
                  </xsd:sequence>
                  <xsd:attribute name="attr1" type="xsd:string" />
                  <xsd:attribute name="attr2" type="xsd:int" />
                  <xsd:attribute name="attr3" type="xsd:double" />
                  <xsd:attribute name="attr4" type="xsd:dateTime" />
                </xsd:complexType>
              </xsd:element>
            </xsd:schema>
 
 
            <?xml version="1.0" encoding="UTF-8"?>
            <xsd:schema	attributeFormDefault="unqualified"
                elementFormDefault="qualified"
                version="1.0"
                xmlns="http://com.sonic/types/complexTypes"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:xmime="http://www.w3.org/2005/05/xmlmime"
                targetNamespace="http://com.sonic/types/complexTypes">
              <xsd:complexType name="SubElementType">
                <xsd:attribute name="attr1" type="xsd:int" />
                <xsd:attribute name="attr2" type="xsd:double" />
                </xsd:complexType>
            </xsd:schema>
 

Użyjemy klasy org.castor.anttask.CastorCodeGenTask

Tworzymy plik build.xml. W zasadzie możemy usunąć katalogi com/sonic/factory, bo nie będą nam potrzebne. Generowanie plików będziemy uruchamiąć korzystając z Maven'a. W fazie generowania źródeł <phase>generate-sources</phase> uruchomimy task w Ant przy pomocy wtyczki <artifactId>maven-antrun-plugin</artifactId> [9].

Przykładowa konfiguracja:

 
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.3</version>
                <executions>
                    <execution>
                        <id>generate-me-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <tasks>
                                <property name="compile_classpath"
                                        refid="maven.compile.classpath"/>
                                <ant antfile="build.xml" dir="${basedir}" />
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
 

Property o nazwie compile_classpath jest referencją do classpath w Maven. Wykorzystamy ją w pliku build.xml. Plugin ten uruchomi domyślny task z pliku build.xml

<project name="CastorMsgfactoryME" default="castor:gen:src" basedir="."> można to obejść definiując w tagu <ant> coś takiego: <target name="nazwa"/>

Generowanie kodu odbywać się będzie przy pomocy zdefiniownego taska:

 
            <taskdef name="castor-srcgen"
                     classname="org.castor.anttask.CastorCodeGenTask"
                     classpathref="castor.class.path" />
 

.. i wywołanie:

 
            <castor-srcgen file="src/main/resources/element.xsd"
                           todir="${src.dir}"
                           package="${package.name}"
                           warnings="true"
                           nodesc="true"
                           nomarshal="true"/>
 

gdzie:

 
            <property name="package.name" value="com.sonic.dto"/>
            <property name="src.dir" value="src/main/java"/>
            <path id="castor.class.path">
              <path path="${compile_classpath}"/>
            </path>
 

Domyślnie CastorCodeGenTask wygeneruje kolekcje zgodne z Java 1.1, jeżeli chcielibyśmy, z jakichkolwiek względów wymusić zgodność z Java 1.2 to należy dodać atrybut types a jego wartość ustawić na j2.

Atrybut nomarshal [10] ustawiliśmy na true aby poinformować generator, aby nie tworzył metod do marshalingu/unmarshalingu.

Na koniec przedstawię jeszcze Dependency Graph w obu projektach (rys. 5 i rys. 6):

Rys. 5. Widok: Maven POM XML Editor

Rys. 6. Widok: Maven POM XML Editor

Artefakty enough-j2mepolish-client.jar, midp.jar oraz cldc.jar znajdują się w katalogu /j2me-polish2.0.7/lib/

Przykład instalacji artefaktów to lokalnego repozytorium:

            mvn install:install-file -DgroupId=javax.midp -DartifactId=midp
                -Dversion=2.0 -Dpackaging=jar -Dfile=/path/to/file
        

Linki

[1] http://maven.apache.org/

[2] http://ant.apache.org/

[3] http://m2eclipse.codehaus.org/

[4] http://dist.codehaus.org/castor/1.2/castor-1.2-src.zip

[5] http://java.sun.com/javame/reference/apis/jsr139/

[6] http://java.sun.com/products/archive/jdk/1.1/index.html

[7] http://maven.apache.org/plugins/maven-compiler-plugin/

[8] http://wiki.eclipse.org/index.php/Introduction_to_the_XSD_Editor

[9] http://maven.apache.org/plugins/maven-antrun-plugin/plugin-info.html

[10] http://www.castor.org/srcgen-anttask.html

Nie ma jeszcze komentarzy.

Tylko zalogowani użytkowincy mogą pisać komentarze

Developers World