java-8java-ee-7jpa-2.1nashorn

CommandLine nashorn script (jjs) unable to create entity manager. Why?


CommandLine nashorn script (jjs) unable to create entity manager.

i.e.,

running the script looks like this...

    $ jjs -cp ".;myjpaclasses-1.jar;" myNashornScript.js

i.e., where "myNashornScript.js" contains...

    /* global Java, java */
    print("begin test...");
    var EntityManagerFactory = Java.type('javax.persistence.EntityManagerFactory');
    var EntityManager = Java.type('javax.persistence.EntityManager');
    var Persistence = Java.type('javax.persistence.Persistence');
    var Employees = Java.type('aaa.bbb.ccc.jpa.Employees');
    var employees = new Employees();
    var javaImports = new JavaImporter(java.io, java.lang, java.util);

    try
    {
        with (javaImports) {

            var emf = Persistence.createEntityManagerFactory("hr_pu"); <== issue here(?)...
            var em = emf.createEntityManager();

            var query = em.createQuery(
                    "SELECT e FROM Employees e WHERE e.employeeId > ?1")
                    .setParameter(1, 100)
                    .setFirstResult(0);

            var rows = query.getResultList();

            //...print info on 2nd row object of returned list...
            //...print returned list size...
            print("rows.get(2).getFirstName()="+ rows.get(2).getFirstName()
                + "...returned row count=" + rows.size());
        }
    } catch (e) {
        print(e.message);
    }
    print("end test...");

Running script from command line consistently yields the following...

    begin test...
    No Persistence provider for EntityManager named hr_pu
    end test...

Note: fwiw, this script seems to work fine, when called from a java app...i.e.,

    package aaa.bbb.ccc.jar;

    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptException;

    public class RunScript {
        public static void main (String[] args) throws ScriptException, FileNotFoundException
        {
            ScriptEngineManager engineManager = new ScriptEngineManager();
            ScriptEngine engine = engineManager.getEngineByName("nashorn");
            engine.eval(new FileReader("src/main/resources/myNashornScript.js"));        
        }
    }

...i.e., which yields...

    begin test...
    [EL Warning]: transaction: 2016-05-12 14:18:00.773--ServerSession(1829217853)--PersistenceUnitInfo hr_pu has transactionType RESOURCE_LOCAL and therefore jtaDataSource will be ignored
    [EL Info]: 2016-05-12 14:18:00.78--ServerSession(1829217853)--EclipseLink, version: Eclipse Persistence Services - 2.6.3.v20160428-59c81c5
    [EL Info]: connection: 2016-05-12 14:18:01.183--ServerSession(1829217853)--/file:/C:/tools/netbeansWS/myjpaclasses/target/classes/_hr_pu login successful
    rows.get(2).getFirstName()=Alexander...returned row count=106
    end test...

P.S.

If it makes any difference, the persistence.xml (located in src/main/resources/META-INF of "myjpaclasses.jar") looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
        <persistence-unit name="hr_pu" transaction-type="RESOURCE_LOCAL">      
            <jta-data-source>jdbc/HR</jta-data-source>
            <class>aaa.bbb.ccc.jpa.Regions</class>
            <class>aaa.bbb.ccc.jpa.Employees</class>
            <class>aaa.bbb.ccc.jpa.Departments</class>
            <class>aaa.bbb.ccc.jpa.Locations</class>
            <class>aaa.bbb.ccc.jpa.Jobs</class>
            <class>aaa.bbb.ccc.jpa.Countries</class>
            <exclude-unlisted-classes>false</exclude-unlisted-classes>
            <properties>
                <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
                <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
                <property name="javax.persistence.jdbc.user" value="HR"/>
                <property name="javax.persistence.jdbc.password" value="HR"/>
            </properties>
        </persistence-unit>
    </persistence>

The JPA "Employee" class looks like this:

    package aaa.bbb.ccc.jpa;

    import java.io.Serializable;
    import java.math.BigDecimal;
    import java.util.Date;
    import java.util.List;
    import javax.persistence.Basic;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    import javax.persistence.Temporal;
    import javax.persistence.TemporalType;
    import javax.persistence.UniqueConstraint;
    import javax.xml.bind.annotation.*;

    @XmlAccessorType(XmlAccessType.FIELD)
    @Entity
    @Table(name = "EMPLOYEES", uniqueConstraints = @UniqueConstraint(columnNames = {"EMAIL"}))

    @XmlRootElement
    public class Employees implements Serializable {

        @Column(name = "LAST_NAME", table = "EMPLOYEES", nullable = false, length = 25)
        @Basic
        private String lastName;

        @Column(name = "HIRE_DATE", table = "EMPLOYEES", nullable = false)
        @Temporal(TemporalType.TIMESTAMP)
        @Basic
        private Date hireDate;

        @ManyToOne(targetEntity = Departments.class)
        @JoinColumn(name = "DEPARTMENT_ID", referencedColumnName = "DEPARTMENT_ID")
        private Departments departmentId;

        @Column(name = "EMPLOYEE_ID", table = "EMPLOYEES", nullable = false)
        @Id
        private Integer employeeId;

        @ManyToOne(targetEntity = Employees.class)
        @JoinColumn(name = "MANAGER_ID", referencedColumnName = "EMPLOYEE_ID")
        private Employees managerId;

        @Column(name = "SALARY", table = "EMPLOYEES", scale = 2, precision = 8)
        @Basic
        private BigDecimal salary;

        @Column(name = "COMMISSION_PCT", table = "EMPLOYEES", scale = 2, precision = 2)
        @Basic
        private BigDecimal commissionPct;

        @XmlTransient
        @OneToMany(targetEntity = Employees.class, mappedBy = "managerId")
        private List<Employees> employeesCollection;

        @Column(name = "FIRST_NAME", table = "EMPLOYEES", length = 20)
        @Basic
        private String firstName;

        @ManyToOne(optional = false, targetEntity = Jobs.class)
        @JoinColumn(name = "JOB_ID", referencedColumnName = "JOB_ID")
        private Jobs jobId;

        @Column(name = "PHONE_NUMBER", table = "EMPLOYEES", length = 20)
        @Basic
        private String phoneNumber;

        @XmlTransient
        @OneToMany(targetEntity = Departments.class, mappedBy = "managerId")
        private List<Departments> departmentsCollection;

        @Column(name = "EMAIL", table = "EMPLOYEES", nullable = false, length = 25)
        @Basic
        private String email;

        public Employees() {

        }

        public String getLastName() {
            return this.lastName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }

        public Date getHireDate() {
            return this.hireDate;
        }

        public void setHireDate(Date hireDate) {
            this.hireDate = hireDate;
        }

        public Departments getDepartmentId() {
            return this.departmentId;
        }

        public void setDepartmentId(Departments departmentId) {
            this.departmentId = departmentId;
        }

        public Integer getEmployeeId() {
            return this.employeeId;
        }

        public void setEmployeeId(Integer employeeId) {
            this.employeeId = employeeId;
        }

        public Employees getManagerId() {
            return this.managerId;
        }

        public void setManagerId(Employees managerId) {
            this.managerId = managerId;
        }

        public BigDecimal getSalary() {
            return this.salary;
        }

        public void setSalary(BigDecimal salary) {
            this.salary = salary;
        }

        public BigDecimal getCommissionPct() {
            return this.commissionPct;
        }

        public void setCommissionPct(BigDecimal commissionPct) {
            this.commissionPct = commissionPct;
        }

        @XmlTransient
        public List<Employees> getEmployeesCollection() {
            return this.employeesCollection;
        }

        public void setEmployeesCollection(List<Employees> employeesCollection) {
            this.employeesCollection = employeesCollection;
        }

        public String getFirstName() {
            return this.firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public Jobs getJobId() {
            return this.jobId;
        }

        public void setJobId(Jobs jobId) {
            this.jobId = jobId;
        }

        public String getPhoneNumber() {
            return this.phoneNumber;
        }

        public void setPhoneNumber(String phoneNumber) {
            this.phoneNumber = phoneNumber;
        }

        @XmlTransient
        public List<Departments> getDepartmentsCollection() {
            return this.departmentsCollection;
        }

        public void setDepartmentsCollection(List<Departments> departmentsCollection) {
            this.departmentsCollection = departmentsCollection;
        }

        public String getEmail() {
            return this.email;
        }

        public void setEmail(String email) {
            this.email = email;
        }
    }

pom.xml that used to create "myjpaclasses-1.jar"...

    <?xml version="1.0" encoding="UTF-8"?>
    <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/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>aaa.bbb.ccc.jar</groupId>
        <artifactId>myjpaclasses</artifactId>
        <version>1</version>
        <packaging>jar</packaging>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.test.skip>false</maven.test.skip>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <timestamp>${maven.build.timestamp}</timestamp>
            <maven.build.timestamp.format>yyyyMMdd.HHmmss</maven.build.timestamp.format>
        </properties>

        <dependencies>

            <dependency>
                <groupId>org.eclipse.persistence</groupId>
                <artifactId>eclipselink</artifactId>
                <version>2.6.3</version>
            </dependency>

            <dependency>
                <groupId>javax</groupId>
                <artifactId>javaee-web-api</artifactId>
                <version>7.0</version>
                <scope>provided</scope>
            </dependency>

            <dependency>
                <groupId>com.oracle</groupId>
                <artifactId>ojdbc7</artifactId>
                <version>12.1.0.2</version>
            </dependency>

        </dependencies>

        <build>

            <!-- use for snapshot versioning <finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName> -->
            <finalName>${project.artifactId}-${project.version}</finalName>

            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <showDeprecation>true</showDeprecation>
                    </configuration>
                </plugin>

                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>2.4.3</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <artifactSet>
                                    <excludes>
                                        <exclude>junit:junit</exclude>
                                    </excludes>
                                </artifactSet>                          
                            </configuration>
                        </execution>
                    </executions>
                </plugin> 

            </plugins>
        </build>
        <name>myjpaclasses</name>
    </project>

Solution

  • It is likely that EntityManager / JPA library requires that persistence library class loader be the thread context class loader. With jjs -cp option, a fresh class loader is created and that is not the thread context class loader. With "java -cp", the application class loader is set as the thread context loader during initialization.

    You may want to try the following in your .js script:

    var EntityManagerFactory = Java.type('javax.persistence.EntityManagerFactory');
    // set the thread context class to be the loader of EntityManagerFactory class
    var cls = EntityManagerFactory.class;
    java.lang.Thread.currentThread().contextClassLoader = cls.classLoader;
    //... rest of your script..
    

    Please let me know if this works.