Oracle8i Java Developer's Guide Release 2 (8.1.6) A81353-01 |
|
This chapter details how to provide security for your Java applications--both for the connection and for the loaded classes--and how to make your application more effective.
The following features are discussed in this chapter:
Security is a large arena that includes network security for the connection, access and execution control of operating system resources or of JVM and user-defined classes, and bytecode verification of imported JAR files from an external source. You should be aware of what type of security you desire for your Java applications. The following sections describe the various security support available for Java applications within Oracle8i.
There are two major aspects to network security: authentication and data confidentiality. The type of authentication and data confidentiality is dependent on how you connect to the database--through Net8, JDBC, or distributed object (EJB or CORBA) connection.
Connection Security | Description |
---|---|
Net8 |
The database can require both authentication and authorization before allowing a user to connect to the database. Net8 database connection security can require one or more of the following:
|
JDBC |
JDBC connection security required is similar to the constraints required on a Net8 database connection. In addition to the books listed in the Net8 database connection section, see the Oracle8i JDBC Developer's Guide and Reference. |
Distributed Object |
Encryption and authentication might be required for distributed applications, such as EJB and CORBA. For more information, see the Oracle8i Enterprise JavaBeans and CORBA Developer's Guide. |
Once you are connected to the database, you still must have the correct Java 2 permissions and database privileges to access the resources stored within the database. These resources include the following:
These resources can be protected by the following two methods:
Resource Security | Description |
---|---|
Database Resource Security |
Authorization for database resources requires that database privileges (not the same as the Java 2 security permissions) are granted to resources. For example, database resources include tables, classes, or PL/SQL packages. For more information, see the Oracle8i Application Developer's Guide - Fundamentals. |
JVM Security |
JServer uses Java 2 security, which uses permissions to protect operating system resources and all loaded classes--both JVM and user-defined classes. Java 2 security is automatically installed upon startup and protects all operating system resources and JVM classes from all users, except JAVA_ADMIN. JAVA_ADMIN can grant permission to other users to access these classes.
All user-defined classes are secured against users from other schemas. You can grant execution permission to other users/schemas through an option on the See "Java 2 Security" for how to manage and modify Java 2 permissions and policies. |
Each user or schema must be assigned the proper permissions to access JVM classes and operating system resources. For example, this includes sockets, files, and system properties.
Java 2 security was created to provide a flexible, configurable security for Java applications. With Java 2 security, you can define exactly what permissions on each loaded object a schema or role will have. In 8.1.5, the security provided you the choice of two secure roles:
JAVAUSERPRIV
--few permissions, including examining properties
JAVASYSPRIV
--major permissions, including updating JVM protected packages
Because JServer security is based on Java 2 security, you assign permissions to users on a class by class basis.
Java security was created for the non-database world. When you apply the Java 2 security model within the database, certain differences manifest themselves. For example, Java 2 security defines that all applets are implicitly untrusted and all classes within the CLASSPATH are trusted. Within Oracle8i, all classes are loaded within a secure database; thus, no classes are trusted.
The following table briefly describes the differences between Sun Microsystem's Java 2 security and Oracle8i's implementation. This table assumes that you already understand Sun Microsystem's Java 2 security model. For more information, we recommend the following books:
As designed in Java 2 security, Oracle8i supports the security classes. Normally, you set the permissions for the code base either through a tool or by editing the security policy file. In Oracle8i, you set the permissions dynamically through DBMS_JAVA procedures. These procedures modify a policy table, which is a new table within the database that exclusively manages Java 2 security permissions.
Two views have been created for you to view the policy table: USER_JAVA_POLICY and DBA_JAVA_POLICY. Both views contain information about granted and restricted permissions. The DBA_JAVA_POLICY view can see all rows within the policy table; the USER_JAVA_POLICY table can only see permissions relevant to the current user. The following is a description of the rows within each view:
Table entry | Description |
---|---|
Kind |
GRANT or RESTRICT. Shows whether this permission is a positive (GRANT) or negative (RESTRICT) permission. |
Grantee |
The name of the user, schema, or role that the permission is assigned to. |
Permission schema |
The schema that the permission is loaded in. |
Permission type |
The Permission class that you assign. The syntax for the permission name is a string containing the entire class name, such as, |
Permission name |
The permission name of the assigned Permission class. You use this name when defining the permission. When defining the name for a permission of type PolicyTablePermission, the name can become quite complicated. See "Acquiring Administrative Permission to Update Policy Table" for more information. |
Permission action |
The type of action you grant or restrict for this permission. If no action is appropriate for the permission, supply a null value. |
Status |
ACTIVE or INACTIVE. After granting the permission, you can disable or re-enable the permission. This field shows the status of whether the permission is enabled (ACTIVE) or disabled (INACTIVE). |
Key |
Sequence number you use to identify this row. This number should be supplied when disabling, enabling, or deleting the permission. |
There are two ways to set your permissions:
To set individual permissions within the policy table, you must provide the following information:
You can grant either Java 2 Permissions or create your own. The Java 2 Permissions are listed in Table 5-1. If you would like to create your own permissions, see "Creating Permissions".
Permission type |
Granting permissions using the DBMS_JAVA package:
proceduregrant_permission
( grantee varchar2, permission_type varchar2,
permission_name varchar2,
permission_action varchar2 )
proceduregrant_permission
( grantee varchar2, permission_type varchar2,
permission_name varchar2,
permission_action varchar2, key OUT number)
Granting permissions using Java:
long oracle.aurora.rdbms.security.PolicyTableManager.grant
(
java.lang.String grantee,
java.lang.String permission_type,
java.lang.String permission_name,
java.lang.String permission_action);
void oracle.aurora.rdbms.security.PolicyTableManager.grant
(
java.lang.String grantee,
java.lang.String permission_type,
java.lang.String permission_name,
java.lang.String permission_action,
long[] key);
Note:
In the Java version of |
Restricting permissions using the DBMS_JAVA package:
procedurerestrict_permission
( grantee varchar2, permission_type varchar2,
permission_name varchar2,
permission_action varchar2)
procedurerestrict_permission
( grantee varchar2, permission_type varchar2,
permission_name varchar2,
permission_action varchar2, key OUT number)
Restricting permissions using Java:
long oracle.aurora.rdbms.security.PolicyTableManager.restrict
(
java.lang.String grantee,
java.lang.String permission_type,
java.lang.String permission_name,
java.lang.String permission_action);
void oracle.aurora.rdbms.security.PolicyTableManager.restrict
(
java.lang.String grantee,
java.lang.String permission_type,
java.lang.String permission_name,
java.lang.String permission_action,
long[] key);
Assuming that you have appropriate permissions to modify the policy table, you use the grant_permission method within the DBMS_JAVA package to modify the PolicyTable to allow the user access to the indicated file. In this example, the user, Larry, has PolicyTable modification permission. Within a SQL package, Larry grants permission to read and write a file to the user Dave.
connect larry/larry REM Grant DAVE permission to read and write the Test1 file. call dbms_java.grant_permission('DAVE',
'java.io.FilePermission', '/test/Test1',
'read,write'); REM commit the changes to the PolicyTable commit;
You use the restrict method for specifying exceptions for general rules. You create general rules to grant or restrict access to a larger arena. That is, if you have defined a general rule that no one can read or write for an entire directory, you can use restrict for eliminating one aspect of this rule. In addition, you can override this restriction with a more specific grant. For example, if you want to allow access to all files within the /tmp
directory--except for your password
file that exists in that directory--you would grant permission for read and write to all files within /tmp
and restrict read and write access to the password
file. Furthermore, if you want the file owner to still be able to modify the password
file, you can grant a more specific permission to allow access to one user, which will override the restriction. JServer security combines all rules to understand who really has access to the password
file. The following is the code that implements this example:
connect larry/larry REM Grant permission to all users (PUBLIC) to be able to read and write
REM all files in /tmp. call dbms_java.grant_permission('PUBLIC',
'java.io.FilePermission',
'/tmp/*',
'read,write'); REM Restrict permission to all users (PUBLIC) from reading or writing the REM password file in /tmp. call dbms_java.restrict_permission('PUBLIC',
'java.io.FilePermission',
'/tmp/password',
'read,write'); REM By providing a more specific rule that overrides the restriction, REM Larry can read and write /tmp/password. call dbms_java.grant_permission('LARRY',
'java.io.FilePermission',
'/tmp/password',
'read,write'); commit;
The explicit rule for this scenario is as follows:
If the restrict permission implies the request, then for a grant to be effective, the restrict permission must also imply the grant.
After the first initialization for JServer, only a single role--JAVA_ADMIN--is allowed to modify the policy table. The JAVA_ADMIN role is immediately assigned to DBA; thus, if you are assigned to the DBA group, you will automatically take on all JAVA_ADMIN permissions.
In order for you to be able to add permissions to this table, JAVA_ADMIN must grant you administrative permission to change the policy table. JAVA_ADMIN grants your schema update rights for the permission, PolicyTablePermission
. This permission defines that your schema can update certain specific permission types. For example, in order for you to add a permission that controls access to a file, you must have a PolicyTablePermission
that allows you to add grant or restrict permission on a FilePermission
. To grant administrative permission, use the following method within the DBMS_JAVA package.
Granting policy table administrative permissions using DBMS_JAVA:
proceduregrant_policy_permission
( grantee varchar2, permission_schema varchar2, permission_type varchar2,
permission_name varchar2)
proceduregrant_policy_permission
( grantee varchar2, permission_schema varchar2, permission_type varchar2,
permission_name varchar2,
key OUT number)
Granting policy table administrative permission using Java:
long oracle.aurora.rdbms.security.PolicyTableManager.grantPolicyPermission
(
java.lang.String grantee,
java.lang.String permission_type,
java.lang.String permission_name);
void oracle.aurora.rdbms.security.PolicyTableManager.grantPolicyPermission
(
java.lang.String grantee,
java.lang.String permission_type,
java.lang.String permission_name,
long[] key);
The following example shows JAVA_ADMIN (as SYS) giving Larry permission to update the PolicyTable for FilePermission
. Once this permission is granted, Larry can grant permissions to other users for reading, writing, and deleting files.
REM Connect as SYS, which is assigned JAVA_ADMIN role, to give Larry permission
REM to modify the PolicyTable connect SYS/SYS REM SYS grants Larry the right to administer permissions for REM FilePermission call dbms_java.grant_policy_permission('LARRY', 'SYS',
'java.io.FilePermission', '*');
You can create your own Permission type by performing the following:
1. Create and load the user permission.
2. Grant administrative and action permissions to specified users.
3. Implement security checks for the permission.
You can create your own permission by extending the Java 2 Permission class. Any user created permission must extend Permission
. The following example creates MyPermission
. MyPermission
extends BasicPermission
, which in turn extends Permission
.
package test.larry; import java.security.Permission; import java.security.BasicPermission; public class MyPermission extends BasicPermission { public MyPermission(String name) { super(name); } public boolean implies(Permission p) { boolean result = super.implies(p); return result; } }
When you create a permission, you are designated as owner of that permission. The owner always has administrative rights for the permission. This means that the owner can grant permission, including administrative rights, to other users. Administrative rights permits the user to update the PolicyTable for the user-defined permission. For example, if LARRY creates a permission, MyPermission
, only LARRY can invoke grant_policy_permission
for himself or another user. This method updates the PolicyTable
on who can grant rights to MyPermission
. The following code demonstrates this:
REM Since Larry is the user that creates MyPermission, Larry connects to REW the database to assign permissions for MyPermission. connect larry/larry REM As the owner of MyPermission, Larry grants himself the right to REM administer permissions for test.larry.MyPermission within the JVM REM security PolicyTable. Only the owner of the user-defined permission REM can grant administrative rights. call dbms_java.grant_policy_permission
('LARRY', 'LARRY',
'test.larry.MyPermission', '*'); REM commit the changes to the PolicyTable commit;
Once you have granted administrative rights, you can grant action permissions for the user created permission. For example, the following SQL grants permission for LARRY to execute anything within MyPermission
and DAVE to be able to only execute actions that start with "act.".
REM Since Larry is the user that creates MyPermission, Larry connects to REW the database to assign permissions for MyPermission. connect larry/larry REM Once able to modify the PolicyTable for MyPermission, Larry grants himself REM full permission for MyPermission. Notice that the Permission is prepended REM with its owner schema. call dbms_java.grant_permission( 'LARRY',
'LARRY:test.larry.MyPermission', '*', null); REM Larry grants Dave permission to do any actions that start with 'act.*'. call dbms_java.grant_permission ('DAVE', 'LARRY:test.larry.MyPermission', 'act.*', null); REM commit the changes to the PolicyTable commit;
Once you have created, loaded, and assigned permissions for MyPermission
, you must implement the call to SecurityManager for having the permission checked. There are four methods in the following example: sensitive
, act
, print
, and hello
. Because of the permissions granted in the SQL example in step 2, the following users can execute methods within the example class:
act
method.
print
and hello
methods. The print
method does not check any permissions, so anyone can execute the print
method. The hello
method executes AccessController.doPrivileged
, which means that the method executes with LARRY's permissions. This is referred to as definer's rights.
package test.larry; import java.security.AccessController; import java.security.Permission; import java.security.PrivilegedAction; import java.sql.Connection; import java.sql.SQLException; /** * MyActions is a class with a variety of public methods that * have some security risks associated with them. We will rely * on the Java security mechanisms to ensure that they are * performed only by code that is authorized to do so. */ public class Larry { private static String secret = "Larry's secret"; MyPermission sensitivePermission = new MyPermission("sensitive"); /** * This is a security sensitive operation. That is it can * compromise our security if it is executed by a "bad guy". * Only larry has permission to execute sensitive. */ public void sensitive() { checkPermission(sensitivePermission); print(); } /** * Will print a message from Larry. We need to be * careful about who is allowed to do this * because messages from Larry may have extra impact. * Both larry and dave have permission to execute act. */ public void act(String message) { MyPermission p = new MyPermission("act." + message); checkPermission(p); System.out.println("Larry says: " + message); } /** * Print our secret key * No permission check is made; anyone can execute print. */ private void print() { System.out.println(secret); } /** * Print "Hello" * This method invokes doPrivileged, which makes the method run * under definer's rights. So, this method runs under Larry's * rights, so anyone can execute hello. * Only Larry can execute hello */ public void hello() { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { act("hello"); return null; } }); } /** * If a security manager is installed ask it to check permission * otherwise use the AccessController directly */ void checkPermission(Permission permission) { SecurityManager sm = System.getSecurityManager(); sm.checkPermission(permission); } }
Once you have defined a permission, you can disable it so that it is no longer valid. However, if you decide you want the permission validated again, you can enable the permission. You can delete the permission from the table if you believe that it will never be used again. To delete, you must first disable the permission. If you do not disable the permission, the deletion will not occur.
To disable permissions, you can use either the disable_permission
or the revoke
method.
Disabling permissions using DBMS_JAVA:
procedurerevoke_permission
(permission_schema varchar2, permission_type varchar2,
permission_name varchar2,
permission_action varchar2)
proceduredisable_permission
(key number)
Disabling permissions using Java:
voidrevoke
(String schema, String type, String name, String action); void oracle.aurora.rdbms.security.PolicyTableManager.disable
(long number);
Enabling permissions using DBMS_JAVA:
procedure enable_permission
(key number)
Enabling permissions using Java:
void oracle.aurora.rdbms.security.PolicyTableManager.enable
(long number);
Deleting permissions using DBMS_JAVA:
procedure delete_permission
(key number)
Deleting permissions using Java:
void oracle.aurora.rdbms.security.PolicyTableManager.delete
(long number);
Table 5-2 lists the installed permission types. Whenever you want to grant or restrict permission, you must provide the permission type within the DBMS_JAVA method. The permission types with which you control access are the following:
java.security.Permission
Permission type |
All the Java permission types are documented in Sun Microsystem's Java 2 documentation.
The Oracle-specific permissions, PolicyTablePermission
and JServerPermission
are described below:
You use this permission to control who can update the policy table. Once granted the right to update the policy table for a certain Permission
type, the user can control another user's access to some resource.
After JServer initialization, only the JAVA_ADMIN role can grant administrative rights for the policy table through PolicyTablePermission
. Once it grants this right to other users, these users can in turn update the policy table with their own grant and restrict permissions.
To grant policy table updates, you use the DBMS_JAVA method: grant_policy_permission
, as discussed in "Acquiring Administrative Permission to Update Policy Table". Once you have updated the table, you can view either the DBA_JAVA_POLICY or USER_JAVA_POLICY views to see who has been granted permissions.
You use this permission to grant and restrict access to Aurora JVM resources. The JServerPermission
extends from BasicPermission
. The following table lists the names that JServerPermission
grants access for:
When you first initialize JServer, several roles are populated with certain permission grants. The following tables show these roles and their initial permissions:
Table 5-3 SYS Initial Permissions
JServerPermission
, all users can load classes, except for the list specified in the table. These exceptions are RESTRICT permissions. For more information on RESTRICT permissions, see Example 5-2.
Table 5-4 PUBLIC Default Permissions
Permission type | Permission name | Action |
---|---|---|
|
Debug |
null |
|
* |
connect, resolve |
In 8.1.5, JVM security was controlled by granting the roles of JAVASYSPRIV, JAVAUSERPRIV, or JAVADEBUGPRIV to schemas. In the current version, these roles still exist as permission groups. See the previous section, "Initial Permission Grants" for the explicit permissions set for each role. You can set up and define your own collection of permissions. Once defined, you can grant any collection of permissions to any user. That user will then have the same permissions that exist within the role. In addition, if you need additional permissions, you can add individual permissions to either your specified user or role. Permissions defined within the policy table have a cumulative effect. See "Fine-Grain Definition for Each Permission" for information on how to grant permissions to a user or a role.
The following example gives Larry and Dave the following permissions:
REM Granting Larry the same permissions as exist within JAVASYSPRIV grant javasyspriv to larry; REM Granting Dave the ability to debug grant javadebugpriv to dave; commit; REM I also want Dave to be able to read and write all files on the system call dbms_java.grant_permission('DAVE', 'SYS:java.io.FilePermission', '<<ALL FILES>>', 'read,write', null);
A debug role, JAVADEBUGPRIV, was created to grant permissions for running the debugger. The permissions assigned to this role are listed in Table 5-7. In order to have permission to invoke the debug agent, the caller must have been granted JAVADEBUGPRIV or the debug JServerPermission
as follows:
REM Granting Dave the ability to debug grant javadebugpriv to dave; REM Larry grants himself permission to start the debug agent. call dbms_java.grant_permission ('LARRY', 'oracle.aurora.security.JServerPermission', 'Debug', null);
A debugger provides extensive access to both code and data on the server, but at this time, we envision its use to be restricted to development environments. Refer to the discussion in the section "Debugging Server Applications" for information on using the debugging facilities in this release.
In order to load classes, you must have the following permission:
JServerPermission("LoadClassInPackage." + <class_name>)
The class name is the fully qualified name of the class that you are loading.
This excludes loading into system packages or replacing any system classes. Even if you are granted permission to load a system class, JServer prevents you from performing the load. System classes are those classes that are installed with the database.
The following shows the ability of each user after database installation, including permissions and JServer restrictions:
java.*
, oracle.aurora.*
, oracle.jdbc.*
. If the user wants to load into another schema, it must be granted the JServerPermission(LoadClassInPackage.<class>)
permission.
The following example shows how to grant SCOTT permission to load classes into the oracle.aurora.*
package:
dbms_java.grant_permission('SCOTT', 'SYS:oracle.aurora.tools.*', null);
You can increase your Java application performance through one of the following methods:
All core Java class libraries and Oracle-provided Java code within JServer is natively compiled for greater execution speed. Java classes exist as shared libraries in $ORACLE_HOME/javavm/admin
, where each shared library corresponds to a Java package. For example, libjox8java_lang.so
on Solaris and libjox8java_lang.dll
on Windows/NT hold java.lang
classes. Specifics of packaging and naming can vary by platform. The Aurora JVM uses natively compiled Java files internally and opens them, as necessary, at runtime.
Native compilation provides a speed increase ranging from two to ten times the bytecode interpretation. The exact speed increase is dependent on several factors, including:
In general, natively compiled code consumes more memory than interpreted code, by a factor of two to three. Caching in an adaptive optimization technique produces a similar trade-off. This is particularly true when a Java server is executing independent code or stored procedures from thousands of users. Using the JServer's static compilation approach for delivering natively compiled Java code provides a large, consistent performance gain, regardless of the number of users or the code paths they traverse on the server.
Java native compilation technology will be available for your Java code in subsequent releases. In the current JServer release, Java code you load to the server is interpreted; the underlying core classes upon which your code relies (java.lang.*
) are natively compiled. Until the native compiler is available for user programs, the net speed benefit of native compilation to your executing program is dependent upon how much native code is traversed, as opposed to interpreted code. The more Java code from core classes and Oracle-provided class libraries you use, the more benefit you will see from native compilation.
The typical and custom database installation process furnishes a database that has been configured for reasonable Java usage during development. However, runtime use of Java should be determined by the usage of system resources for a given deployed application. Resources you use during development can vary widely, depending on your activity. The following sections describe how you can configure memory depending on your performance needs, how to tell how much SGA memory you are using, and what errors denote a Java memory issue:
You can modify the following database initialization parameters to tune your memory usage to reflect more accurately your application needs:
SHARED_POOL_SIZE
--Shared pool memory is used by the class loader within the JVM. The class loader uses an average of about 8 KB for each loaded class. Shared pool memory is used when loading and resolving classes into the database. It is also used when compiling source in the database or when using Java resource objects in the database.
The memory specified in SHARED_POOL_SIZE
is consumed transiently when you use loadjava
. The database initialization process (executing initjvm.sql
against a clean database, as opposed to the installed seed database) requires SHARED_POOL_SIZE
to be set to 50 MB as it loads the Java binaries for approximately 8,000 classes and resolves them. The SHARED_POOL_SIZE
resource is also consumed when you create call specifications and as the system tracks dynamically loaded Java classes at runtime.
JAVA_POOL_SIZE
--Aurora's memory manager allocates all other Java state during runtime execution from the amount of memory allocated using java_pool_size
. This memory includes the shared in-memory representation of Java method and class definitions, as well as the Java objects migrated to session space at end-of-call. In the first case, you will be sharing the memory cost with all Java users. In the second case, under MTS, you must adjust JAVA_POOL_SIZE
allocation based on the actual amount of state held in static variables for each session. See "Java Pool Memory" for more information on JAVA_POOL_SIZE
.
JAVA_SOFT_SESSIONSPACE_LIMIT
--This parameter allows you to specify a soft limit on Java memory usage in a session, which will warn you if you must increase your Java memory limits. The memory that you request in this parameter is allocated when the session is started.
When a user's session-duration Java state exceeds this size, Aurora generates a warning that is written into the trace files. The default is 1 MB. You should understand the memory requirements of your deployed classes, especially as they relate to usage of session space.
JAVA_MAX_SESSIONSPACE_SIZE
--If a user-invokable Java program executing in the server can be used in a way that is not self-limiting in its memory usage, this setting may be useful to place a hard limit on the amount of session space made available to it. The default is 4 GB. This limit is purposely set extremely high to be normally invisible.
When a user's session-duration Java state attempts to exceeds this size, your application can receive an out-of-memory failure.
JServer's unique memory management facilities and sharing of read-only artifacts (such as bytecodes) enables HelloWorld to execute with a per-session incremental memory requirement of only 35 KB. More stateful server applications, such as the Aurora/ORB that CORBA and EJB applications use, have a per-session incremental memory requirement of approximately 200 KB. Such applications must retain a significant amount of state in static variables across multiple calls. Refer to the discussion in the section, "End-of-Call Migration", for more information on understanding and controlling migration of static variables at end-of-call.
Java pool memory is used in server memory for all session-specific Java code and data within the JVM. Java pool memory is used in different ways, depending on what mode the Oracle8i server is running in.
Under dedicated servers, which is probably the case for applications using only Java Stored Procedures, the total required Java pool memory is not much more than 10 MB.
Under MTS servers, which is the case for applications using CORBA or EJB, this figure could be very large. Java-intensive, multi-user benchmarks could require more than 1 GB. Current size limitations are unknown; however, it is platform dependent.
You can find out how much of Java pool memory is being used by viewing the V$SGASTAT table. Its rows include pool, name, and bytes. Specifically, the last two rows show the amount of Java pool memory used and how much is free. The total of these two items equals what you configured in the database initialization file.
SVRMGR> select * from v$sgastat; POOL NAME BYTES ----------- -------------------------- ---------- fixed_sga 69424 db_block_buffers 2048000 log_buffer 524288 shared pool free memory 22887532 shared pool miscellaneous 559420 shared pool character set object 64080 shared pool State objects 98504 shared pool message pool freequeue 231152 shared pool PL/SQL DIANA 2275264 shared pool db_files 72496 shared pool session heap 59492 shared pool joxlod: init P 7108 shared pool PLS non-lib hp 2096 shared pool joxlod: in ehe 4367524 shared pool VIRTUAL CIRCUITS 162576 shared pool joxlod: in phe 2726452 shared pool long op statistics array 44000 shared pool table definiti 160 shared pool KGK heap 4372 shared pool table columns 148336 shared pool db_block_hash_buckets 48792 shared pool dictionary cache 1948756 shared pool fixed allocation callback 320 shared pool SYSTEM PARAMETERS 63392 shared pool joxlod: init s 7020 shared pool KQLS heap 1570992 shared pool library cache 6201988 shared pool trigger inform 32876 shared pool sql area 7015432 shared pool sessions 211200 shared pool KGFF heap 1320 shared pool joxs heap init 4248 shared pool PL/SQL MPCODE 405388 shared pool event statistics per sess 339200 shared pool db_block_buffers 136000java pool free memory 30261248
java pool memory in use 19742720
37 rows selected.
If you run out of memory while compiling (within loadjava or deployejb), you should see an error:
A SQL exception occurred while compiling: : ORA-04031: unable to allocate bytes of shared memory ("shared pool","unknown object","joxlod: init h", "JOX: ioc_ allocate_pal")
The cure is to shut down your database and to reset JAVA_POOL_SIZE to a larger value. The mention of "shared pool" in the error message is a misleading reference to running out of memory in the "Shared Global Area". It does not mean you should increase your SHARED_POOL_SIZE. Instead, you must increase your JAVA_POOL_SIZE, restart your server, and try again.
If you run out of memory while loading classes, it can fail silently, leaving invalid classes in the database. Later, if you try to invoke or resolve any invalid classes, you will see ClassNotFoundException
or NoClassDefFoundException
exceptions being thrown at runtime. You would get the same exceptions if you were to load corrupted class files. You should perform the following:
loadjava -force
option to force the new class being loaded to replace the class already resident in the server.
loadjava -resolve
option to attempt resolution of a class during the load process. This allows you to catch missing classes at load time, not run time.
select * from user_objects where object_name = dbms_java.shortname('');
The STATUS field should be "VALID". If loadjava
complains about memory problems or failures such as "connection lost", increase SHARED_POOL_SIZE and JAVA_POOL_SIZE, and try again.
Aurora preserves the state of your Java program between calls by migrating all objects reachable from static variables into session space at the end of the call. Session space exists within the client's session to store static variables and objects that exist between calls. Aurora performs this migration operation at the end of every call, without any intervention by you.
This migration operation is a memory and performance consideration; thus, you should be aware of what you designate to exist between calls and keep the static variables and objects to a minimum. If you store objects in static variables needlessly, you impose an unnecessary burden on the memory manager to perform the migration and consume per-session resources. By limiting your static variables to only what is necessary, you help the memory manager and improve your server's performance.
To maximize the number of users who can execute your Java program at the same time, it is important to minimize the footprint of a session. In particular, to achieve maximum scalability, an inactive session should take up as little memory space as possible. A simple technique to minimize footprint is to release large data structures at the end of every call. You can lazily recreate many data structures when you need them again in another call. For this reason, the Aurora JVM has a mechanism for calling a specified Java method when a session is about to become inactive, such as at end-of-call time.
This mechanism is the EndOfCallRegistry
notification. It enables you to clear static variables at the end of the call and reinitialize the variables using a lazy initialization technique when the next call comes in. You should execute this only if you are concerned about the amount of storage you require the memory manager to store in between calls. It becomes a concern only for more complex stateful server applications you implement in Java.
The decision of whether to null-out data structures at end-of-call and then recreate them for each new call is a typical time and space trade-off. There is some extra time spent in recreating the structure, but you can save significant space by not holding on to the structure between calls. In addition, there is a time consideration because objects--especially large objects--are more expensive to access after they have been migrated to session space. The penalty results from the differences in representation of session, as opposed to call-space based objects.
Examples of data structures that are candidates for this type of optimization include:
You can register the static variables that you want cleared at the end of the call when the buffer, field, or data structure is created. Within the Oracle-specified oracle.aurora.memoryManager
.EndOfCallRegistry
class, the registerCallback
method takes in an object that implements a Callback
object. The registerCallback
object stores this object until the end of the call. When end-of-call occurs, Aurora invokes the act
method within all registered Callback
objects. The act
method within the Callback
object is implemented to clear the user-defined buffer, field, or data structure. Once cleared, the Callback
is removed from the registry.
The way you use the EndOfCallRegistry
depends on whether you are dealing with objects held in static fields or instance fields.
EndOfCallRegistry
to clear state associated with an entire class. In this case, the Callback
object should be held in a private static field. Any code that requires access to the cached data dropped between calls must invoke a method that lazily creates--or recreates--the cached data. The example below does the following:
Callback
object within a static field, thunk
.
Callback
object for end-of-call migration.
Callback.act
method to free up all static variables, including the Callback object itself.
createCachedField
, for lazily recreating the cache.
When the user creates the cache, the Callback
object is automatically registered within the getCachedField
method. At end-of-call, Aurora invokes the registered Callback.act
method, which frees the static memory.
import oracle.aurora.memoryManager.Callback; import oracle.aurora.memoryManager.EndOfCallRegistry; class Example { static Object cachedField = null;private static Callback thunk = null;
static void clearCachedField() { // clear out both the cached field, and the thunk so they don't // take up session space between calls cachedField = null; thunk = null; } private static Object getCachedField() { if (cachedField == null) { // save thunk in static field so it doesn't get reclaimed // by garbage collectorthunk = new Callback () {
public void act(Object obj) {
Example.clearCachedField();
}
}; // register thunk to clear cachedField at end-of-call.EndOfCallRegistry.registerCallback(thunk);
// finally, set cached field cachedField = createCachedField(); } return cachedField; } private static Object createCachedField() { .... } }
EndOfCallRegistry
to clear state in data structures held in instance fields. For example, when a state is associated with each instance of a class, each instance has a field that holds the cached state for the instance and fills in the cached field as necessary. You can access the cached field with a method that ensures the state is cached.
Callback
object.
Callback.act
method to free up the instance's fields.
Callback
object registers itself for end-of-call migration.
createCachedField
, for lazily recreating the cache.
When the user creates the cache, the Callback
object is automatically registered within the getCachedField
method. At end-of-call, Aurora invokes the registered Callback.act
method, which frees the cache.
This approach ensures that the lifetime of the Callback
object is identical to the lifetime of the instance, because they are the same object.
import oracle.aurora.memoryManager.Callback; import oracle.aurora.memoryManager.EndOfCallRegistry; class Example2implements
Callback { private Object cachedField = null; public voidact
(Object obj) { // clear cached field cachedField = null; } // our accessor method private static Object getCachedField() { if (cachedField == null) { // if cachedField is not filled in then we need to // register self, and fill it in.EndOfCallRegistry.registerCallback(self);
cachedField = createCachedField(); } return cachedField; } private Object createCachedField() { .... } }
A weak table holds the registry of end-of-call callbacks. If either the Callback object or value are not reachable (see JLS section 12.6) from the Java program, they will both be dropped from the table. The use of a weak table to hold callbacks also means that registering a callback will not prevent the garbage collector from reclaiming that object. Therefore, you must hold on to the callback yourself if you need it--you cannot rely on the table holding it back.
You can find other ways in which end-of-call notification will be useful to your applications. The following sections give the details for methods within the EndOfCallRegistry
class and the Callback interface:
The registerCallback
method installs a Callback
object within a registry. At the end of the call, Aurora invokes the act
methods of all registered Callback
objects.
You can register your Callback
object by itself or with a value
object. If you need additional information stored within an object to be passed into act
, you can register this object within the value parameter.
public static void registerCallback(Callback thunk, Object value); public static void registerCallback(Callback thunk);
static void runCallbacks()
The JVM calls this method at end-of-call and calls act
for every Callback
object registered using registerCallback
. You should never call this method in your code. It is called at end-of-call, before object migration and before the last finalization step.
Interface oracle.aurora.memoryManager.Callback
Any object you want to register using EndOfCallRegistry.registerCallback
implements the Callback
interface. This interface can be useful in your application, where you require notification at end-of-call.
public void act(Object value)
You can implement any activity that you require to occur at the end of the call. Normally, this method will contain procedures for clearing any memory that would be saved to session space.