CommandLine nashorn script (jjs) unable to create entity manager.
Why does this not work?
How can it be made to work (if at all)?
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>
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.