a Java EE Web Framework to develop java ee web apps fast.
jax-rs,CDI,EJB,JPA2
- On Early development!
- Update README.md with examples/tutorials(Jax-rs,Ejb,Master-Details)
- Master Detail functionality
- Search on details functionality
- Basic junit tests/iTtests
- Create on details (1t1,1tM,MtM)
- Edit/Delete on details
- Support xml.bind(JAXB) for JavaEE 7 and Jsonb(JSR 367) for JavaEE 8
- IT Test CRUD Master/Details
- Test @Transform/TransformRelation
- Generic Base Search with Criteria Api
- Create appropriate exception classes
- Run/Test against java EE various implementations
- Support Hibernate and Eclipselink
- Support Spring
- Documentation/api
- Publish on maven central
Maven
<dependency>
<groupId>com.protectsoft</groupId>
<artifactId>apiee</artifactId>
<version>0.3</version>
</dependency>
- The following code examples are from my draft/working version witch
- Also these samples are ment to show what the framework is about
and what it is already doing.
- See Sample project (apieeweb) witch contains all the examples
- This framework is NOT completed yet! It's in Working progress
- Your StartUp class must extend BaseConfig for provider registration
for example
@ApplicationPath("api")
public class Startup extends BaseConfig {
public Startup() {
packages("you.package.endpoints;");
}
}
A vert simple and minimalistic entity class and its relations.
This entity has some properties the framework provides which is
- The implementation of the Transform interface
- The TransformBean annotation, which is used by the MessageBodyReader Provider(for Jax-rs discovery)
- will be used later in the examples.
@Entity
@XmlRootElement
@Table(name = "DEPARTMENTS")
@NamedQuery(name = "Department.findAll", query = "SELECT d FROM Department d")
public class Department extends BaseEntityAUTO implements Transform {
//Ok this is a foreign key showing to the same table
//so every oranization has a parent organization
@JoinColumn(name = "ORGANIZATION_ID", referencedColumnName = "ID")
@ManyToOne(fetch = FetchType.LAZY)
//by using this annotation and implementing Transform interface
//The Provider will try to convert the id and set organization
@TransformBean
private Organization organization;
//and one organization has many child organizations
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<Department> childs;
//A one to one relationship
@OneToOne(mappedBy = "department")
private DepartmentInfo departmentInfo;
//Many to Many relationship
//One dept contains many emps, but also one emp can work one many deps.
@ManyToMany
@JoinTable(name = "DEP_EMPS",
joinColumns=@JoinColumn(name="DEPARTMENT_ID",referencedColumnName="ID"),
inverseJoinColumns=@JoinColumn(name="EMPLOYEE_ID",referencedColumnName="ID"))
private List<Employee> employees;
//And an Embedded field
//embDept contains a simple department description and tha parent department relation.
@Embedded
@AttributeOverrides({
@AttributeOverride(name="description", column=@Column(name="DESCRIPTION")),
})
@AssociationOverrides({
@AssociationOverride(name = "parent",
joinColumns = @JoinColumn(name = "PARENT_ID",referencedColumnName = "ID"))
})
private EmbeddableDept embDept;
public OrganizationUnit() {
}
...
setters
getters
...
//prevent mapping of javabean to xml
@XmlTransient
@JsonbTransient
public List<Employee> getEmployees() {
return employees;
}
//We dont need tha whole organization object
//Just the id
@XmlElement(name = "organization")
@JsonbProperty("organization")
public Long getOrganizationId() {
return organization.getId();
}
}
A simple ejb service for our Department that extending Api abstract class
All the ejb's extending Api<? extends BaseEntity> have the following functionallity
- Master-Detail CRUD Operations
- read range
- validation
- move/transfer
- master-Detail search
- javax.persistence.Embedded support for serialization/desiarization
@Stateless
public class DepartmentFacade extends Api<Department> {
public DepartmentFacade() {
super(Department.class);
}
//We are registering the relations and the appropriate functions
@Inject
public DepartmentFacade(DepartmentFacade childService,EmployeeFacade empService,DepartmentInfoFacade infoService) {
this();
//one to many
//one department has many sub departments
super.addChildDetail(Department.class,Department.class,new OneToManyFunction<Department,Department>() {
@Override
public List<Department> getDetails(Department master) {
return master.getChilds();
}
@Override
public void setMaster(Department master,Department child) {
child.getEmbDept().setParent(master);
}
},childService, MoveOption.ORPHANS_ALLOWED);
//many to many
//one department has many employees
//but also one employee can work on many departments
super.addChildDetail(Department.class, Employee.class,new ManyToManyFunction<Department,Employee>() {
@Override
public List<Employee> getDetails(Department master) {
return master.getEmployees();
}
@Override
public void addMaster(Department master, Employee detail) {
detail.getDepartments().add(master);
}
}, empService, MoveOption.ORPHANS_ALLOWED);
//One To One
//one Department has One Departnemt Information
super.addChildDetail(Department.class,DepartmentInfo.class,new OneToOneFunction<Department,DepartmentInfo>(){
@Override
public DepartmentInfo getDetail(Department master) {
return master.getDepartmentInfo();
}
@Override
public void setDetail(Department master, DepartmentInfo detail){
master.setDepartmentInfo(detail);
}
@Override
public void setMaster(Department master, DepartmentInfo detail) {
detail.setDepartment(master);
}
}, infoService, MoveOption.ORPHANS_NOT_ALLOWED);
}
}
A simple jax-rs endpoint for our organization service
every endpoint extending ApiResource have access to the following functionality
- All CRUD by default
- Details CRUD (SubResources) are scoped based on the master
- Search
By extending ApiResource<? extends baseEntity> we have all CRUD operations by default for
out entity
@Path("departments")
public class DepartmentResource extends ApiResource<Department> {
@Inject
public DepartmentResource(DepartmentFacade service) {
super(service);
}
@Path("{id}/departments")
public ApiSubResource subResource(@PathParam("id") Long id) {
return new SubResource(id,getService());
}
@Path("{id}/employees")
public ApiSubResource subResource2(@PathParam("id") Long id) {
return new SubResource2(id,getService());
}
@Path("{id}/departmentInfo")
public ApiOneToOneSubResource subResource3(@PathParam("id") Long id) {
return new SubResource3(id,getService());
}
public class SubResource3 extends ApiOneToOneSubResource<Department,DepartmentInfo> {
public SubResource3(Long id,Api<Department> service) {
super(id,service,DepartmentInfo.class);
}
}
public class SubResource2 extends ApiSubResource<Department,Employee> {
public SubResource2(Long id,Api<Department> service) {
super(id,service,Employee.class);
}
}
public class SubResource extends ApiSubResource<Department,Department> {
public SubResource(Long id,Api<Department> service) {
super(id,service,Department.class);
}
@Path("{id}/departments")
public ApiSubResource subResource(@PathParam("id") Long id) {
return new SubResource(id,getService());
}
}
}
And lets see what we can do with this.
GET departments/
-> will return a json list with all department objects.
Create a department.(lets suppose we have an organization with id 1 And a parent department with id 2)
POST Request in departments/
with body
{
"organization": 1,
"embDept": {
"description": "Human R Data And Finan. SUB_department",
"parent": 2
}
}
we have the following response if the object is created successfully
{
"type": "department",
"id": 1,
"organization": 1,
"embDept": {
"description": "Human R Data And Finan. SUB_department",
"parent": 2
}
}
GET departments/2/departments
-> will return list with all Sub departments of department 2.
same with PUT But we must specify the id in url
ex. departments/1
{
"type": "department",
"id": 1,
"description": "Human And Resources DEPT 01",
"organization": 1
}
DELETE request in departments/1
Now lets Create an Employee and Add him to the previous department we just created.
If we call GET at departments/1/employees
-> we get an empty list simple because no employee is working under this one.
So we need to create an Employee update department with working emps and
update employee with working deps
We can do that we a POST at departments/1/employees
and body data.
{
"firstName": "John",
"lastName": "Appleyard"
}
a GET at departments/1/employees
And we get the a list with the employee with just created.
Now lets suppose we want a department for the SubDepartment.
We can do that following the same logic.
A POST at departments/2/departments/1/departments
and body data. without specifying any parent department.
We also specifying the id of the Organization this department belongs
{
"embDept": {
"description": "Human R Data SUB_SUB"
},
"organization": 2
}
And we get the following response.
{
"type": "department",
"id": 77,
"embDept": {
"description": "Human R Data SUB",
"parent": 1
},
"organization": 2
}
Move a child Department
Lets move the department with id=3 from parent 2 to parent 1
We simply make a POST at departments/1/departments/2/departments/3?moveTo=1
SEARCH
Not supported yet.
We can search with sort/filtering both the master and as well the details
All endpoints have search capabilities.
A simple search is done with a POST following a search endpoint
eg. departments/search
with body data
{
"limit":2,
"offset":0,
"description":"Huma"
}
more advanced search can contain
{
"limit":2,
"offset":0,
"sort":[
"+description"
]
}
- on development
A easier way to access and lookup ejb's.
A wrapper around Oracle's recommend way (http://www.oracle.com/technetwork/java/servicelocator-137181.html
)
See package for on going workcom.protectsoft.apiee.api.control
that was a fast showcase of the framework.
It need's more work and more to detail documentation.
I will update it when i will have free time.