Saturday, October 26, 2013
Java Synchronization and Concurrency Across Multiple JVMs, Multiple Computers, Part 2
Coding a Fast Browser Proxy Script
Java Method Access Modifiers
1) Protected is for situations you will rarely, if ever, encounter – keeping methods from being implemented in a subclass
2) Public is for when you write a method that you want everybody to call
3) Private is for methods that you only ever want called from this specific class
4) No-modifier (default or package) is appropriate for almost everything – it allows your method to be called by other classes in your package, but not by other folks’ classes (in other packages)
I mentioned that I had on occasion abused those rules when using classes written by others. A couple times, I created an empty folder hierarchy in my project that mirrored someone else’s package and created my class there so I could use their package-access resources. For example, I once extended sun.net.ftp.TransferProtocolClient, creating my own FTPClient class with the addition of methods to do such things as proxy login, make directory, make path and chmod. That was in 2003, when Sun was pretty strict about the sun.* packages and classes. I just now edited that FTPClient code in Eclipse, referring to a new JDK and found that not only does sun.net.ftp.FtpProtocolException no longer extend IOException (requiring major code updates); but also, I can extend TransferProtocolClient in a different package with no problem. In this case, I no longer need to mirror the original package in order to extend the class – and I conclude that the resources I need to access are no longer package-protected, they are public.
Saturday, October 12, 2013
New Oracle Installation Lockdown
It is my standard practice to turn off services and applications which are not needed. I do this as an administrative account; however, I regularly run as a non-administrative account, and you should too.
1) Typically, I remove everything from Windows Start Menu / All Programs / Startup (C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup ).
2) Then I run the Registry Editor (regedit.exe) and search for Keys that match the whole string “run”, as shown below. Search the entire registry (find next from top to bottom) and comment applications that need not run (some call this stuff “Crapware”). Typically, I add “x_” in front of the application command, as shown below. Note that you may need to research each application listed in order to determine whether or not it is needed on your system.
3) Finally, I run Computer Management (%windir%\system32\compmgmt.msc /s) and set any Services that are not needed from “Automatic” to “Manual” startup.
When Oracle 12c is installed, there are several Services that are created, and most of them are set for “Automatic” startup. On my development workstation, I only start Oracle as needed (as shown below), but even in production environments, several of these Services may not be required at all times – they should be started only as needed.
Oracle Create Session Role and Kicking Everybody Off
Do not use the default Oracle role, Connect. Use of that role is deprecated. Instead, create a role to provide valid users with the Create Session privilege. (Please refer to my book, “Expert Oracle and Java Security”.) Do not grant the Create Session privilege directly to users; but rather, grant them your role. Here is an example:
create role create_session_role not identified;
grant create session to create_session_role;
create user username identified by userpassword container=CURRENT;
grant create_session_role to username;
Note that the qualifier “container=CURRENT” is for Pluggable Database (PDB) instances in Oracle 12c.
You should ask, “besides adhering to a philosophy or rule of standardization, why should I use a role for the Create Session privilege? After all, all standard users need to be able to connect.” In this case, our role is less for providing a standard grant for users to connect than it is for providing a mechanism to remove that ability without deleting the users (or shutting down the listener or database.) Basically, the create_session_role role provides us a mechanism for kicking everyone off an active Oracle instance.
Say, for example, we have a hundred user accounts, and all have been granted the create_session_role role. At any time, twenty of those users may be connected. Now, for some security or administrative reason, we need to stop all user activity without shutting down the database. First we need to assure that no one else can connect, and that the current users cannot reconnect. This is the easy part. We simple revoke the Create Session privilege from create_session_role.
revoke create session from create_session_role;
This does not also have the effect of breaking the current sessions. Those have already been “created”; and so for active sessions, the Create Session privilege has already been used, and is no longer needed. For active sessions, we need to manually kill them to stop their activity. This Anonymous PL/SQL Block will kill all active sessions, except the current session in which it is run.
declare
pragma autonomous_transaction;
m_sid v$session.SID%TYPE;
cursor session_cur is
select serial#, sid from sys.v$session
where type='USER' and not sid = m_sid;
session_rec session_cur%ROWTYPE;
begin
-- Oracle will not let you kill your own current, active session
m_sid := SYS_CONTEXT( 'USERENV', 'SID' );
open session_cur;
loop
fetch session_cur into session_rec;
exit when session_cur%NOTFOUND;
dbms_output.put_line( 'Killing: ' ||
session_rec.SID || ', ' || session_rec.serial# );
execute immediate 'ALTER SYSTEM KILL SESSION ''' ||
session_rec.SID || ', ' || session_rec.serial# || '''';
end loop;
close session_cur;
end;
In this block, we create a cursor (session_cur) of active sessions, selected from sys.v$session; so this needs to be run by an account with SYS, SYSTEM or DBA level privileges. We only care about USER sessions. Also, we filter out the current session by Session ID (SID). We find our current SID from the USERENV environment of the SYS_CONTEXT context.
We loop through our cursor, getting each record (session_rec) while there are more to find. We print out a line to DBMS_OUTPUT for each session we are killing. And we call EXECUTE IMMEDIATE, passing the ALTER SYSTEM command syntax required to kill each session.
So everybody is kicked off the database, except the current user. And no one may connect / reconnect. The current user may grant Create Session to any additional user needed for troubleshooting or research on the current situation. After the situation is remedied, and normal operations are restored, users can be permitted to connect once again with a single grant:
grant create session to create_session_role;
Now, we are very glad to have a role to distribute this privilege to all users.
Java Synchronization and Concurrency Across Multiple JVMs, Multiple Computers
public void init( ServletConfig config ) throws ServletException {
super.init( config );
try {
fileOut = new PrintWriter( "logfile.log" );
} catch( Exception x ) {
throw new ServletException( x.toString() );
}
}
public void destroy( ServletConfig config ) {
fileOut.close();
}
public void doGet( HttpServletRequest req, HttpServletResponse res )
throws ServletException, IOException {
logWrite( "Using doGet()" );
//…
}
public void doPost( HttpServletRequest req, HttpServletResponse res )
throws ServletException, IOException {
logWrite( "Using doPost()" );
//…
}
public static synchronized void logWrite( String logEntry ) {
fileOut.println( logEntry );
}
}
In this first example, each time a browser calls on the doGet() and doPost() methods we will write to a log file. We need to have these threads take turns, so only one thread attempts to write to the file at a time – if more than one write occurs concurrently, a runtime exception will be generated. The simplest way get the threads to take turns is to place the actual log file write in a synchronized method. The example logWrite() method is modified with the “synchronized” key word so that each thread in this web application (Servlet) will wait until the logWrite() method is available, then the thread will lock the method for exclusive use until finished. After that, the lock will be released, and the next thread will be able to use the method.
Another way we can synchronize multiple threads using a shared resource is to put every use of the resource in synchronized blocks. The following example shows how we might implement a doGet() success counter.
private static Object synchObject = new Object();
public void doGet( HttpServletRequest req, HttpServletResponse res )
throws ServletException, IOException {
try {
synchronized( synchObject ) {
doGetSuccessCounter++;
}
//…
synchronized( synchObject ) {
System.out.println( "There are " +
doGetSuccessCounter + " doGet() successes!" );
}
} catch( Exception x ) {
synchronized( synchObject ) {
doGetSuccessCounter--;
}
throw new ServletException( x.toString() );
}
}
}
Notice in this example that we increment the doGetSuccessCounter integer in the doGet() method, and we decrement doGetSuccessCounter in the catch block – just for fun, we do not count a thread’s passage through doGet() as a “success” if an Exception is thrown. Again we want to synchronize use of this shared resource (it is declared outside the method scope) so that we count and discount for every thread calling doGet(), without concurrent threads overwriting one another. In addition, we synchronize around the report of the current value of doGetSuccessCounter so we are guaranteed to get the current value (after any other lock is released and we have exclusive access to the integer.)
SynchServlet.doGetSuccessCounter++;
}
Synchronized methods and synchronized blocks are foundational concurrency tools. There are a number of other synchronization and concurrency techniques, many Thread-safe classes and the java.util.concurrent packages that may be also used. All of them deal with synchronization within a single JVM. Probably the clearest example of when all these techniques fall short is when you are running Java applications on separate computers and need to update a shared resource, like a file.
public void init( ServletConfig config ) throws ServletException {
super.init( config );
centralLog( "Starting SynchServlet");
}
private static void centralLog( String message ) throws ServletException {
// Need something external to this JVM to test singularity
FileOutputStream fos = null;
FileLock fl = null;
PrintWriter centralOut = null;
try {
fos= new FileOutputStream( "\\\\server\\dir\\lock.lock" );
fl = fos.getChannel().tryLock();
// Null when can't get exclusive lock on file
while( fl == null ) {
try {
Thread.sleep(1000);
fl = fos.getChannel().tryLock();
} catch( Exception v ) {}
}
// At this point, I have exclusive lock on file!
centralOut = new PrintWriter( "\\\\server\\dir\\central.log" );
centralOut.println( message );
} catch( Exception x ) {
throw new ServletException( x.toString() );
} finally {
try {
if( centralOut != null ) centralOut.close();
} catch(Exception y) {}
try {
if( fl != null ) fl.release();
if( fos != null ) fos.close();
} catch(Exception y) {}
}
}
}
There are three classes in the java.io package that can provide FileChannel objects: FileInputStream, FileOutputStream and RandomAccessFile. In our example, we instantiate a FileOutputStream on the “lock.lock” file and call getChannel() to get the FileChannel object. There are several methods to get an exclusive lock on a FileChannel. We use the FileChannel.tryLock() method in our example. If tryLock() is unsuccessful, it returns null, else it returns the associated FileLock object. In our example, we call tryLock() then test the return value for null. If it is null, we sleep for one second then try again, in the while( null ) loop. When the FileLock is not null, we have an exclusive lock and can update shared resources. It is very important that we release the FileLock. For this reason, I have an independent try/catch block within the finally block that calls the release() method on the FileLock object.
package dac;
import org.apache.commons.net.ftp.*;
import org.apache.commons.net.ftp.parser.*;
private int getActivePort() {
if( true ) return 11111; // requires ip / port filter permission
Let me mention the limitation of this file locking strategy. It does not work between JVMs running on different OS architectures. If all your JVMs are running on Windows or all your JVMs are running on UNIX, there is no problem; however, you cannot obtain an exclusive lock on a file from a Windows computer and have that lock observed on a UNIX computer, nor vice versa. On the bright side, you can have a JVM on a Windows computer get an exclusive lock on a file that resides on a UNIX computer, and other Windows JVMs will observe the lock. The same is true for JVMs on UNIX obtaining exclusive locks on files that reside on Windows.
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.FileLock;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SynchServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static PrintWriter fileOut; // Synchronize use of this!
private static int doGetSuccessCounter = 0;
private static Object synchObject = new Object();
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
try {
centralLog("Starting SynchServlet");
fileOut = new PrintWriter("logfile.log");
} catch (Exception x) {
throw new ServletException(x.toString());
}
}
public void destroy(ServletConfig config) {
fileOut.close();
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
try {
synchronized (synchObject) {
doGetSuccessCounter++;
}
logWrite("Using doPost()");
// …
+ " doGet() successes!");
System.out.println("There are " + doGetSuccessCounter
+ " doGet() successes!");
}
} catch (Exception x) {
synchronized (synchObject) {
doGetSuccessCounter--;
}
throw new ServletException(x.toString());
}
}
public void altGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
try {
synchronized (SynchServlet.synchObject) {
SynchServlet.doGetSuccessCounter++;
}
SynchServlet.logWrite("Some other message");
// …
+ " doGet() successes!");
System.out.println("There are " + doGetSuccessCounter
+ " doGet() successes!");
}
} catch (Exception x) {
synchronized (synchObject) {
doGetSuccessCounter--;
}
throw new ServletException(x.toString());
}
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
logWrite("Using doPost()");
// …
// synchronized instance method, not class method (static)
private static synchronized void logWrite(String logEntry) {
fileOut.println(logEntry);
}
private static void centralLog(String message) throws ServletException {
// Need something external to this JVM to test singularity
FileOutputStream fos = null;
FileLock fl = null;
PrintWriter centralOut = null;
try {
fos = new FileOutputStream("\\\\server\\dir\\lock.lock");
fl = fos.getChannel().tryLock();
// Null when can't get exclusive lock on file
while (fl == null) {
try {
Thread.sleep(1000);
fl = fos.getChannel().tryLock();
} catch (Exception v) {}
}
// At this point, I have exclusive lock on file!
centralOut = new PrintWriter("\\\\server\\dir\\central.log");
centralOut.println(message);
} catch (Exception x) {
throw new ServletException(x.toString());
} finally {
try {
if (centralOut != null) centralOut.close();
} catch (Exception y) {}
try {
if (fl != null) fl.release();
if (fos != null) fos.close();
} catch (Exception y) {}
}
}
}