Tuesday, September 16, 2014

How to secure a password file on Windows 7 (JMX interface of ActiveMQ, to be specific)

This took me a while to figure out, so here's a description of how to make use of a password-protected JMX interface with ActiveMQ (5.8 in my case).

1. Make sure your activemq.xml specifies that you actually want to allow JMX monitoring:
   <managementContext>
        <managementContext createConnector="true" connectorPort="1098"/>
   </managementContext>

2. Change activemq.bat startup script to specify an explicit password files:

set SUNJMX=-Dcom.sun.management.jmxremote.port=1098
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.password.file=%ACTIVEMQ_BASE%/conf/jmx.password
-Dcom.sun.management.jmxremote.access.file=%ACTIVEMQ_BASE%/conf/jmx.access

when you start ActiveMQ, you will probably get this error now:

> activemq.bat
Error: Password file read access must be restricted: .../conf/jmx.password

ActiveMQ requires the password file to have specific user-only permissions, see here for more information. Unfortunately this link is for Windows XP, so here's what to do on Windows 7

I've actually found two solutions, one graphical, the other one from the command line:

Solution (using Windows Explorer):

1) change the owner to be 'you' (required step!!)
Select jmx.password, Right-Mouse-Cick -> Properties -> Security -> Advanced -> Owner -> Edit
and select the single owner of this to be your username.

Note: you need to click OK and exit out of Properties for this to be effective

2) Select jmx.password, Right-Mouse-Cick -> Properties -> Security -> Advanced -> Change Permissions 

- uncheck "Include inheritable permissions" and click Remove to remove all inherited permissions
- then click Add... to add read/write permissions for only your user: Enter your username as object name, and select for example 'Full Control'. Click Ok and exit out of properties.


Solution (using Windows command line):

1) open a windows command prompt in your ActiveMQ 'conf' folder.


2) use icacls (run 'icacls' without options for help) to change the owner to be 'you', in my case:

icacls jmx.password /setowner apodehl


3) remove all inherited permissions:

icacls jmx.password /inheritance:r


4) grant minimal permissions to your user (read/write in this case):

icacls jmx.password /grant:r apodehl:(r,w)



Monday, September 8, 2014

A simple disk performance test

Test the I/O performance by writing several messages to the current directory
The files created are named writetest..dat and should be removed after the test !!

Usage: writetest
       Will write buffers of size each and print the rate every msgs.
       The test is repeated times and the used time is displayed
Example: writetest async 1000000 100000 100 1



#include
#include
#include
#include

#ifdef WIN32
#include
#else
#include
#endif

#include // for S_IRUSR and S_IWUSR

#include

#ifndef WIN32
  #include
  #include
#else
  #include
#endif

#include
#include

#pragma warning(disable : 4996)  // we want to keep the same code for Unix

using namespace std;

class MsgTimer
{
public:

  MsgTimer( const char* applname ) {
    m_startTime = 0.0;
    m_lastTime = 0.0;
    m_endTime = 0.0;
    m_numMsgs = 0;
    strcpy( m_applName, applname );
  }

  ~MsgTimer() { };

  // --- timer starts now ---
  void start() {
 
    m_startTime = getTimeInSeconds();
    m_lastTime = m_startTime;

    cerr << "\n" << m_applName << ": ###### MsgTimer started" << endl;
    printTimestamp();
  }

  // --- specify the number of processed messages ---
  void stop( long numMsgs ) {

    m_endTime = getTimeInSeconds();
    m_lastTime = m_endTime;
    m_numMsgs = numMsgs;

    cerr << "\n" << m_applName << ": ###### MsgTimer stopped after processing " << numMsgs << " messages " << endl;
    printTimestamp();
    printResult();
  }

  // --- specify the number of processed messages ---
  // returns current msgs/sec
  double  current( long numMsgs ) {

    double currentTime = getTimeInSeconds();

    double elapsedTime, mps;

    elapsedTime = currentTime - m_startTime;
    if( elapsedTime > 0.0 ) mps = (double)numMsgs / elapsedTime;
    else mps = 0.0;
    cerr << m_applName << ": ###### Accumulated Number of msgs : " << m_numMsgs << endl;
    cerr << m_applName << ": ###### Accumulated Elapsed time in sec: " << elapsedTime << endl;
    cerr << m_applName << ": ###### Accumulated Msgs-per-sec: " << mps << endl;

    elapsedTime = currentTime - m_lastTime;
    if( elapsedTime > 0.0 ) mps = (double)(numMsgs-m_numMsgs) / elapsedTime;
    else mps = 0.0;
    cerr << m_applName << ": ###### Number of msgs since last call: " << numMsgs - m_numMsgs << endl;
    cerr << m_applName << ": ###### Partial Elapsed time in sec: " << elapsedTime << endl;
    cerr << m_applName << ": ###### Partial Msgs-per-sec: " << mps << endl;

    m_numMsgs = numMsgs;
    m_lastTime = currentTime;
    return mps;
  }

  // --- prints a timestamp in a common format ---
  void printTimestamp() {

    time_t t;
    char* buf;
    t = time(NULL);
    //ctime_r(&t,buf);
    //buf[strlen(buf)-1]='\0';
    buf = ctime(&t);
    cerr << m_applName << ": ###### Timestamp: " << buf << endl;
  }


  static double getTimeInSeconds() {

    double t0,t1,t2;

#ifndef WIN32
    int r;
    struct timeval tp;
    struct timezone tzp;
    r = gettimeofday(&tp,&tzp);
    if (-1 == r) {

      cerr << "gettimeofday() failed" << endl;
      exit(-1);
    }

    t1 = (double)tp.tv_sec;
    t2 = (double)tp.tv_usec;
    t0 = t1+ t2/1000000;
#else
    struct _timeb tp;
    _ftime(&tp);

    t1 = (double)tp.time;
    t2 = (double)(tp.millitm/1000.0);
    t0 = t1+t2;
#endif

    return (t0);
  }

private:

  // --- prints the result in a common format ---
  void printResult() {

    double elapsedTime,mps;
    elapsedTime = m_endTime - m_startTime;

    if( m_numMsgs <= 0 ) {

      cerr << "MsgTimer::stop() was not called !" << endl;
      return;
    }

    if (elapsedTime > 0.0) {

      mps = (double)m_numMsgs / elapsedTime;

    }
    else {

      mps = 0.0;
    }

    cerr << m_applName << ": ###### Final Elapsed time in sec: " << elapsedTime << endl;
    cerr << m_applName << ": ###### Final Number of msgs : " << m_numMsgs << endl;
    cerr << m_applName << ": ###### Final Msgs-per-sec: " << mps << endl;
  }


  double m_startTime;
  double m_lastTime;
  double m_endTime;
  long m_numMsgs;
  char m_applName[512];
};


MsgTimer mt("writetest");

int isSync = 0;
char buffer[1024*1024];

void _testWrite( int cnt, int nummsgs, int printrate, int msgsize )
{
  char filename[30];
  int msgCount;
  int fd;

  sprintf( filename, "writetest.%d.dat", cnt );

  int oflags = O_RDWR | O_CREAT;

#ifdef WIN32
  int pmode = _S_IREAD | _S_IWRITE;
#else
  // this flag exists only on Unix
  if( isSync==1 ) oflags = oflags | O_SYNC;
  mode_t pmode = S_IRUSR | S_IWUSR;
#endif

  fd = open( filename, oflags, pmode );
  if( fd==-1 ) {
    fprintf( stderr, "writetest: Could not create file %s, maybe you did not remove a former instance ?\n", filename );
    exit(1);
  }

  mt.start();
  for( msgCount=0; msgCount< nummsgs; msgCount ++ ) {

    if( msgCount%10000==0 ) fprintf(stderr,"writetest: written %d msgs\n", msgCount );

    if( msgCount%printrate==0 ) {
      mt.current( msgCount );
    }

    write( fd, (char*)buffer, msgsize );

#ifdef WIN32
    // flush every message if sync writing (Windows NT only)
    if( isSync==1 ) _commit(fd);
#endif

  }
 
  close(fd);
  mt.stop( msgCount );
}

void usage()
{
  fprintf(stderr,"Test the I/O performance by writing several messages to the current directory\n");
  fprintf(stderr,"The files created are named writetest..dat and should be removed after the test !!\n\n");
  fprintf(stderr,"Usage: writetest \n");
  fprintf(stderr,"       Will write buffers of size each and print the rate every msgs.\n");
  fprintf(stderr,"       The test is repeated times and the used time is displayed\n");
  fprintf(stderr,"Example: writetest async 1000000 100000 100 1\n");
  exit(1);
}

int
main(int argc, char **argv)
{

  if( argc < 6 ) usage();

  if( !strcmp(argv[1],"sync") ) isSync = 1;
  else {
    if( !strcmp(argv[1],"async") ) isSync = 0;
    else usage();
  }

  int nummsgs = atoi(argv[2]); // number of messages to write
  int printrate = atoi(argv[3]); // number of tests to run
  int msgsize = atoi(argv[4]); // length of the message to write in bytes
  int numtests = atoi(argv[5]); // number of tests to run

  if( msgsize > sizeof(buffer) ) {
    fprintf(stderr,"writetest: msgsize can not be bigger than %ld\n", sizeof(buffer) );
    exit(1);
  }

  fprintf(stderr,"writetest: ARGUMENTS: sync:%d nummsgs:%d printrate:%d msgsize:%d numtests:%d\n",
        isSync, nummsgs, printrate, msgsize, numtests );
  for( int cnt=0; cnt < numtests; cnt++ ) {

    _testWrite( cnt, nummsgs, printrate, msgsize  );
  }
}

Monday, June 2, 2014

How to use JDBC-ODBC in a 64-bit JVM with a 32-bit version of Office

When using the JDBC-ODBC bridge in the JDK to access Microsoft Access files, you would set your JDBC class to sun.jdbc.odbc.JdbcOdbcDriver and your JDBC URL, for example, to:

"jdbc:odbc:Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=C:\CodeStreet\sample.accdb"

So far so good, but in case you are running a 64-bit JVM with a 32-bit Microsoft Office installation, your JVM and Access driver architecture don't match  and you would see error messages such as:
java.sql.SQLException: [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified


or
"The setup routines for the Microsoft Access Driver .. could not be found."

Fortunately, there are now 64-bit Microsoft Access drivers available, but using them in this context is quite tricky. Once you install the drivers, Microsoft Office stops working !

Opening an Excel file, for example, tries to find the 64-bit version of Office, which you don't have :-(

Instead of opening a file, you will see "Configuration Progress" and "Configuring Microsoft Office Professional Plus 2010..." - what the heck ?

But there's a neat little workaround which goes like this: 

1. Download the Microsoft Access drivers  

2. Check this registry key:
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\14.0\Common\FilesPaths 

If if currently contains an entry with mso.dll, you are using Office 64-bit, which is ok. If there is NO mso.dll key then your Office version is 32-bit. 

3. Open a command prompt and install the 64-bit driver in passive mode (it won't let you do this any other way):
    AccessDatabaseEngine_X64.exe /passive

4. If  mso.dll was not in your registry in step 2, then remove this key now from
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\14.0\Common\FilesPaths 


Microsoft Office (32-bit) should start working again and your 64-bit Access drivers are ready to go.



Good Luck!