AutoFirma ins and outs (3). Errors and problems

0. Introduction

1. zip64

2. Fat jar and executable jar and the MANIFEST.MF is not alone in the META-INF directory

3. Project order, test failure. distTar and distZip duplicated dependencies

4. CommandLineLauncher for avoiding System.exit

5. PDF version from 1.4 to 1.7 (s/Jesus Romero) Including file Ian...

6. java.smartcardio dependency

7. Passing arguments to SimpleFirma.main() 

1. zip64

When a large number of libraries need to be included in a fat jar, the zip utility is overwhelmed, so you need to indicate zip64=true when defining a jar task o equivalent

jar {
    zip64=true
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    manifest {
.......

2. Fat jar/Uber jar, executable jar, and the MANIFEST.MF is not alone in the META-INF directory

If you want to create a fat jar (uber jar), you must include all the dependencies in the generated jar.

There are several options (when you want only a subproject to have a fat jar or if you want that every child project have its own fat jar) 

2.1 Creating only a fat jar in one of the subprojects.

In this case, in the subproject gradle.build file should include a jar task (or equivalent)  with a "from" clause that collects the jars indicated in the classpath. Note the highlighted code:

plugins {
    // Apply the java-library plugin to add support for Java Library
    id 'java-library'
    id 'java'
    id 'application' // For executable jars
    id 'jvm-test-suite' //Integration tests
}

// BEGIN: Integration TESTS ===============================
testing {
    suites {
        
        test { 
                    useJUnitJupiter() 
            }
    }
}

//END: Intgration TESTS ====================================

repositories {
    jcenter()
    mavenCentral()
    maven { url "https://jitpack.io" } //@see: https://stackoverflow.com/questions/38905939/how-to-import-library-from-jitpack-io-using-gradle
    flatDir {dirs "$rootDir/../mynewtargets2"} //@see https://stackoverflow.com/a/25966303
}

// My customization 
project.jar.destinationDirectory = file("$rootDir/../mynewtargets2")  
project.archivesBaseName = 'x04-if08-run' 
project.version = '4.0'

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation libs.junit.jupiter
        
    api libs.javax.xml.bind.jaxb.api  // Defined in the file setting.gradle from the parent pronect
    .... // Other libraries
    

    api(project(":A00-Basic")) // Depends on project A00-Basic
    api(project("..........")) // Depends on other projects
    //=========================================================================================
    //3. Add by hand the references to the shadow plugin projects that are needed
    /*
    implementation files(
        "$rootDir/../mynewtargets2/g01-ws-shdw-0.1.jar",
        .....
    )
    */
}

jar {
    zip64=true
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    manifest {
        attributes(
             'Main-Class': 'com.foo.bar.MainClass'  //Only for executable jar
             //'Class-Path': configurations.runtimeClasspath.files.collect { 'lib/'+it.getName() }.join(' ') // DOES NOT WORK!!!
        ) 
    }
    
    from {
           sourceSets.main.allSource  //Include java sources
           configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
       }
}

distTar{ // When complains about duplicates
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
distZip{ // When complains about duplicates
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}    

// BEGIN Unit tests ===================================
/*
test {
    useJUnitPlatform()
}
*/
// END Unit tests =====================================


application { // Only for executable jars
    //mainClass = 'actions.TestWS' 
}

In this case, you can create a task in the build.gradle file of the parent project. Then you can execute this task in the terminal window (./gradlew uberJar)

Here is a sample of the build.gradle file of the parent project

group 'com.ximodante'

//Define these common elements that are available to all the subproject files
// Now you only need to define the dependencies of every project and this way
// you simplify your code
subprojects { 
    apply plugin: 'eclipse'
    apply plugin: 'java'
    apply plugin: 'java-library'
    
    project.jar.destinationDirectory = file("$rootDir/../mynewtargets2")  
    //project.archivesBaseName = "$name" 
    project.version = '1.7' 
    
    eclipse { //Avoids java.smartcardio complaint
        classpath {
            file {
                whenMerged {
                    def jre = entries.find { it.path.contains 'org.eclipse.jdt.launching.JRE_CONTAINER' }
                    jre.entryAttributes['module'] = 'true'
                    jre.entryAttributes['limit-modules'] = 'java.se,jdk.accessibility,jdk.dynalink,jdk.httpserver,jdk.jartool,jdk.javadoc,jdk.jconsole,jdk.jshell,jdk.jsobject,jdk.management.jfr,jdk.net,jdk.nio.mapmode,jdk.sctp,jdk.security.auth,jdk.security.jgss,jdk.unsupported,jdk.unsupported.desktop,jdk.xml.dom,java.smartcardio'
                    jre.entryAttributes['add-modules'] = 'java.smartcardio'           //--limit-modules java.se --add-modules java.smartcardio
                }
            }
        }
    }
    
    //Define a task for creating a uber jar foer every project
    // Just run ./gradlew uberJar in the parent project 
    tasks.register('uberJar', Jar) {
        destinationDirectory = file("$rootDir/../mynewtargets2")  
        zip64=true
        archiveClassifier = 'uber1'
        duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    
    
        manifest.attributes["Main-Class"] = "es.gob.afirma.standalone.SimpleAfirma"
    

        from sourceSets.main.output
        from sourceSets.main.allSource

        dependsOn configurations.runtimeClasspath
        from {
                // This clause makes a fat jar!!!!
            configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) }
        }
    }
    
}

3. Project order, test failure. distTar and distZip duplicated dependencies

Sometimes, a project needs some dependencies that are not available. In this case, the subproject AutofirmaSimple needs some jars that are not available, but some sibling projects can provide the needed dependencies. So there is no way to build this project. So you need to exclude this project from the parent project in the settings.gradle (You only need to comment the reference to this project in the include section) and build the parent project, then you can reinclude the project in the settings.gradle and build it.

Sometimes gradle complaints about duplicate dependencies in the distTar and distZip tasks. So set duplicateStrategy to exclude as seen in a previous build.gradle

4. CommandLineLauncher for avoiding System.exit

If you don't want to exit the program, comment the lines with System.exit in the CommandLineLauncher class of the afirma-simple project. Otherwise when you call a method from a class (for instance SimpleFirma from afirma-simple project) the execution is broken !!!


5. Convert PDF version 1.4 to 1.7  (PDFA)

Here is a java class for converting from PDF v.1.4 to v.1.7 (PDFA). Note that you should download the file "sRGB_Color_Space_Profile.icm" from ETDA_Github 

Here is the class

package openadmin.utils.decrets;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.transform.TransformerException;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.DublinCoreSchema;
import org.apache.xmpbox.schema.PDFAIdentificationSchema;
import org.apache.xmpbox.type.BadFieldValueException;
import org.apache.xmpbox.xml.XmpSerializer;

import ximodante.basic.utils.basic.ExecutionTypeEnum;
import ximodante.basic.utils.basic.FileUtilsEdu;

public class PDF_AUtils {
    // Convert a PDF 1.4 to 1.7 (PDF/A) s/Jesús Romero
    public static PDDocument setVersionPDFA(PDDocument doc, String fileName, ExecutionTypeEnum execType) {
        // add XMP metadata
        XMPMetadata xmp = XMPMetadata.createXMPMetadata();
        try {
            DublinCoreSchema dc = xmp.createAndAddDublinCoreSchema();
            dc.setTitle(fileName);

            PDFAIdentificationSchema id = xmp.createAndAddPFAIdentificationSchema();
            id.setPart(1);
            id.setConformance("B");

            XmpSerializer serializer = new XmpSerializer();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            serializer.serialize(xmp, baos, true);

            PDMetadata metadata = new PDMetadata(doc);
            metadata.importXMPMetadata(baos.toByteArray());
            doc.getDocumentCatalog().setMetadata(metadata);
            
            // sRGB output intent
            //InputStream colorProfile = PDF_AUtils.class.getResourceAsStream("/org/apache/pdfbox/resources/pdfa/sRGB.icc");
            String fileICM=FileUtilsEdu.getRelativeResourceFile(execType, "icm"+File.separator+ "sRGB_Color_Space_Profile.icm");
            InputStream colorProfile = new  FileInputStream(fileICM);
            PDOutputIntent intent;
            intent = new PDOutputIntent(doc, colorProfile);
            intent.setInfo("sRGB IEC61966-2.1");
            intent.setOutputCondition("sRGB IEC61966-2.1");
            intent.setOutputConditionIdentifier("sRGB IEC61966-2.1");
            intent.setRegistryName("http://www.color.org");
            doc.getDocumentCatalog().addOutputIntent(intent);

        } catch (BadFieldValueException e) {
            // won't happen here, as the provided value is valid
            throw new IllegalArgumentException(e);
        } catch (TransformerException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return doc;

        
    }

    public static void main(String[] args) {
        

    }

}


6. java.smartcardio dependency

This error can be solved as mentioned in StackOverflow. In the 2.2 section of this post, you can see the eclipse plugin and the eclipse task for solving this problem

7. Passing arguments to SimpleFirma.main() 

Thesde are the arguments to pass:

sign -i /home/eduard/prova_decrets/V1/2021/ES_L01462384_2021_DOC_RESOLUCIO_0001.pdf -o /home/eduard/prova_decrets/V1/2021/ES_L01462384_2021_DOC_RESOLUCIO_0001_FIRMA_XADES_PROVA_CAGADA_EDU20230119.xsig -format xades -store pkcs12:/home/eduard/WORKSPACES/WS_GRDL01/Ajuntament/X04-IF08/src/main/resources/certs/MY_CERT.ep_1650458863958.p12 -alias epn1 -password MY_PASSWORD -config "format=XAdES Detached"

Comments

Popular posts from this blog

ORVE WS (Dynamic) (4) Jackson XML mapper

ENI (1) ENI Document OR the Spanish Electronic Administration Mafia

ORVE WS (Dynamic) (12) Defiining the control tables