Failed to connect to Glassfish MysqlDatasource

Standard

The Issue

Creating a JDBC connection pool in Netbeans 8.0 on a Centos 6.5 linux distribution resulted in a class not found exception.

java.lang.classnotfoundexception com.mysql.jdbc.jdbc2.optional.mysqldatasource

This error occurs because Glassfish 4.0 bundled with Netbeans did not include the required JDBC driver.

The Solution

Download the MySQL connector driver by selecting the platform independent download. Select the compressed type of your choice (tar / zip). Extract the downloads content and look for the “mysql-connector-java-5.x.x-bin.jar” file. This is the file that Glassfish needs to properly load the MySQL datasource.

Place the jar file in the the following location

\PATH\TO\GLASHFISH\INSTALL\FOLDER\glassfish4.X.X\glassfish\lib

After placing the jar file in the Glassfish lib folder restart the server. This should fix the class not found exception.

Tomcat fresh install on Amazon EC2 Redhat Instance

Standard

This tutorial will demonstrate how to install a fresh version of apache tomcat 7.0.53 from source on an Amazon EC2 Redhat based instance. Including the installation of mysql, vsftpd, ssl (forced for the entire tomcat server), and iptables prerouting.

To begin, login to your EC2 instance and do a quick yum update. This will assure that all of your virtual machine’s libraries are up to date.

yum update 

When prompted, type “yes” to install updates. This update process can last several minutes.

The first library to install will be mysql. Run the following commands to install the server.

yum install mysql
yum install mysql-server
yum install mysql-devel 

Once installed turn on mysql to the chkconfig. This command makes it so mysql will automatically start on server reboot.

chkconfig mysqld on

Now you must configure mysql. Begin by starting the service.

service mysqld start 

It will output the following message:

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:

Run the following command to set your new password for root login.

/usr/bin/mysqladmin -u root password 'new-password'

Now login to mysql terminal by typing the following:

mysql -u root -p

It will prompt you for your password that you have just set above. Next step is to set up user permissions. This is accomplished by first creating a user, then assigning them permissions to access a given database.

#Create a new user, with password
CREATE USER 'username'@'%' IDENTIFIED BY 'user_password';

#Set to given database for a user
GRANT ALL PRIVILEGES ON database_name.* TO 'username'@'*' WITH GRANT OPTION;

#List all users and grants
SELECT user,host FROM mysql.user;

Mysql is now ready to use, you now have a user that should have grant permissions to access a given database (if you made one).

The next step is to setup apache tomcat 7.0.52. Navigate to the opt directory of your server. Then download the Tomcat file and extracting it.

cd /opt/
wget http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.53/bin/apache-tomcat-7.0.53.tar.gz
tar -zxvf apache-tomcat-7.0.53.tar.gz
rm apache-tomcat-7.0.53.tar.gz

Tomcat comes loaded will all the files you need. You can test running the server by navigating to the bin directory and running the startup script.

cd /opt/apache-tomcat-7.0.53/bin/
./startup.sh

Note: If tomcat fails to start; check to make sure that java jdk is installed.

java -version
java version "1.7.0_71"
OpenJDK Runtime Environment (rhel-2.5.3.2.el6_6-x86_64 u71-b14)
OpenJDK 64-Bit Server VM (build 24.65-b04, mixed mode)

If no installation of java is found using yum install jdk 1.7

yum install java-1.7.0-openjdk java-1.7.0-openjdk-devel

It would be much nicer if you could start / stop the server like a service ex. “service tomcat start”. If you want tomcat to run as a server read the Tomcat Service Script tutorial.

Now I want tomcat to run on port 80. Port 80 is the standard port for all internet traffic. To direct traffic from port 80 to tomcat please follow my “Running Tomcat port 80” guide.

The next step is to enable SSL for security. In my case I want SSL to be force / required on all requests. Let’s say I have private data being transmitted so this is necessary.

First edit the conf/server.xml file. Note that the tomcat.keystore file should point to the location you placed your keystore file on the webserver. I have placed my in the root of the tomcat server.

<Connector port="8443" enableLookups="false" protocol="HTTP/1.1" proxyPort="443" keystorePass="changeit" keystoreFile="/opt/apache-tomcat-7.0.53/keys/tomcat.keystore" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" Server="My server name" clientAuth="false" sslProtocol="TLS" />

To force SSL on all connections edit the conf/web.xml file. At the end of the file before the closing tag add:

<!-- Require HTTPS for everything except /files and (favicon) and /css. -->
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>HTTPSOnly</web-resource-name>
      <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>HTTPSOrHTTP</web-resource-name>
      <url-pattern>*.ico</url-pattern>
      <url-pattern>/files/*</url-pattern>
      <url-pattern>/css/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
      <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
  </security-constraint>

Tomcat will now force SSL on all incoming connections, it is ready for your war file. To upload a war file we need a ftp client. By default this Redhat instance does not come with the libraries configured. I choose to use vsftpd.

yum install vsftpd
yum install ftp

The next step is to configure permissions.

vi /etc/vsftpd/vsftpd.conf

Look for the following lines and uncomment / modify.

anonymous_enable=NO
local_enable=YES
write_enable=YES
chroot_local_user=YES

After edits are made, restart the service.

service vsftpd restart

Finally, you need to add a user to the system to login as.

adduser ec2-user
passwd ec2-user

Your server should now accept incoming connections via port 21 (FTP).

Once you login you will only have access to your home directory. Hence, you will not be able / have permissions to upload to the tomcat server directory in the opt folder. To fix this add a symbolic link in your home directory to the webapps directory of the tomcat installation.

ln -s /opt/apache-tomcat-8.0.8/webapps/ /home/ec2-user/webapps

Couchbase Datasource

Standard

I ran into a problem where I needed to utilize a NoSQL database. Since CakePHP is more fit for relational style database (MYSQL) I had to create a custom datasource. The following datasource allows models to have basic functionality of Couchbase Server.

Example Usage:

$keyName = "Jackson";
$cache = $this->YourModel->Get(array($keyName));
if (!$cache) {
   $data = array('Firstname' => 'Jessie',
                 'Lastname' => 'Jackson');
   $this->YourModel->Assign(array($keyName), json_encode($data));
}

Setup / Configuration:

To use the couchbaseSource you must specify it in your model. Do so by adding the following.
YourModel.php

public $useDbConfig = ‘bucketCB';

You can also programmatically change datasouces / database on the fly. For example, sometimes I want to use my default MYSQL datasource. This can be accomplished like so:

if($useNOSQL) {
   $this->Model->useDbConfig = 'bucketCB';
}else {
   $this->Model->useDbConfig = 'default'; // MYSQL
}

To use be able to use this datasource it must be added to cakePHP list of available datasources.
Database.php

public $bucketCB = array(
    'datasource' => 'CouchbaseSource',
    'username' => ‘bucketUsername,
    'password' => ‘bucketPassword’,
    'bucket' => ‘bucketName,
    'prefix' => ‘p’, // A _ is automatically prepended
    'expiry' => '1814400', // 3 Weeks
    'autoConnect' => true,
    'database' => NULL,
    'persistent' => false
);

app/Model/Datasource/CouchbaseSource.php

/**
 * Couchbase Datasource class
 * @Author Brandon Klimek
 * 
 */
class CouchbaseSource extends DataSource {

    /**
     * Description of datasource
     *
     * @var string
     */
    public $description = 'Couchbase DataSource';

    /**
     * Holds the object for the connected database
     *
     * @var object
     */
    public $conObject = NULL;

    /**
     * Holds the configuration settings that are passed in
     *
     * @var array
     */
    public $config = NULL;

    /**
     * The prefix of the couchbase keys
     *
     * @var string
     */
    public $prefix = NULL;

    /**
     * CouchDBSource Constructor
     *
     * @param array $config The configuration for the Datasource
     *
     * @return void
     * @link http://api.cakephp.org/class/data-source#method-DataSource__construct
     */
    public function __construct($config = array()) {

        // If no configuration is set we use the default
        $this->config = $config;

        // Setup the cache string that is used when building the string
        $this->prefix = (isset($this->config['prefix']) ? $this->config['prefix'] . "_" : "");

        if ($this->config['autoConnect']) {
            $this->connect();
        }
    }

    /**
     * Connect to the Datasource
     *
     * @return obj
     * @throws InternalErrorException
     */
    public function connect() {
        if ($this->conObject !== true) {
            try {
                $this->conObject = new Couchbase("127.0.0.1:8091", $this->config['username'], $this->config['password'], $this->config['bucket'], $this->config['persistent']);
            } catch (CouchbaseException $e) {
                throw new InternalErrorException(array('class' => $e->getMessage()));
            }
        }
        return $this->conObject;
    }

    /**
     * Handle queries to couchbase
     *
     * @param unknown_type $method
     * @param array() $param
     * @return array or false
     */
    public function query($method, $params) {

        // If not connected... reconnect!
        if ($this->conObject === NULL) {
            $this->connect();
        }

        $apiMethod = $this->__methodToClass($method);
        if (!method_exists($this, $apiMethod)) {
            throw new NotFoundException("Class '{$apiMethod}' was not found");
        } else {
            return call_user_func_array(array($this, $apiMethod), $params);
        }
    }

    /**
     * Translate method to className
     *
     * @param $method
     * @return string
     */
    private function __methodToClass($method) {
        return 'CB' . strtolower(Inflector::camelize($method));
    }

    /**
     * describe() tells the model your schema for ``Model::save()``.
     *
     * You may want a different schema for each model but still use a single
     * datasource. If this is your case then set a ``schema`` property on your
     * models and simply return ``$Model->schema`` here instead.
     */
    public function describe(&$Model) {
        return $this->description;
    }

    /////////////////////////////////////////////////
    // Query Methods
    /////////////////////////////////////////////////

    /**
     * Add a value with the specified key that does not already exist. Will fail if the key/value pair already exist.
     *
     * @return Contains the document ID or false if the operation failed
     */
    public function CBadd($key = NULL, $value = NULL, $expiry = NULL, $persisto = NULL, $replicateto = NULL) {
        return $this->conObject->add($key, $value, $expiry, $persisto, $replicateto);
    }

    /**
     * Append a value to an existing key
     *
     * @return scalar ( Binary object )
     */
    public function CBappend($key = NULL, $value = NULL, $expiry = NULL, $persisto = NULL, $replicateto = NULL) {
        return $this->conObject->append($key, $value, $expiry, $persisto, $replicateto);
    }

    /**
     * Compare and set a value providing the supplied CAS key matches
     *
     * @return scalar ( Binary object )
     */
    public function CBcas($casimoqie = NULL, $key = NULL, $value = NULL, $expiry = NULL) {
        return $this->conObject->cas($casimoqie, $key, $value, $expiry);
    }

    /**
     * Decrement the value of an existing numeric key. The Couchbase Server stores numbers as unsigned values. Therefore the lowest you can decrement is to zero.
     *
     * @return scalar ( Binary object )
     */
    public function CBdecrement($key = NULL, $offset = NULL) {
        return $this->conObject->decrement($key, $offset);
    }

    /**
     * Delete a key/value
     *
     * @return scalar ( Binary object )
     */
    public function CBdelete($key = NULL, $offset = NULL) {
        $this->conObject->delete($key, $offset);
    }

    /**
     * Wait until the durability of a document has been reached
     *
     * @return boolean ( Boolean (true/false) )
     */
    public function CBkeyDurability($key = NULL, $casunique = NULL) {
        return $this->conObject->keyDurability($key, $casunique);
    }

    /**
     * Wait until the durability of a document has been reached
     *
     * @return boolean ( Boolean (true/false) )
     */
    public function CBflush() {
        return $this->conObject->flush();
    }

    /**
     * Get a value and update the expiration time for a given key
     *
     * @return obj
     */
    public function CBgetAndTouch($key = NULL, $expiry = NULL) {
        return $this->conObject->getAndTouch($key, $expiry);
    }

    /**
     * Get a value and update the expiration time for a given key
     *
     * @return obj
     */
    public function CBgetAndTouchMulti($key = NULL, $expiry = NULL) {
        return $this->conObject->getAndTouchMult($key, $expiry);
    }

    /**
     * Fetch the next delayed result set document
     *
     * @return array ( Result list )
     */
    public function CBfetch($key = NULL, $keyn = NULL) {
        return $this->conObject->fetch($key, $keyn);
    }

    /**
     * Fetch all the delayed result set documents
     *
     * @return array ( Result list )
     */
    public function CBfetchAll($key = NULL, $keyn = NULL) {
        return $this->conObject->fetchAll($key, $keyn);
    }

    /**
     * Get one or more key values
     *
     * @return scalar ( Binary object )
     */
    public function CBget($key = NULL, $callback = NULL, $casunique = NULL) {

        if (is_array($key)) {
            $key = $this->buildCacheString($key);
        }
        return $this->conObject->get($key, $callback, $casunique);
    }

    /**
     * Get one or more key values
     *
     * @return boolean ( Boolean (true/false) )
     */
    public function CBgetDelayed($keyn = NULL, $with_cas = NULL, $callback = NULL) {
        return $this->conObject->getDelayed($keyn, $with_cas, $callback);
    }

    /**
     * Get one or more key values
     *
     * @return array ( Array of documents )
     */
    public function CBgetMulti($keycollection = NULL, $casarray = NULL) {
        return $this->conObject->getMulti($keycollection, $casarray);
    }

    /**
     * Returns the version of the client library
     *
     * @return scalar ( Binary object )
     */
    public function CBgetClientVersion() {
        return $this->conObject->getClientVersion();
    }

    /**
     * Get the value for a key, lock the key from changes
     *
     * @return (none)
     */
    public function CBgetAndLock($key = NULL, $casarray = NULL, $getlexpiry = NULL) {
        return $this->conObject->getAndLock($key, $casarray, $getlexpiry);
    }

    /**
     * Get the value for a key, lock the key from changes
     *
     * @return (none)
     */
    public function CBgetAndLockMulti($keycollection = NULL, $casarray = NULL, $getlexpiry = NULL) {
        return $this->conObject->getAndLockMulti($keycollection, $casarray, $getlexpiry);
    }

    /**
     * Returns the number of replicas for the configured bucket
     *
     * @return scalar ( Number of replicas )
     */
    public function CBgetNumReplicas() {
        return $this->conObject->getNumReplicas();
    }

    /**
     * Retrieve an option
     *
     * @return mixed ( Different possible types )
     */
    public function CBgetOption($option) {
        return $this->conObject->getOption($option);
    }

    /**
     * Returns the versions of all servers in the server pool
     *
     * @return array ( List of things )
     */
    public function CBgetVersion() {
        return $this->conObject->getVersion();
    }

    /**
     * Execute a view request
     *
     * @return (none)
     */
    public function CBview($ddocname = NULL, $viewname = NULL, $viewoptions = array()) {
        return $this->conObject->view($ddocname, $viewname, $viewoptions);
    }

    /**
     * Generate a view request, but do not execute the query
     *
     * @return (none)
     */
    public function CBviewGenQuery($ddocname = NULL, $viewname = NULL, $viewoptions = NULL) {
        return $this->conObject->viewGenQuery($ddocname, $viewname, $viewoptions);
    }

    /**
     * Increment the value of an existing numeric key. Couchbase Server stores numbers as unsigned numbers, therefore if 
     * you try to increment an existing negative number, it will cause an integer overflow and return a non-logical numeric 
     * result. If a key does not exist, this method will initialize it with the zero or a specified value.
     *
     * @return scalar ( Binary object )
     */
    public function CBincrement($key = NULL, $offset = NULL, $create = NULL, $expiry = NULL, $initial = NULL) {
        return $this->conObject->increment($key, $offset, $create, $expiry, $initial);
    }

    /**
     * Get the durability of a document
     *
     * @return boolean ( Boolean (true/false) )
     */
    public function CBobserve($key = NULL, $casunique = NULL, $observeddetails = NULL) {
        return $this->conObject->observe($key, $casunique, $observeddetails);
    }

    /**
     * Get the durability of a document
     *
     * @return boolean ( Boolean (true/false) )
     */
    public function CBobserveMulti($keycollection = NULL, $observeddetails = NULL) {
        return $this->conObject->observeMulti($keycollection, $observeddetails);
    }

    /**
     * Prepend a value to an existing key
     *
     * @return scalar ( Binary object )
     */
    public function CBprepend($key = NULL, $value = NULL, $expiry = NULL, $casunique = NULL, $persistto = NULL, $replicateto = NULL) {
        return $this->conObject->prepend($key, $value, $expiry, $casunique, $persistto, $replicateto);
    }

    /**
     * Update an existing key with a new value
     *
     * @return scalar ( Binary object )
     */
    public function CBreplace($key = NULL, $value = NULL, $expiry = NULL, $casunique = NULL, $persistto = NULL, $replicateto = NULL) {
        return $this->conObject->replace($key, $value, $expiry, $casunique, $persistto, $replicateto);
    }

    /**
     * Store a value using the specified key, whether the key already exists or not. Will overwrite a value if the given key/value already exists.
     *
     * @return scalar ( Binary object )
     */
    public function CBassign($key = NULL, $value = NULL, $expiry = NULL, $casunique = NULL, $persistto = NULL, $replicateto = NULL) {

        if (is_array($key)) {
            $key = $this->buildCacheString($key);
        }

        $time = (($expiry == NULL) ? $this->config['expiry'] : $expiry);

        try {
            $this->conObject->set($key, $value, $time, $casunique, $persistto, $replicateto);
        } catch (Exception $e) {
            return false;
        }
        return true;
    }

    /**
     * Set multiple key/value items at once
     *
     * @return scalar ( Binary object )
     */
    public function CBassignMulti($kvarray = NULL, $expiry = NULL) {
        return $this->conObject->setMulti($kvarray, $expiry);
    }

    /**
     * Specify an option
     *
     * @return boolean ( Boolean (true/false) )
     */
    public function CBsetOption($option = NULL, $mixed = NULL) {
        return $this->conObject->setOption($option, $mixed);
    }

    /**
     * Get the database statistics
     *
     * @return array ( List of things )
     */
    public function CBgetStats() {
        return $this->conObject->getStats();
    }

    /**
     * Update the expiry time of an item
     *
     * @return boolean ( Boolean (true/false) )
     */
    public function CBtouch($key = NULL, $expiry = NULL) {
        return $this->conObject->touch($key, $expiry);
    }

    /**
     * Change the expiration time for multiple documents
     *
     * @return boolean ( Boolean (true/false) )
     */
    public function CBtouchMulti($keyarray = NULL, $expiry = NULL) {
        return $this->conObject->touchMulti($keyarray, $expiry);
    }

    /**
     * Private buildCacheString
     *
     * Takes in a array and then splits it into a string and underscores
     *
     * @param array resetSplit
     * @return string
     */
    private function buildCacheString($restSplit = array()) {

        $count = count($restSplit);
        $string = "";
        foreach ($restSplit as $key => $method) {
            $string .= Inflector::slug($method);
            if ($count - 1 != $key) {
                $string .= "_";
            }
        }
        return strtolower($this->prefix . $string);
    }

}