Showing posts with label jboss. Show all posts
Showing posts with label jboss. Show all posts

Monday, December 23, 2013

Automate JBoss Teiid Development Environment with Vagrant and VirtualBox

Why To Automate?

What benefits has the development environment automation:
  • Ability to run several development versions in parallel. Each running environment can host a separate branch of the same product. Or keep some unique environment options, like different JVMs (Pentaho Mondrian is a good example);
  • Some complex project which require simultaneous development in several places (happens rare, so it is arguable benefit);
  • Freedom to experiment with your environment. You can always install something, rollback to a stable step, or start from scratch in minutes;
  •  Your development environment configuration now is a code itself. You can debug it, branch, release, or write tests;
  • You can migrate your environment to a new machine or upload it to a cloud in several mouse clicks. You just need to find a way to VNC or RDP to it. Finally you can do an Eclipse development from your android phone! (yeah, right...)
  • If you work for a company, spawn a new environment for new developers would take hours, not days or weeks;
  • Environment upgrades and updates for everyone in your team are centralized, tested and predictable. No more "it works on my side". 
Along with benefits above, here are the things to remember:
  • Your Host machine should have enough RAM and CPU. Maven can build in multiple threads, GWT compile permutations over all available cores. So obviously more you have, and more you can slice for your environments - better they work;
  • Commercial vs Free. If you develop on Windows or Mac - each generated environment should be registered, which means extra $$ and extra complexity;
  • You need good internet connection to generate the environment (can be several GB to download). On opposite side, most of the time you need to do it only once, the large downloads are cached locally;
  • Writing environment automation scripts can be a slow job. Depends of your goals the environment generation process might take from several minutes to several hours, and some stupid typo you carelessly made in the last step would require to start over;
  • You still have to install and get familiar with some (not very complex) tools which help you with the automation;
All this pros and cons are individual. If you think that in your case it worth attention, below is my recipe.

Instant Eclipse Development Environment

My goal is to have a development environment to work on data federation projects with JBoss Teiid. Below is the list of software I put to it, but your task might be different. Due to modularity, you can exclude some items or add new things very quickly.
  • Linux OS. I took Ubuntu 12.04.03 LTS, it is wide-known, considerably stable and supported by large community. No hassle or extra money for licenses.
  • Oracle Java 7 SDK. Installation requires to accept the license agreement, but it can be automated;
  • Apache Maven 3.0.4. I need specifically this version, but script can be easily changed to the most recent;
  • Git as a source control. Just use the one which Ubuntu has in its repository;
  • Eclipse Kepler SR1 for JEE. I need specifically this version, change several lines in script to put another one;
  • JBoss Application Server. I put EAP-6.1.0.Alpha, this is the one which works best with the latest Teiid;
  • Teiid 8.6.0.Final distribution, download and install on JBoss;
  • Latest Teiid 8.7.0.Alpha1 sources;
As a result, you're getting a pretty decent development environment (8 GB RAM, 2 CPU, 32GB storage), which you can immediately fire up and start working on your own project, or extend Teiid itself.
Steps to follow to generate your own environment using the template above:
  1. Get the latest version of Oracle VM VirtualBox. I used 4.3.6. It is free and easy to use virtualization;
  2. Install latest Vagrant. I have version 1.3.5. This is a tool which manages the collection of your virtual environments, takes care about network access and describes how you want to alter the original OS image for your purposes;
  3. Get the latest Packer. Version I used is 0.4.1. This is a nice functional addition to Vagrant, which allows you to get and apply the customizations and easy install additional software on generated box;
  4. Get the packer-teiid template I made for the environment described above. It is hosted on GitHub, so clone or simply download it to some folder;
  5. Go to the packer-template folder and run "packer build ubuntu-12.04.3-desktop-amd64.json". This will start the generation process. After some time (~40 minutes or so), you should see a shiny new "ubuntu-12.04.3-desktop-amd64.box" file in the same folder, around 2GB size;
  6. Now it is a time to add your new box to a Vagrant collection. Type "vagrant box add ubuntu-12.04.3-desktop-amd64 ubuntu-12.04.3-desktop-amd64.box" in the packer-teiid folder, or specify a full path to your *.box file if you do it outside;
  7. You can bring your environment up now. If you want to do it in the same folder, run "vagrant up". If you want to do it from other place, you should do "vagrant init" first in this new folder. The generated "Vagrantfile" also included in my project, compare it with the one which appear in your new folder and see the differences ("vb.gui = true" is the most important one, otherwise your environment will start in background).
  8. If everything went well, you should see something line on image below. Feel free to login using vagrant/vagrant credentials, sudo and change the root password, run Eclipse or open JBoss bin folder using a shortcuts on a desktop.
  9. You can get to your running environment from Host using ssh (port 2222 on localhost). To transfer files between Host and Guest, you can use "/vagrant" mapping from inside your Guest. It is mapped to the actual folder on Host machine from where you started your VM box ("packer-teiid" in my case).
  10. To stop your environment, simply run "vagrant halt" or "vagrant suspend". To kill it - "vagrant destroy".
  11. Happy Packing!

Screenshot of Ubuntu Linux Development Environment running under Windows 7 host machine:

Friday, April 19, 2013

Running HornetQ Bridge Under JBoss 7

Here is the explanation of Bridge from the HornetQ documentation: "Some messaging systems allow isolated clusters or single nodes to be bridged together, typically over unreliable connections like a wide area network (WAN), or the internet. A bridge normally consumes from a queue on one server and forwards messages to another queue on a different server. Bridges cope with unreliable connections, automatically reconnecting when the connections becomes available again".

My goal is to setup a bridge which will forward messages between two queues residing on a different physical servers over the TCP connection. I plan to use two HornetQ services running as part of the latest JBoss (EAP 6.1.0). The "jms-bridge" example from standalone HornetQ distribution has a sample configuration (standalone-example.xml) for JBoss to setup a bridge on a single JBoss instance with HornetQ service. We will have to modify it to support two servers over the network.

In my configuration I have two systems set up, let's name them "laptop1" and "remoteHost", each of them has JBoss with HornetQ service installed. Below are the changes to the configuration from "jms-bridge" example:
  1. Define new HornetQ connector and acceptor. Note that HornetQ has "netty-connector" defined for JBoss configuration, but seems it does not support "host" and "port" parameters, and rely on mandatory "socket-binding" attribute (see jboss-as-messaging_1_3.xsd from JBoss distribution for more details). That's why I had to redefine both connector and acceptor. For simplicity the acceptor has "0.0.0.0" for host to allow connections from everywhere. Also note that on second server the ports should be listed as opposite - 5457 for acceptor and 5456 for connector.
  2. <connector name="netty-bridge">
        <factory-class>org.hornetq.core.remoting.impl.netty.NettyConnectorFactory</factory-class>
        <param key="host" value="remoteHost" />
        <param key="port" value="5457" />
    </connector>
    <acceptor name="netty-bridge">
        <factory-class>org.hornetq.core.remoting.impl.netty.NettyAcceptorFactory</factory-class>
        <param key="host" value="0.0.0.0" />
        <param key="port" value="5456" />
    </acceptor>
  3. Disable the HornetQ security, also for simplicity reasons. The proper approach will be to specify "user" and "password" as parameters for connector above.
  4. <hornetq-server>
            <security-enabled>false</security-enabled>
            ...
  5. Define two queues: "source" and "target". If desired, for the purposes of this example would be enough to have only one "source" query on local server and one "target" on remote. 
  6. <jms-queue name="sourceQueue">
        <entry name="queue/sourceQueue"/>
        <entry name="java:jboss/exported/jms/queues/sourceQueue"/>
    </jms-queue>
    <jms-queue name="targetQueue">
        <entry name="java:/queue/targetQueue"/>
         <entry name="java:jboss/exported/jms/queues/targetQueue"/>
    </jms-queue>
  7. Define a new connection factory to use in bridge.
  8. <connection-factory name="RemoteConnectionFactoryBridge">
        <connectors>
           <connector-ref connector-name="netty-bridge"/>
        </connectors>
        <entries>
           <entry name="RemoteConnectionFactoryBridge"/>
           <entry name="java:jboss/exported/jms/RemoteConnectionFactoryBridge"/>
        </entries>
    </connection-factory>
  9. Modify bridge definition added after "hornetq-system" tag in settings.xml.
  10. <jms-bridge name="myBridge">
        <source>
            <connection-factory name="ConnectionFactory" />
            <destination name="queue/sourceQueue" />
        </source>
        <target>
            <connection-factory name="RemoteConnectionFactoryBridge" />
            <destination name="queue/targetQueue" />
        </target>
        <quality-of-service>AT_MOST_ONCE</quality-of-service>
        <failure-retry-interval>1000</failure-retry-interval>
        <max-retries>7890</max-retries>
        <max-batch-size>1</max-batch-size>
        <max-batch-time>1000</max-batch-time>
    </jms-bridge>
After starting up both JBoss servers, watch for log message indicating that the bridge is started and connection between two HornetQ instances has been established:
18:54:55,621 INFO [org.hornetq.core.server] (MSC service thread 1-1) HQ221024: Started Netty Acceptor version 3.6.2.Final-c0d783c 0.0.0.0:5456 for CORE protocol
...
18:54:57,953 DEBUG [org.hornetq.core.client] (ServerService Thread Pool -- 61) Remote destination: rokan01-VM3762.ca.com/10.130.248.122:5457
...
18:54:58,723 INFO [org.jboss.messaging] (ServerService Thread Pool -- 61) JBAS011610: Started JMS Bridge myBridge
To test the configuration, send a sample message to a "sourceQueue" using for example Teiid messageSender from one of my previous articles:
call Times.messageSender('queue/sourceQueue', 'My Message1'); 
Now login to JBoss Management Console on your remoteHost (http://remoteHost:9990/console/App.html#jms-metrics) and make sure the messages are gets delivered over the bridge to remote host "targetQueue":


There might be a moment when you have only a "connector" side of the bridge up and running. The bridge will automatically reattempt to instantiate the connection every second (configurable by "failure-retry-interval" setting in bridge definition above), until the "acceptor" side of the bridge will be available.

Wednesday, September 12, 2012

Teiid and HornetQ Integration

In my previous article "Simpe JMS Provider on JBoss Teiid" you can learn about how to create a Teiid translator which will "convert" your SQL commands into messages and put them into queue. This article talks about opposite approach: how to retrieve messages from queue using SQL SELECT statements, so our integration pattern will be complete. The schema below explains the integration logic.
Our goal is to pass a specific command (CALL... or INSERT...) to Teiid with some parameters. This parameters will be converted in Teiid by our custom translator (JMS Producer) into the message, which will be immediately placed into queue. New player is another custom translator (JMS Consumer). Client can issue a Continuous Execution to Teiid, which will automatically repeat itself with some time interval. Each round of execution will retrieve messages from queue and pass back to client as execution results. Client gets notified by Teiid when result is available - client obtains a callback object from Teiid when the execution initiated. The StatementCallback.onRow() method gets executed when data is passed back to client.
The process can be further improved, if instead of retrieving messages periodically we will listen queue and retrieve message back to client right after it will appear. So instead of polling we will implement pushing approach.

Implementation Details

The custom translator code for JMS Consumer should implement MessageListener interface. The provided onMessage(Message msg) method will be called each time the message will appear on subscribed queue. Translator also implements ResultSetExecution (so it will appear as a view in Teiid Virtual Database), and ReusableExecution interfaces. The DataNotAvailableException.NO_POLLING will be thrown from the translator next() method, to put translator "into sleep". This is a non-blocking process and part of Continuous Execution functionality. To bring translator back to life, we will call ExecutionContext.dataAvailable() from onMessage() method.
The complete code of JMS Consumer Translator (MessageReceiveExecution.java) is attached, and below are the most important methods:
      public void execute() throws TranslatorException {  
           this.callNext = DataNotAvailableException.NO_POLLING;  
           this.callNext.setStrict(true);  
      }  
      public List<?> next() throws TranslatorException, DataNotAvailableException {  
           if (callNext != null) {  
                DataNotAvailableException e = callNext;  
                callNext = null;  
                throw e;  
           }  
           if (msgRow != null) {  
                List<Object> results = msgRow;  
                msgRow = null;  
                return results;  
           }  
           return null;  
      }  
      public void dispose() {  
           try {  
                qreceiver.close();  
                qsession.close();  
                qcon.close();  
           } catch (JMSException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
           }  
      }  
      public void onMessage(Message msg) {  
           try {  
                String messageContent;  
                if (msg instanceof TextMessage) {  
                     messageContent = ((TextMessage) msg).getText();  
                } else {  
                     messageContent = msg.toString();  
                }  
                System.out.println("!!!!!! MSG:" + messageContent);  
                setMessage(messageContent);  
           } catch (JMSException jmse) {  
                jmse.printStackTrace();  
           }  
           this.context.dataAvailable();            
      }  

To make sure it works I used a JBoss with HornetQ configuration from the previous article, and made a sample java servlet which connects to Teiid and executes a SELECT statement continuously. Also I have another SQL client application (SQuirreL or Eclipse Database Explorer) connected to Teiid, which I use to produce messages (CALL JMS Producer, like in my previous article).
Testing approach:
  • start JBoss/HornetQ/Teiid with deployed translator jar module and war file with servlet;
  • open browser, call servlet to issue a continuous execution;
  • run call statement from SQL client to send a message;
  • check servlet logs that message successfully retrieved by translator and passed back to servlet;
 MessageReceiveExecution (Google Docs).

Monday, September 3, 2012

Simple JMS Producer on JBoss Teiid

With it's Continuous Execution feature, new Teiid 8.1 came closer to functionality which reminds me SQL Server Service Broker. In particular, it might be possible to write Teiid extensions which can serve as integration points between relational data from VDB and asynchronous data available in message-oriented middleware.
For example, Teiid translator can be a JMS client which can send messages into some queue. Another Teiid translator running in Continuous Execution mode can retrieve messages from queue.
Below is results of my experiments with first approach: to have a stored procedure which will act as JMS producer.

Configure JMS in JBoss

The  JBoss 7.1.1 has built-in JMS support with HornetQ, so it is only a matter of properly editing config files. The HornetQ stuff included in standalone-full.xml profile, and Teiid configuration is in standalone-teiid.xml, so I took simplest approach and merged all JMS-related parts from -full.xml into *-teiid.xml one. Also I defined a test queue "MyQueue":
 ....  
      <address-setting match="jms.queue.MyQueue">  
        <dead-letter-address>jms.queue.MyDLQ</dead-letter-address>  
        <redelivery-delay>0</redelivery-delay>  
        <max-delivery-attempts>3</max-delivery-attempts>  
        <max-size-bytes>10485760</max-size-bytes>  
        <address-full-policy>BLOCK</address-full-policy>  
        <message-counter-history-day-limit>10</message-counter-history-day-limit>  
      </address-setting>  
 ....  
  <jms-destinations>  
    <jms-queue name="MyQueue">  
      <entry name="java:jboss/exported/MyQueue"/>  
      <durable>false</durable>  
    </jms-queue>  
 ....  


It is worth to write a small producer and consumer java classes to test the queue and make sure all configured correctly. The JBoss Management console and this post might be helpful.

Write Teiid Translator

I made a simple translator which is a stored procedure, implements the ProcedureExecution and acts as a JMS producer. Queue name and message passed as stored procedure IN parameters.

 package com.rokhmanov.test.teiid.translator.async;  
   
 import java.util.ArrayList;  
 import java.util.List;  
   
 import javax.jms.JMSException;  
 import javax.jms.Queue;  
 import javax.jms.QueueConnection;  
 import javax.jms.QueueConnectionFactory;  
 import javax.jms.QueueSender;  
 import javax.jms.QueueSession;  
 import javax.jms.Session;  
 import javax.jms.TextMessage;  
 import javax.naming.Context;  
 import javax.naming.InitialContext;  
 import javax.naming.NamingException;  
   
 import org.teiid.language.Argument;  
 import org.teiid.language.Call;  
 import org.teiid.translator.DataNotAvailableException;  
 import org.teiid.translator.ExecutionContext;  
 import org.teiid.translator.ProcedureExecution;  
 import org.teiid.translator.TranslatorException;  
   
 public class MessageSendExecution implements ProcedureExecution {  
        
      public final static String CNN_FACTORY="/ConnectionFactory";  
        
      private String queueName;  
      private String messageContent;  
      private Call cmd;  
        
      private QueueConnectionFactory qconFactory;  
      private QueueConnection qcon;  
      private QueueSession qsession;  
      private QueueSender qsender;  
      private Queue queue;  
      private TextMessage msg;  
        
      public MessageSendExecution(Call command,  
                ExecutionContext executionContext, Object connection) {  
           this.cmd = command;  
             
     final List<Argument> procArguments = new ArrayList<Argument>(this.cmd.getArguments());  
     this.queueName = procArguments.get(0).getArgumentValue().getValue().toString();  
     this.messageContent = procArguments.get(1).getArgumentValue().getValue().toString();   
      }  
   
      public List<?> next() throws TranslatorException, DataNotAvailableException {  
           return null;  
      }  
   
      public void close() {  
           // TODO Auto-generated method stub  
      }  
   
      public void cancel() throws TranslatorException {  
           try {  
                qcon.close();  
           } catch (JMSException e) {  
                throw new TranslatorException(e);  
           }  
      }  
   
      public void execute() throws TranslatorException {            
           InitialContext ic = getInitialContext();  
           initJMS(ic, queueName);  
           sendMsg(messageContent);  
      }  
   
      public List<?> getOutputParameterValues() throws TranslatorException {  
           return null;  
      }  
   
        
      private InitialContext getInitialContext() throws TranslatorException  
      {  
           try {  
                return new InitialContext();  
           } catch (NamingException e) {  
                throw new TranslatorException(e);  
           }  
      }  
        
        
      public void initJMS(Context ctx, String queueName) throws TranslatorException  
      {  
           try {  
                qconFactory = (QueueConnectionFactory) ctx.lookup(CNN_FACTORY);  
                qcon = qconFactory.createQueueConnection();  
                qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);  
                queue = (Queue) ctx.lookup(queueName);  
                qsender = qsession.createSender(queue);  
                msg = qsession.createTextMessage();  
                qcon.start();  
           } catch (NamingException e) {  
                throw new TranslatorException(e);  
           } catch (JMSException e) {  
                throw new TranslatorException(e);  
           }  
      }  
        
        
      private void sendMsg(String userMessage) throws TranslatorException  
      {  
           try {  
                msg.setText(userMessage);  
                qsender.send(msg);  
                qcon.close();  
           } catch (JMSException e) {  
                throw new TranslatorException(e);  
           }  
      }  
 }  
   

This is the important parts from the Execution Factory:

      @Override  
      public ProcedureExecution createProcedureExecution(Call command,  
                ExecutionContext executionContext, RuntimeMetadata metadata,  
                Object connection) throws TranslatorException   
      {  
           return new MessageSendExecution(command, executionContext, connection);  
      }  
   
        
      @Override  
      public void getMetadata(MetadataFactory metadataFactory, Object conn)  
                throws TranslatorException   
      {      
     final Procedure messageSender = metadataFactory.addProcedure("messageSender");  
     metadataFactory.addProcedureParameter("queue", TypeFacility.RUNTIME_NAMES.STRING, Type.In, messageSender);  
     metadataFactory.addProcedureParameter("message", TypeFacility.RUNTIME_NAMES.STRING, Type.In, messageSender);  
      }  
   

To post the message, use this SQL statement:

 call Times.messageSender('java:jboss/exported/MyQueue', 'My Message');  

Then monitor JBoss Management console or use your sample java JMS client to retrieve a messsage from queue:

 Sep 03, 2012 10:34:45 PM org.xnio.Xnio <clinit>  
 INFO: XNIO Version 3.0.3.GA  
 Sep 03, 2012 10:34:45 PM org.xnio.nio.NioXnio <clinit>  
 INFO: XNIO NIO Implementation Version 3.0.3.GA  
 Sep 03, 2012 10:34:45 PM org.jboss.remoting3.EndpointImpl <clinit>  
 INFO: JBoss Remoting version 3.2.3.GA  
 JMS Ready To Receive Messages (To quit, send a "quit" message from QueueSender.class).  
   
       My Message  

Note: this translator code is not optimized for performance or thread-safety, I wrote it just for illustration purposes. Areas of potential improvement:
  • implement UpdateExecution to make a translator handle INSERT SQL statement (more intuitive approach);
  • reuse connection to Queue;

Thursday, August 2, 2012

Poll vs Push in Database using JBoss Teiid

Sometimes you have to monitor a specific table or view in your database, and do some action when INSERT or UPDATE happens. There are two approaches - you can periodically poll the table using Quartz or any other scheduling tool. Or you can be notified by database itself when your table modified - database will push you the new data.
The second approach even more beneficial when your table get updates rarely (so you not waste CPU by returning empty results most of the time), or when you want to be notified immediately when the new data available.

The good solution to the task above on my mind is database trigger. The commercial database vendors (like Oracle, if I am not mistaken) provide a triggers which (when fired up) are capable to run an arbitrary java code. But what if you don't have Oracle, and the data source of your choice is mySQL, Apache Hive, H2, or even arbitrary CSV file, laying down somewhere on filesystem?

Here is my approach: I made an UPDATABLE VIEW for my table in H2 database under JBoss Teiid, then created a VIRTUAL FUNCTION (Teiid virtual functions are capable to run specified java class and method). Last step - define two triggers: INSTEAD OF UPDATE and INSTEAD OF INSERT. When my table in H2 got a new record, or existing one updated - my java class gets immediately executed by Teiid.

Implementation Details:
  • Below is the Teiid 8.1 model definition with VIEW, VIRTUAL FUNCTION and two TRIGGERs defined. Note a property "lib" - it is a name of JBoss 7 Module with your java class in it (yes, you have to create a JBoss module. It is a simple task and well documented).
 <vdb name="DynamicPortfolio" version="1">  
   ...  
   <property name="lib" value="com.test.teiid.translator.async" />  
   <model name="TrigView" type="VIRTUAL">  
      <metadata type="DDL"><![CDATA[  
             CREATE VIRTUAL FUNCTION RepHealth(  
                     healthTime varchar,  
                     policyKey varchar,  
                     objKey varchar,  
                     healthState varchar)   
                RETURNS integer   
                OPTIONS (JAVA_CLASS 'com.test.teiid.udf.HealthStatesTeiidFunction',   
                     JAVA_METHOD 'reportHealthState');  
           CREATE VIEW HS_VIEW OPTIONS(UPDATABLE TRUE)  
                as select * from Accounts.HEALTHSTATE;  
             CREATE TRIGGER ON HS_VIEW INSTEAD OF INSERT  
                AS FOR EACH ROW  
                BEGIN ATOMIC  
                     SELECT RepHealth(  
                          New.HEALTHTIME, New.POLICYKEY, New.OBJKEY, New.HEALTHSTATE)   
                     from HS_VIEW;       
                END;  
             CREATE TRIGGER ON HS_VIEW INSTEAD OF UPDATE  
                AS FOR EACH ROW  
                BEGIN ATOMIC  
                     SELECT RepHealth(  
                          New.HEALTHTIME, New.POLICYKEY, New.OBJKEY, New.HEALTHSTATE)   
                     from HS_VIEW;       
                END;  
      ]]></metadata>  
   </model>  
 ...  
 </vdb>  

  • The VDB changes above should work with Teiid's "dynamicvdb-portfolio" example, I installed this example on my Teiid in advance and created schema for additional "Accounts.HEALTHSTATE" table in H2. The DDL is below. You can add a new model right into portfolio-vdb.xml from the example.
 CREATE TABLE HEALTHSTATE  
 (  
   HS_ID integer,  
   HEALTHTIME varchar(8),  
   POLICYKEY varchar(8),  
   OBJKEY varchar(8),  
   HEALTHSTATE varchar(8),  
   CONSTRAINT HEALTHSTATE_PK PRIMARY KEY(HS_ID),  
 );  
  • Write your java class and package it as JBoss Module. The called java method should be static.
 package com.test.teiid.udf;  
 public class HealthStatesTeiidFunction {  
      public static int reportHealthState(String healthTime, String policyKey, String objKey, String healthState)  
      {  
           String ret = "At:" + healthTime + " for Policy:" + policyKey + " and Object:" + objKey + " the State is:" + healthState;  
           System.out.println(ret);  
           return 1;  
      }  
 }  

  • Start JBoss, make sure your module and VDB are deployed and active. Connect to your database and issue INSERT or UPDATE statement against your defined view.
 insert into HS_VIEW(HS_ID, HEALTHTIME, POLICYKEY, OBJKEY, HEALTHSTATE)  
 values(4, '10:28 AM', 'policy1', 'obj1', 'RED');   

  • See the java output in console:
 13:51:18,336 INFO [stdout] (Worker0_QueryProcessorQueue0) At:10:28 AM for Policy:policy1 and Object:obj1 the State is:RED  

Note that since triggers defined as "INSTEAD OF..", the actual insert or update does not happen. Also, in order to be notified by the database changes, your underlying system should issue inserts or updates not to the actual H2 table, but to Teiid UPDATEABLE VIEW we defined (HS_VIEW in example above). Teiid itself is a federated database, available by JDBC, but connecting to it might be an added complexity to your solution.

Wednesday, August 1, 2012

JBoss 7 and Maven Plugin

This days I play a lot with the latest version of JBoss Teiid (8.1.Beta2). As part of my development activity I have JBoss 7.1.1.Final running on my workstation, and I want to shorten my development cycle: modify code -> build war -> deploy. Yes, I know about JRebel, but today I want not to use it.
My project build tool is Maven, so solution is straight: use jboss-as-maven-plugin. I want to generate war file from maven and deploy it to standalone JBoss without restarting it.

Steps to follow


  • Define plugin in pom.xml. The definition below attaches to "clean" and "package" maven life cycles.
<project...>
    <build>
        ...
        <plugin>
                    <groupId>org.jboss.as.plugins</groupId>
                    <artifactId>jboss-as-maven-plugin</artifactId>
                    <version>7.1.1.Final</version>
            <configuration>
                <filename>${project.build.finalName}.war</filename>
            </configuration>
                    <executions>
                           <execution>
                                <id>undeploy</id>
                                <phase>clean</phase>
                                <goals>
                                    <goal>undeploy</goal>
                                </goals>
                                <configuration>                                           <ignoreMissingDeployment>

                                       true
                                   </ignoreMissingDeployment>
                                </configuration>
                           </execution>
                           <execution>
                                <phase>package</phase>
                                <goals>
                                    <goal>deploy</goal>
                                </goals>
                            </execution>
                    </executions>
            </plugin>
        ...
    </buiid>
</project
>


  • Optionally - create Eclipse launch configuration to execute "mvn package" or "mvn clean package".
  • Start JBoss, run launch configuration in Eclispe, check messages in your server log that new context is regustered and war file is deployed / replaced.

 

 What happens behind the scenes?

Usually deployed war files placed in <jboss7>/standalone/deployments folder (when standalone configuration used). After JBoss restart the war file gets deployed (new "xxxx.deployed" flag file gets created in the same folder).
In case of "jboss-as-maven-plugin" deployment, the <jboss7>/standalone/configuration/standalone.xml file gets modified: a new "<deployment>...</deployment>" added to it.
The actual deployed war file can be found now under one of the <jboss7>/standalone/data/content/ subfolders.