Вы находитесь на странице: 1из 53

ADF Essentials Tips -n- Tricks

Oracle OpenWorld 2014 - UGF9377

Alexis Lopez
Consultant, Iteria SAS
@aa_lopez
aalopez@gmail.com
www.java-n-me.com

Agenda

What is ADF - What is ADF Essentials?

Best Practice

Configuring MySQL for ADF development

Configuring your ADF projects for ADF Essentials

Working with history columns and the phantom error

Using MySQL AI columns

No Security in ADF Essentials?

Using the same datasource name for Weblogic (Integrated) and Glassfish

Managing the Glassfish server from inside JDeveloper

What is ADF?

Model-View-Controller Framework

Build on top of Java EE

11g JavaEE 5

12c JavaEE 6

Productive Application Development

Visual and Declarative

Rich web, mobile and Desktop UI

Advanced Page Flows (Controller)

Integrated Security

IDE Support: JDeveloper and Eclipse (OEPE)

ADF Home Page


http://www.oracle.com/technetwork/developer-tools/adf/overview/index.html

What is ADF?
Architecture

ADF Home Page


http://www.oracle.com/technetwork/developer-tools/adf/overview/index.html

What is ADF Essentials?

Subset of key technologies from ADF

ADF Faces

ADF DvT

ADF Controller

ADF Binding

ADF Business Components

Free to develop Free to deploy

JDeveloper/Eclipse

MySQL

Glassfish and other app servers

ADF Essentials Home Page

http://www.oracle.com/technetwork/developer-tools/adf/overview/adfessentials-1719844.html

Provide your own base classes


Best Practice

When you are working with Business


Components, you are extending Oracle ADF base
classes, the most common base classes are:

oracle.jbo.server.EntityImpl is used for your entities


implementations

oracle.jbo.server.ViewObjectImpl and
oracle.jbo.server.ViewRowImpl are used for your view
object implementations

oracle.jbo.server.ApplicationModuleImpl is used for


your application module implementations

What happens if after a few months you need


some new behavior in all your entities?

More details on:


http://www.java-n-me.com/2013/11/adf-good-practice-provide-your-own-base.html

Provide your own base classes


Best Practice 1. Create a custom app for your base classes

More details on:


http://www.java-n-me.com/2013/11/adf-good-practice-provide-your-own-base.html

Provide your own base classes


Best Practice 2. Add one custom class per standard class

Configure your project for BC4J

Add your own base classes for:


oracle.jbo.server.EntityImpl
oracle.jbo.server.ViewObjectImpl
oracle.jbo.server.ViewRowImpl
oracle.jbo.server.ApplicationModuleImpl
More details on:
http://www.java-n-me.com/2013/11/adf-good-practice-provide-your-own-base.html

Provide your own base classes


Best Practice 3. Deploy your custom app as a ADF library

More details on:


http://www.java-n-me.com/2013/11/adf-good-practice-provide-your-own-base.html

Provide your own base classes


Best Practice 3. Import your ADF library

More details on:


http://www.java-n-me.com/2013/11/adf-good-practice-provide-your-own-base.html

Provide your own base classes


Best Practice 3. Configure Jdeveloper to use our base classes

Tell JDeveloper that it needs to


use our base classes instead
of the Oracle ADF base
classes.

At project level, so we need to


configure every project where we
want to use our base classes.

At JDeveloper level so every


single project will make use of
our base classes.

More details on:


http://www.java-n-me.com/2013/11/adf-good-practice-provide-your-own-base.html

Configuring MySQL for ADF Development


What version should I install?

Install MySQL 5.6.4 or higher

From this version, MySQL stores fractional seconds


(milliseconds) into columns of any temporal data
type.

InnoDB is the default storage engine. The benefits of


this are: ACID Transactions, Referential Integrity, and
Crash Recovery.

More details on:


http://www.java-n-me.com/2013/11/configuring-mysql-for-adf-development.html

Configuring MySQL for ADF Development


What about the JDBC driver?

JDev 12c mysql-connector-java-commercial-5.1.22

Download MySQL Connector/J

http://dev.mysql.com/downloads/connector/j/

The official JDBC driver for MySQL

Open source

You get the latest version (mysql-connector-java-5.1.32)

More details on:


http://www.java-n-me.com/2013/11/configuring-mysql-for-adf-development.html

Configuring MySQL for ADF Development


Add MySQL Connector/J to your library
1

2
3

More details on:


http://www.java-n-me.com/2013/11/configuring-mysql-for-adf-development.html

Configuring MySQL for ADF Development


Tips

Make sure you define a password for the user you will use to access MySQL otherwise
JDeveloper will fail to connect.
Configure sql-mode in my.cnf file (restart required)

From MySQL Manual: Server SQL modes define what SQL syntax MySQL should support and
what kind of data validation checks it should perform. This makes it easier to use MySQL in
different environments and to use MySQL together with other database servers...

Use special mode: ORACLE

Equivalent to PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS,


NO_TABLE_OPTIONS, NO_FIELD_OPTIONS

If MySQL is in the cloud, look for a parameter configuration section.

In Amazon RDS you can use DB Parameter Groups where you can define configurations for your
DB instance.

More details on:


http://www.java-n-me.com/2013/11/configuring-mysql-for-adf-development.html

Configuring MySQL for ADF Development


SQL Developer and MySQL

More details on:


http://www.java-n-me.com/2013/11/configuring-mysql-for-adf-development.html

Configuring your ADF projects for ADF Essentials


Initial configuration

When creating your Business


Components Project...

Make sure to select SQL


Platform as SQL92 (which is
the generic 1992 SQL standard)

For the Data Type Map select


Java.

More details on:


http://www.java-n-me.com/2013/11/configuring-mysql-for-adf-development.html

Configuring your ADF projects for ADF Essentials


Initial configuration

Make sure that your MySQL


library has been added to your
project

Using old MySQL JDBC drivers


may introduce bugs to your
development.

For example, not sending milliseconds


information to a DATETIME column.
Old drivers don't send milliseconds

New drivers do send milliseconds

More details on:


http://www.java-n-me.com/2014/02/adf-entity-track-change-history.html

Configuring your ADF projects for ADF Essentials


Considerations

Change your application


module
jbo.sql92.DbTimeQuery
property query expression:

Default is:
select sysdate from dual

Change to:
select NOW() from dual
Or use any of the
MySQL Date Functions

More details on:


http://www.java-n-me.com/2014/02/adf-entity-track-change-history.html

Configuring your ADF projects for ADF Essentials


Considerations

Not changing the


jbo.sql92.DbTimeQuery
property query expression
will result on an error at
runtime every time that you
ask for the current time, for
example, when using the
Track Change Columns

More details on:


http://www.java-n-me.com/2014/02/adf-entity-track-change-history.html

Working with history columns and the phantom error


The Case

Assume the following table exists


in database:

Entity for the Player table:

CREATE TABLE Player (


idPlayer INT NOT NULL AUTO_INCREMENT,
name VARCHAR(150) NOT NULL,
age INT NOT NULL ,
created_by VARCHAR(50) NULL,
created_on DATETIME NULL,
modified_by VARCHAR(50) NULL,
modified_on DATETIME NULL,
PRIMARY KEY (idPlayer)
);

More details on:


http://www.java-n-me.com/2014/02/adf-entity-track-change-history.html

Working with history columns and the phantom error


The Case
Configure your Track Change History columns at Entity level: CreatedBy,
CreatedOn, ModifiedBy, ModifiedOn:

More details on:


http://www.java-n-me.com/2014/02/adf-entity-track-change-history.html

Working with history columns and the phantom error


The Case

Follow these steps:

Run your AM

Create a Player

Commit

Try to modify the


age field and
commit again

More details on:


http://www.java-n-me.com/2014/02/adf-entity-track-change-history.html

Working with history columns and the phantom error


The Case
This is what the framework sees

This is what it was committed to database

What happened?
Are you using MySQL 5.6 or superior?
Are you using the latest driver?
Are you using DATETIME(N) where N is
0...6?
More details on:
http://www.java-n-me.com/2014/02/adf-entity-track-change-history.html

Working with history columns and the phantom error


The Solution

Set your DATETIME columns to


have a minimum of 3 milliseconds
CREATE TABLE Player (
idPlayer INT NOT NULL AUTO_INCREMENT,
name VARCHAR(150) NOT NULL,
age INT NOT NULL ,
created_by VARCHAR(50) NULL,
created_on DATETIME(3) NULL,
modified_by VARCHAR(50) NULL,
modified_on DATETIME(3) NULL,
PRIMARY KEY (idPlayer)
);

Now your database is storing milliseconds


into your DATETIME columns

And the phantom error disappears...

More details on:


http://www.java-n-me.com/2014/02/adf-entity-track-change-history.html

Using MySQL AI columns in your Entities


The Case
Assume the following table exists
in database:
CREATE TABLE Actor (
id_actor INT NOT NULL AUTO_INCREMENT ,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY (id_actor)
);

Limited to one
column per table

Assigned to a
Specific table

Default mapping does not work out-ofthe-box

DBSequence is not available when Data


Type Map is Java

We need a workaround here

General solution for all Entities is


required

Auto Increment Properties

Entity requires manual changes

Two approaches: The not so elegant


solution and the elegant solution

More details on:


http://www.java-n-me.com/2013/11/using-mysql-autoincrement-pk-column-in.html

Using MySQL AI columns in your Entities


Manual Changes required on Entities
What we need: Similar to DBSequence

Original mapping
More details on:
http://www.java-n-me.com/2013/11/using-mysql-autoincrement-pk-column-in.html

Using MySQL AI columns in your Entities


The Workaround

Use MySQL function to retrieve the last inserted value for an AUTO_INCREMENT
column:

Where should we invoke the LAST_INSERT_ID() function?

LAST_INSERT_ID()
Extend your Entity class and override the doDML method

Worried about concurrency?

According to MySQL documentation:

Using LAST_INSERT_ID() and AUTO_INCREMENT columns


simultaneously from multiple clients is perfectly valid. Each client will
receive the last inserted ID for the last statement that client executed.
More details on:
http://www.java-n-me.com/2013/11/using-mysql-autoincrement-pk-column-in.html

Using MySQL AI columns in your Entities


The not so elegant solution
//package...
//imports...
public class ActorImpl extends EntityImpl {
//... other class members

1
2

@Override
protected void doDML(int op, TransactionEvent e) {
//got to call first to super, so the record is posted
//and then we ask for the last insert value
super.doDML(op, e);
//ask for the last insert value if we are inserting
if (op == DML_INSERT) {
populateAutoincrementAtt();
}

}
//... populateAutoincrementAtt() is shown next...
}
More details on:
http://www.java-n-me.com/2013/11/using-mysql-autoincrement-pk-column-in.html

Using MySQL AI columns in your Entities


The not so elegant solution
//package...
//imports...
public class ActorImpl extends oracle.jbo.server.EntityImpl {
//... other class members including the doDML method from previous slide
private void populateAutoincrementAtt() {
try (PreparedStatement stmt = this.getDBTransaction().createPreparedStatement("SELECT last_insert_id()", 1))
{
stmt.execute();
try (ResultSet rs = stmt.getResultSet()) {
if (rs.next()) {
setAttribute(IdActor, rs.getInt(1));
}

}
}
catch (SQLException e) { // ...
}

More details on:


http://www.java-n-me.com/2013/11/using-mysql-autoincrement-pk-column-in.html

Why do you think this is


called the not so elegant
solution?

Using MySQL AI columns in your Entities


The elegant solution

More details on:


http://www.java-n-me.com/2013/11/using-mysql-autoincrement-pk-column-in.html

Using MySQL AI columns in your Entities


The elegant solution
Custom Properties at Attribute level
also works, but are less elegant...
Once we have marked our attribute
as an AutoIncrement attribute, we just
need to modify our Entity base class
In this approach, we do not need to
create a java class for our Actor Entity
Do you remember that Best Practice
that we talked about at the beginning?
More details on:
http://www.java-n-me.com/2013/11/using-mysql-autoincrement-pk-column-in.html

Using MySQL AI columns in your Entities


The elegant solution
//package...
//imports...
public class EntityImpl extends oracle.jbo.server.EntityImpl {
//... other class members

@Override
protected void doDML(int op, TransactionEvent e) {
//got to call first to super, so the record is posted
//and then we ask for the last insert value
super.doDML(op, e);
//ask for the last insert value if we are inserting
if (op == DML_INSERT) {
populateAutoincrementAtt();
}
}
}

More details on:


http://www.java-n-me.com/2013/11/using-mysql-autoincrement-pk-column-in.html

This is our Entity base


class, the one we created
at the beginning of the
presentation
Changes in the code are
highlighted

Using MySQL AI columns in your Entities


The elegant solution
//package...
//imports...
public class EntityImpl extends oracle.jbo.server.EntityImpl {
//... other class members including the doDML method from previous slide
private AttributeDef findAutoincrementAtt() {
EntityDefImpl entdef = this.getEntityDef();
AttributeDef pk = null;

//look for primary key with Autoincrement property set


for (AttributeDef att : entdef.getAttributeDefs()) {
if (att.isPrimaryKey() && (att.getProperty("AI") != null && new Boolean(att.getProperty("AI").toString()))) {
pk = att;
break;
}
}
return pk;

More details on:


http://www.java-n-me.com/2013/11/using-mysql-autoincrement-pk-column-in.html

Using MySQL AI columns in your Entities


The elegant solution
//package...
//imports...
public class EntityImpl extends oracle.jbo.server.EntityImpl {
//... other class members including the doDML and findAutoincrementAtt methods from previous slides
private void populateAutoincrementAtt() {
AttributeDef pk = findAutoincrementAtt();
if(pk == null) return;
try (PreparedStatement stmt = this.getDBTransaction().createPreparedStatement("SELECT last_insert_id()", 1))
{
stmt.execute();
try (ResultSet rs = stmt.getResultSet()) {
if (rs.next()) {
setAttribute(pk.getName(), rs.getInt(1));
}
}
}
catch (SQLException e) { // ...
}
}
}

More details on:


http://www.java-n-me.com/2013/11/using-mysql-autoincrement-pk-column-in.html

No Security in ADF Essentials?

The Java Authentication and Authorization Service (JAAS)

Integrated into the Java SE starting with J2SDK 1.4.

Users People

Roles Similar to unix groups. Users may have many roles

Realms Universe of users and groups

JDBC Realm Means that the universe of users and groups exists in a database

File Realm Means that the universe of users and groups exists in a file

Etc.

Authentication of users, to reliably and securely determine who is currently executing Java
code.

BASIC HTTP BASIC authentication protocol. A popup window asking for user/password

FORM Need to define a Login page and an Error page

CLIENT-CERT Needs digital certificates for both client and server

DIGEST Similar to BASIC but with security enabled for transmitting credentials

No Security in ADF Essentials?

The Java Authentication and Authorization Service (JAAS)

Authorization of users to ensure they have the access control rights


(permissions) required to do the actions performed

HTTPServletRequest provides methods to get the user name and verify whether the
current user has a specific role, among other

Different level of transport guaranties

CONFIDENTIAL Communication goes through the SSL

INTEGRAL Full encryption not required but detects MITM attacks

NONE Accept any connection

Define security constraints on a page or group of pages using the above


definitions
Configure JAAS in the application deployment descriptor (web.xml)

No Security in ADF Essentials?


Configuring JAAS authentication in your app
<!-- web.xml -->
<!-- more elements
<login-config>
<auth-method>FORM</auth-method>
<realm-name>realmName</realm-name>
<form-login-config>
<form-login-page>/faces/login.jspx</form-login-page>
<form-error-page>/faces/loginError.jspx</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>Management</role-name>
</security-role>
<security-role>
<role-name>SelfService</role-name>
</security-role>
<!-- more elements -->

In web.xml you define, among other


things, the authentication method, the
realm you want to use and the roles
that you know exists in your application

In this case we are using the FORM


authentication. Notice that the URL of the
pages is relative and it includes the /faces
url-pattern.

We also define two roles that our


application uses.

It's important to notice that the roles


that we define in web.xml are
application roles, they have nothing to
do with the roles that you have in your
realm.

No Security in ADF Essentials?

Use welcome files in order to redirect the user to a security-constraint

Whenever a user enters yourdomain.com he/she gets redirected to the


welcome page

Since faces/Main.jspx is secured, the user will get redirected to the login page
if he/she is not authenticated

<!-- web.xml -->


<!-- more elements
<welcome-file-list>
<welcome-file>faces/Main.jspx</welcome-file>
</welcome-file-list>
<!-- more elements -->

No Security in ADF Essentials?


Configuring JAAS authorization in your app
<!-- web.xml -->
<!-- more elements

<security-constraint>
<web-resource-collection>
<web-resource-name>MainProtection</web-resource-name>
<url-pattern>/faces/Main.jspx</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
<auth-constraint>
<role-name>Management</role-name>
<role-name>SelfService</role-name>
</auth-constraint>
</security-constraint>
<!-- more elements -->

Define as many securityconstraints as you need

Notice that the URL of the pages


is relative and it includes the
/faces url-pattern.

Define what roles are granted for


access. These roles are the
application roles you just defined
in the same web.xml file not the
roles in your realm

You may also specify


transportation guaranties for
every security-constraint.

No Security in ADF Essentials?


Role mapping
<!-- glassfish-web.xml -->
<!-- more elements

<security-role-mapping>
<role-name>Management</role-name>
<group-name>Management</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>SelfService</role-name>
<group-name>SelfService</group-name>
</security-role-mapping>

In glassfish-web.xml is where
you map your application
roles with the roles that you
have in your realm

<!-- more elements -->

In this case the roles have the


same name

Many roles of your realm can


be mapped to one of your
application roles

No Security in ADF Essentials?


Gotchas in 11g

ADF 11g is built on top of Java EE


5, and there are some limitations
about using the FORM auth-method

You need to use html components

The action of the form must be


j_security_check

The name of your login input text


must be j_username

The name of your password input text


must be j_password

Since 12c is built on top of Java EE


6, you have more control over the
authentication process

<!-- login.jspx -->


<!-- more elements
<form method="post" action="j_security_check">
<h:panelGrid columns="2" id="pg1">
<h:outputLabel value="Login" id="ol1"/>
<input type="text" id="j_username" name="j_username"/>
<h:outputLabel value="Password" id="ol2"/>
<input type="password" id="j_password" name="j_password"/>
<af:spacer width="10" height="10" id="s1"/>
<input type="submit" name="submit" value="Login"/>
</h:panelGrid>
</form>
<!-- more elements -->

Same DS name for Weblogic (Integrated) and Glassfish


One time configuration

JDeveloper creates a data source


using the following syntax

Glassfish server uses another syntax

java:comp/env/jdbc/DS_NAME
jdbc/DS_NAME

The problem arises when you want


to test your application on both WLS
or Glassfish

More details on:


http://www.java-n-me.com/2013/03/glassfish-plugin-for-jdeveloper-11gr2.html

Same DS name for Weblogic (Integrated) and Glassfish


One time configuration

What would you do?


A. Change JDeveloper DS name to: jdbc/DS_NAME
B. Change Glasfish DS name to:
java:comp/env/jdbc/DS_NAME
C. Test your app on either WLS or Glassfish but not both
D. None of the above

More details on:


http://www.java-n-me.com/2013/03/glassfish-plugin-for-jdeveloper-11gr2.html

Same DS name for Weblogic (Integrated) and Glassfish


One time configuration

As stated by the Java EE web-app deployment


descriptor (web.xml)
The res-ref-name element specifies the name of a
resource manager connection factory reference.
The name is a JNDI name relative to the
java:comp/env context.
The name must be unique within a web application.

More details on:


http://www.java-n-me.com/2013/03/glassfish-plugin-for-jdeveloper-11gr2.html

Same DS name for Weblogic (Integrated) and Glassfish


One time configuration

Define the resource at Web Content/WEBINF/web.xml


<resource-ref>
<res-ref-name>jdbc/DATASOURCE_NAME</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

Implicitly we are defining java:comp/env/jdbc/DATASOURCE_NAME which matches


exactly the data source name configured in the application and used by WLS.
More details on:
http://www.java-n-me.com/2013/03/glassfish-plugin-for-jdeveloper-11gr2.html

Same DS name for Weblogic (Integrated) and Glassfish


Map your resource to a Glassfish DS

Good practice: Map the resource

Done in a container-specific configuration file:

Web Content/WEBINF/glassfish-web.xml

The file is created by JDeveloper when you deploy to a Glassfish server

If the file already exists it is not overridden

More details on:


http://www.java-n-me.com/2013/03/glassfish-plugin-for-jdeveloper-11gr2.html

Same DS name for Weblogic (Integrated) and Glassfish


Map your resource to a Glassfish DS

Copy the following code:


<?xml version="1.0" encoding="UTF-8" ?>
<glassfish-web-app>
<context-root>YOUR_APP_NAME</context-root>
<property name="useBundledJsf" value="true"/>
Resource defined in web.xml
<class-loader delegate="false"/>
<resource-ref>
<res-ref-name>java:comp/env/jdbc/DATASOURCE_NAME</res-ref-name>
<jndi-name>jdbc/DATASOURCE_NAME_AT_GLASSFISH</jndi-name>
</resource-ref>
</glassfish-web-app>
Datasource defined in Glassfish
More details on:
http://www.java-n-me.com/2013/03/glassfish-plugin-for-jdeveloper-11gr2.html

Managing Glassfish server from inside JDeveloper


Free Extension

What is the extension for:

Start/Stop external Glassfish server

Start in Debug mode an external Glassfish server

Invoke the Web admin console of an external Glassfish server

Works on JDeveloper 11g and 12c

11g version created by Shay Shmeltzer

Updated to run on Linux by Alexis Lopez

Updated to run on Mac by David Aroca

12c version ported by Alexis Lopez

Runs on Linux and Mac

Source code can be found at the


Oracle JDeveloper 3rd Party Extensions project at java.net

More details on:


http://www.java-n-me.com/2014/02/glassfish-extension-for-jdeveloper-12c-go-live.html

Managing Glassfish server from inside JDeveloper


Free Extension

More details on:


http://www.java-n-me.com/2014/02/glassfish-extension-for-jdeveloper-12c-go-live.html

References

ADF Essentials Home Page

MySQL 5.6 Reference Manual

Vesterli E., Sten (2013). Packt Publishing Ltd. Great starter book for ADF Essentials

Glassfish Security

Online documentation for MySQL version 5.6

Developing Web Applications with Oracle ADF Essentials

Downloads, documentation, etc.

Masoud Kalali (2010). Packt Publishing Ltd. Complete reference to security on Glassfish server

Other non ADF Essentials references worth to mention:

http://www.jobinesh.com/

ADF Architecture TV

ADF Code Corner

AMIS Technology Blog

SUBMIT YOUR ABSTRACTS


TODAY!

ADF Essentials Tips -n- Tricks


Oracle OpenWorld 2014 - UGF9377

Alexis Lopez
Consultant, Iteria SAS
@aa_lopez
aalopez@gmail.com
www.java-n-me.com

Вам также может понравиться