Enterprise Software Development with Java

Friday, January 27, 2012

Sneak peak at Java EE 7 - Multitenant Examples with EclipseLink


The Aquarium is a great source of inspiration and most recent information about Java EE progress across all relevant specifications and reference implementations. They picked up a presentation by Oracle's Shaun Smith (blog/twitter) about the status and future of EclipseLink as an open source project. He covers all the new features which are going to be in EclipseLink 2.4 which will be availabke along with the June Eclipse Juno Release. In detail these are REST, NoSQL and Multitenancy. (details refer to the complete slide-deck (PDF) from the marsjug event.)
I like to see that EclipseLink still is the center of innovation in Java persistence and they are trying hard to adopt latest buzz timely. Working for a more conservative industry in general the main new feature I am looking for is multitenancy. What you can guess from the slides is, that something in this field should already be working with latest EclipseLink 2.3.0.

What is Multitenancy going to look like?
Let's start looking at what Oracle's Linda DeMichiel announced at last years JavaOne (compare blog-post) and also let's look at what the early draft (PDF) of the JPA 2.1 specification has to offer. The easier part is the early draft. Not a single line mentions "Multitenan[t|cy]" in any context. So, this is obviously still a big to do for further iterations. A bit more could be found in the JavaOne Strategy Keynote (Slides 41,42) and the JavaOne Technical Keynote (PDF) (Slide 25). The general Java EE 7 approach will be to have support for separate isolated instances of the same app for different tenants. The mapping should be done by the container and be available to apps in some way. This is all very vague until today and the only concrete code examples available from the slides refer to some obvious JPA related examples using two annotations @Multitenant and @TenantDiscriminatorColumn. Hmm. Doesn't this look familiar to you?

What is possible today?
It does! EclipseLink (as of 2.3.0 - Indigo) supports shared multitenant tables using tenant discriminator column(s), allowing an application to be re-used for multiple tenants and have all their data co-located. All tenants share the same schema without being aware of one another and can use non-multitenant entity types as per usual. But be aware of the fact, that this is only one possible approach to multitenancy for data. This is commonly referred to as "dedicated database" because all tenant's data go into one single db! The basic principles for the following are:
- application instances handle multiple tenants
- caching has to be isolated for every tenant by JPA
You can look at all the details on a dedicated EclipseLink wiki page. Want to give it a test drive? let's start. Prerequisites as usual (NetBeans, GlassFish, MySQL, compare older posts if you need more help.). Make sure to have the right EclipseLink dependencies (at least 2.3.0)! Create a new entity via the wizard, setup your datasource and persistence.xml and call it e.g. Customer.
@Entity
public class Customer implements Serializable {
//...
}
If you start your app you see EclipseLink creating something like this in your database.
Let's make this a multitenant entity. Add the following annotations:
@Entity
@Multitenant
@TenantDiscriminatorColumn(name = "companyId", contextProperty = "company-tenant.code")
public class Customer implements Serializable {
//...
}
There are multiple usage options available for how an EclipseLink JPA persistence unit can be used in an application with @Multitenant entity types. Since different tenants will have access to only its rows the persistence layer must be configured so that entities from different tenants do not end up in the same cache. If you compare the detailed approaches (Dedicated PC, PC per tenant, PU per tenant) in more detail, you see, that as of today you end up having two possible options with container managed injection of either the PC or the PU. Let's try the simplest thing first.

Dedicated Persistence Unit
In this usage there is a persistence unit defined per tenant and the application/container must request the correct PersistenceContext or PersistenceUnit for its tenant. There is one single persistence unit and nothing is shared. Go with the above example and add the following property to your persistence.xml:
<property name="company-tenant.code" value="TENANT1" />
Give it a try and compare the tables.
As you can see, you now have your companyId column. If you insert some data it will always be filled with the property value you assigned in the persistence.xml. Use either a  @PersistenceContext or a  @PersistenceUnit to access your entities. Using this approach you have a shared cache as usual for your application.

@PersistenceContext with Shared Cache (Source: S.Smith)

Persistence Context per Tenant
If you don't want to have a single tenant per application you could decide to have a single persistence unit definition in the persistence.xml and a shared persistence unit (EntityManagerFactory and cache) in you application. In this case the tenant context needs to be specified per EntityManager at runtime. In this case you have a shared cache available for regular entity types but the @Multitenant types must be protected in the cache. You do this by specifying some properties:
@PersistenceUnit
EntityManagerFactory emf;
Map props = new HashMap();
props.put("company-tenant.code", "TENANT2");
props.put(PersistenceUnitProperties.MULTITENANT_SHARED_EMF, true);
EntityManager em = emf.createEntityManager(props);

Shared @PersistenceUnit per tentant (Source: S.Smith)

Discriminator Approaches
The above examples work with a single discriminator tenant column. You can add the discriminator column to the PK by specifying a primaryKey attribute like the following:
@TenantDiscriminatorColumn(name = "companyId", contextProperty = "company-tenant.code", primaryKey = true)
It's also possible to have multiple tenant discriminator columns using multiple tables if you do something like this:
@Entity
@SecondaryTable(name = "TENANTS")
@Multitenant
@TenantDiscriminatorColumns({
    @TenantDiscriminatorColumn(name = "TENANT_ID", contextProperty = "company-tenant.id", length = 20, primaryKey = true),
    @TenantDiscriminatorColumn(name = "TENANT_CODE", contextProperty = "company-tenant.code", discriminatorType = DiscriminatorType.STRING, table = "TENANTS")
})

This leads to a secondary tenants table.


Additional Goodies
As always you can do the complete configuration within your persistence.xml only, too. For a reference please look at the already mentioned wiki page. One last thing is of interest. You could also map the tenant discriminator column with your entity. You simple have to make sure it isn't updated or inserted.
 @Basic
    @Column(name = "TENANT_ID", insertable = false, updatable = false)
    private int tenantId;

    public int getTenantId() {
        return tenantId;
    }
Looking at the debug output you can see what is happening behind the scenes:
INFO: Getting EntityManager
INFO: Inserting Test Customer 
FEIN: INSERT INTO CUSTOMER (ID, TENANT_ID) VALUES (?, ?)
 bind => [1, 2]
FEIN: INSERT INTO TENANTS (ID, TENANT_CODE) VALUES (?, ?)
 bind => [1, TENANT2]
FEIN: SELECT t0.ID, t1.TENANT_CODE, t0.TENANT_ID, t1.ID FROM CUSTOMER t0, TENANTS t1 WHERE (((t1.ID = t0.ID) AND (t1.TENANT_CODE = ?)) AND (t0.TENANT_ID = ?))
 bind => [TENANT2, 2]

Curious for more Java EE 7 and JPA 2.1 goodies? Keep updated with the development status wiki page for the EclipseLink JPA 2.1 project.

Thursday, January 26, 2012

New Article in German iX Magazin. Time and Money in Java.


This is my first published article this year. The German iX magazin published an article of mine about the pitfalls of Java's time and money features.

Temporary employment
Time and currency calculations in Java
Dealing with time and currency formats in Java is complicated, difficult to handle, and thus a potential source of error. Only those, who  know the pitfalls of using standard APIs and frameworks are on the safe side.

This is a German article and you can either grab the latest issue online or buy it at your favorite kiosk.

If you like, you can check out some other articles of mine. Some of them done in English, too. You just need to search this blog for posts, labeled "article" and you get some results.

Monday, January 23, 2012

The Heroes of Java: Trisha Gee


The 11th part of my "Heroes of Java" interview series. Thanks for following it! Stay tuned for the next parts!

Trisha is a developer at LMAX, the London Multi Asset eXchange. She's been working in financial markets for the last 5 years or so, but a fear of boredom and healthy amount of job-hopping before then has given her a wide breadth of experience, in a range of industries, over the 10+ years she's been a professional developer. Currently trying to get her head around low-latency, high performance coding whilst also keeping her fingers in the other pies LMAX has to offer, such as continuous delivery and agile. Trisha is heavily involved in the London Java Community and the Graduate Development Community, she believes we shouldn't all have to make the same mistakes again and again.

General part
Who are you?
I'm Trisha - Java developer; geek; triathlete; photographer and socialite. I love problem-solving but I'm more fascinated by people and how teams, groups, companies and societies work, which is why I'm involved in the London Java User Group, and it's what draws me to the conference scene.

Your official job title at your company?
Senior Developer

Do you care about it? 
To some extent. At my company I'm the driving force behind evangelising the awesome technical stuff we've done and I'm heavily involved in our recruitment process, but I wanted to make sure my title was "developer" rather than anything more fluffy or more grand. I feel I won't be taken seriously by the technical community if I'm not an actual, proper developer.

Do you speak foreign languages? Which ones?
I learnt French at school but I'm very bad at it.

How long is your daily "bootstrap" process? (Coffee, news, email)
If you include all the time between waking up and actually starting "real" work, I guess we're talking nearly 4 hours! - First thing I do when I wake up is check my e-mail on my phone. This is a terrible habit I would like to break, because I have no intention of actually replying to anything and it just slows me down - Coffee and breakfast at home followed by the gym or a run. I can't function properly without some sort of physical exercise in the morning. - Then there's all the rubbish of getting ready for, and getting into, work. Since I'm usually thinking about what I'll be working on that day, and what my schedule is like, I think of this as part of the bootstrap. - Second coffee at work, during which time I catch up on e-mail & my RSS feeds, and check up on things like the status of test results in our continuous delivery pipeline. This usually takes from when I get in to our morning standup at 9:45 

Twitter
You have a twitter handle? Why?
Yes, I'm @trisha_gee. Originally I posted to it like a Facebook status update, because I never really posted anything on FB that I wouldn't make public. I still use it for that a bit - I like to be flippant on twitter, but I tend to be more technology focussed on there than I used to be. I link to interesting stuff I've stumbled over and to my blog posts and material related to me (of course!). I had a Twitter revelation at JAX London this autumn though, I found it very useful for engaging in conversations about the presentations or subjects at a conference, and it helps to bridge the gap between you and people you've never met before at these events. Since then I use it much more conversationally.

Whom are you following in general?
I'm terrible, I mostly use it to publish rather than subscribe - I write a lot to it but only dive in to read occasionally. I follow a lot of techies - my fellow London Java Community guys; a lot of people I've met at conferences; and a lot of people who seem to have found me via my blog, including a network of awesome women programmers. I also use it to get the odd bit of info about photography or sports events, and London-based cool stuff. 

Do you have a personal "policy" for twitter?
Twitter is largely my professional-facing persona, if you like. But that's so much a part of who I am that I like to tweet the odd thing that gives an insight into me as a person. We're not automata, and I think any division between personal and professional is either artificial, or so blurry it's not really a division.

Does your company restricts or encourages you with your twitter ussage? 
Neither. My Twitter is useful for work because part of what I do is evangelism, but I'm not pushed to do it or given time for it. But then I'm not discouraged from it either. I have no idea what I would do if I was told not to do it any more!

Work
What's your daily development setup? (OS/IDE/VC/other Tools)
Ridiculous machine for coding on: Fedora Core 13, 24Gb of RAM, a pair of 4-core Xeon E5620 with HT enabled. - IntelliJ IDEA - SVN - Two monitors I always have Chrome, IntelliJ, Pidgeon and a terminal open. In Chrome, I always have Google Mail, Reader, Calendar and Groups open (I love the "pin tabs" functionality), along with my work webmail and my blog real-time stats (I'm a bit OCD). In another chrome window I have the "information radiator" of our build pipeline, which is usually open on my second monitor, and an additional tab for AutoTrish our tool that shows the intermittency in our acceptance tests. When I'm not at work, I have my MacBook Air, which I thought was a terribly frivolous purchase but turns out to be one of the best investments I have ever made. I was going to use it just for blogging, but it's powerful enough for development too.

Which is the tool providing most productivity to your work?
Hmm. Probably my team. My actual tools just distract me. 

Your prefered way of interacting with co-workers?
We pair program most days. If we (my pair and I) have a question we will talk face-to-face like real human beings to the person with the answer to that question. This is very odd to me, I prefer instant messenger, but this turns out to be the most effective way to get answers and allows you to ask questions you didn't even know you had. Most of the technology department has lunch together in our kitchen on most days. I love this, we feel like we're in it together, we get to blow off steam and find out what the other teams are working on. We also get some insight into what's going on outside of day-to-day development, as a lot of those who count as "management" are there too.

What's your favorite way of managing your todo's?
I used to like lists in my notepad (always written in pencil). But because we pair program and rotate around the stories that are in play, I try and capture tasks on post-it notes so that they can be added to the Kanban board. I tend to use my e-mail inbox as a todo list for things which are not directly related to the code I'm working on, and I have several calendars to track time-critical things. I even have a whiteboard at home for todo items and shopping lists. Did I mention I was a bit OCD?

If you could make a wish for a job at your favorite company: What would that be?
Somewhere I'm surrounded by smart people; a job/company that enables me to travel, and to meet people; a role that allows me time to think and experiment and write and present; and, of course, plenty of time to write actual, useful, code.

Java
You're programming in Java. Why?
I studied it at university so it made sense to take Java roles when I graduated in 2001. Previously I had worked with VB and Pascal and Basic, and web technologies like HTML and JavaScript. Object Oriented Design started to make sense to me, and I stuck with Java. I'm not particularly precious about it, but with 10+ years commercial experience, it's frustrating to work in anything more unfamiliar. The fascinating thing we've found at my company is that Java can be a language suitable for high performance code. This is the area that currently interests me, especially as Java actually tries to abstract away many concepts that you might traditionally worry about, like which CPU instructions are the fastest for achieving a particular goal. It's almost a game to get Java to do what you want. And at the end of it, you have code which is not only fast, but really very readable.

What's least fun with Java? 
UI programming. But I haven't had to do it for years because I always work on projects with web front ends or APIs.

If you could change one thing with Java, what would that be?
I was rather taken with C#'s closures. I guess lambdas are a step in that direction.

What's your personal favorite in dynamic languages?
Now you've got me. I haven't actually tried any for a long time. Is that heresy? I'll have to say JavaScript because it's the one I have most experience with.

Which programming technique has moved you forwards most and why?
I think probably Test Driven Development, in terms of a real game-changer in my own code. I started looking into it, and trying it out, about five years ago. In my current role I really got to refine it because we use tests all the way from the unit level to acceptance (using our own DSL) and performance. The thing I've been working on most this last year is improving my Domain Driven Design. TDD can drive to you local optimisations, and can, if you're not careful, lead to losing the big picture. By thinking about the whole domain, modelling it (even if it's just scribbles on a white board) and having a ubiquitous language with the business, you should be able to write cleaner, clearer, more maintainable and extensible code.

What was the biggest project you've ever worked on?
In my early career I did not have enough knowledge of the overall architecture to absorb how big or small a project was. At Ford Motor Company, and at one of the investment banks, I worked on a number of large enterprise projects with teams of a dozen or more programmers on the section I was working on. Looking back, I think the complexity was integration rather than it being some massive application with huge amounts of functionality. In terms of complexity and sheer range of functionality, the project I'm working on at the moment is probably the biggest. Our team is quite small (about 15 developers in the whole company), but we all work on all areas of the system, which includes the matching engine, the user interface, our four different APIs (including a C# one), administration tools, reporting... basically everything an FSA-authorised financial exchange needs to run its business. I believe that keeping the team small, and focussing on one area every milestone, has created a good, maintainable product and a team that is capable of continuing to build on it.

Which was the worst programming mistake you did?
When I was working for a retail bank I deleted two and a half (out of four) test environments 30 minutes before our Christmas lunch... It was lucky everyone was out for the lunch because it gave me a few hours to restore everything. Lesson learned? Always check the directory you're in before you type "rm -rf"...

Arquillian with NetBeans, WebLogic 12c, JPA and a MySQL Datasource


You probably followed my posts about testing more complex scenarios with embedded GlassFish (Part I / Part II). Next on my list of things to do was to get this setup working with latest WebLogic 12c.

Getting Started
Follow the steps in the getting started part of my first two posts. There are only a few things you have to change to get this working. Obviously you need a WebLogic 12c. Grep a copy from the OTN download-page. Read and accept the license and download either the ZIP installer or the full blown installer for your OS. Arun Gupta has a nice post about getting started with the ZIP installer. This basically is about downloading, extracting, configuring, creating your domain. Assume you have a domain1 in place. Make sure to copy the mysql-connector-java-5.1.6-bin.jar to domain1/lib and fire up the server by startWebLogic.cmd/.sh in your domain1 root directory. Next you need to configure the appropriate connection pool. You also could do this using some WLST magic or with the new WebLogic Maven Plugin but I assume you are doing this via the admin console. Go to Domain > Services > Data Sources and create a MySQL Datasource AuditLog with jndi name "jdbc/auditlog". Make sure the server is running while you execute your tests!

Modifying the sampleweb Project
Now open the sampleweb project's pom.xml and remove the glassfish-embedded-all dependency together with the arquillian-glassfish-embedded-3.1 and the javaee-api. Now add the wls-remote-12.1 container and the jboss-javaee-6.0 dependencies:

 <dependency>
            <groupId>org.jboss.arquillian.container</groupId>
            <artifactId>arquillian-wls-remote-12.1</artifactId>
            <version>1.0.0.Alpha2</version>
            <scope>test</scope>
  </dependency>
  <dependency>
            <groupId>org.jboss.spec</groupId>
            <artifactId>jboss-javaee-6.0</artifactId>
            <version>1.0.0.Final</version>
            <type>pom</type>
            <scope>provided</scope>
        </dependency>

Now open your arquillian.xml descriptor and change the container settings to use the wls container:
 
<container qualifier="wls" default="true">
        <configuration>
            <property name="adminUrl">t3://localhost:7001</property>
            <property name="adminUserName">weblogic1</property>
            <property name="adminPassword">weblogic1</property>
            <property name="target">AdminServer</property>
            <property name="wlsHome">X:\path\to\wlserver\</property>
        </configuration>

Make sure to use the right target server and point to the correct wlsHome. Right-click the AuditRepositoryServiceTest in NetBeans and run "Test File". You will see the remote container doing some work:

22.01.2012 22:40:34 org.jboss.arquillian.container.wls.WebLogicDeployerClient deploy
INFO: Starting weblogic.Deployer to deploy the test artifact.
22.01.2012 22:40:46 org.jboss.arquillian.container.wls.WebLogicDeployerClient forkWebLogicDeployer
INFO: weblogic.Deployer appears to have terminated successfully.
22.01.2012 22:40:53 org.jboss.arquillian.container.wls.WebLogicDeployerClient undeploy
INFO: Starting weblogic.Deployer to undeploy the test artifact.
22.01.2012 22:41:00 org.jboss.arquillian.container.wls.WebLogicDeployerClient forkWebLogicDeployer
INFO: weblogic.Deployer appears to have terminated successfully.

And the test going green! If you look at the domain log, you can see, that the test.war module is successfully deployed and undeployed.

Remarks and Thoughts
Looking at what we have with WebLogic 12c (especially the new maven plugin) this all seems very hand-crafted. What would a WebLogic developer have done prior to that in a maven based project? He would have pushed the weblogic.jar to his local repository and use it instead of using any jboss-javaee-6.0 or javaee-api dependencies. If you try this with the Arquillian wls container you start seeing some weird exceptions like the following:
Loading class: javax.transaction.SystemException
Exception in thread "main" java.lang.ClassFormatError: Absent Code
attribute in method that is not native or abstract in class file
javax/transaction/SystemException
This is basically because only the wlfullclient.jar contains all needed classes for remote management via JMX. The magic weblogic.jar does have some additional class-path entries in it's manifest which could not be resolved if you put it to your local m2 repository. So you simply have two options left. Use the wlfullclient.jar (see how to build it in the docs) for testing and the weblogic.jar for your development or stick to the jboss-javaee-6.0 dependency for development and testing (scope provided). Both are valid alternatives. As you can see, the WebLogic container is still undocumented in the Arquillian documentation. You can find a more detailed documentation looking at the wls-container project on github.
Download the simpleweb-wls.zip project as a reference to get you started.
Thanks to Vineet and Aslak for the help!

Wednesday, January 18, 2012

Review: "JBoss AS7 Configuration, Deployment and Administration" by Francesco Marchioni


Packt was so kind to hand their latest publication to me for a review. JBoss AS 7 Configuration, Deployment and Administration promises to give you an expert's understanding of the single components that makes up the JBoss application server. Showing you how to use them, helping you to cut down the learning curve for this exciting product and guide you through configuration, management, deployment and advanced administration of JBoss AS 7.

Abstract: As the Java EE standard has evolved and matured, the API has become increasingly rich and complex. The next generation of application servers needs to be modular and configurable to run only specific services or containers. JBoss AS 7 promises to meet those requirements but the configuration of a complex application server is composed of a mix of administrative and management tasks which often overlap, generating confusion.

JBoss AS 7 Configuration, Deployment and Administration will give you an expert's understanding of every component that makes up the JBoss application server, and will show you how to use them, helping you to dramatically cut down the learning curve for this exciting product.

This book will guide you through configuration, management, deployment and advanced administration in a logical order to avoid the common pitfalls of setting up a new AS. The book dives into the new application server structure and shows you how to install it. You will learn how to configure the core AS services, including thread pools, the messaging system, and the transaction service. Finally, you will learn how to deploy and manage your applications and then tune the performance of your AS to achieve an efficient, indispensible application server.

Book: JBoss AS 7 Configuration, Deployment and Administration
Language : English
Paperback : 380 pages [ 232mm x 186mm ]
Release Date : December 16, 2011
ISBN-10: 1849516782
ISBN-13: 978-1849516785

About the Author
Francesco Marchioni (@mastertheboss) is a Sun Certified Enterprise Architect employed by an Italian company based in Rome. He started learning Java in 1997, and since then he has followed the path to the newest Application Program Interfaces released by Sun. He joined the JBoss Community in 2000, when the application server was running the release 2.X.
He has spent many years as a software consultant, where he has overseen many successful software migrations from vendor platforms to open source products such as JBoss AS, fulfilling the tight budget requirements of current times.
Over the last five years, he started authoring technical articles for O'Reilly Media and running an IT portal focused on JBoss products (http://www.mastertheboss.com).

The Content
The book has 380 pages. Chapter one starts with page 26, chapter 11 sumary ends on page 333. That's not too much overhead and the main part actually cover good content.

Chapter 1 is about installing JBoss AS 7.
It gives a basic introduction to the new application server, depicting its most important features and shows how to install it on your machine.

Chapter 2 covers the Application Server's configuration.
Beside the main configuration file and some core parts of it, such as like the Thread pool configuration it also looks into the Logging subsystem.

Chapter 3, talks about configuring Enterprise Services
and dives into modeling the core Java Enterprise services using the standalone configuration file.

Chapter 4, covers the JBoss Web Server Configuration
and completes the standalone server configuration by looking at the Web subsystem. This chapter also covers a full Java EE example.

Chapter 5, is about the JBoss AS Domain configuration
and teaches the reader how to shape the domain server configuration and the criteria behind the choice of a standalone or domain server configuration. 

Chapter 6, Deploying Applications on JBoss AS
covers all the details of application deployment. A dive into class loading mechanism is also included.

Chapter 7, Managing the Application Server
Covers the related management tools used to control the application server instances.

Chapter 8, talks about Clustering 
All the AS 7 clustering capabilities that provide scalability and high availability to Java EE applications.

Chapter 9, Load Balancing Web Applications
Covers another concern of clustering, which is the ability to make several servers participate in the same service and do the same work.

Chapter 10, Securing JBoss AS 7
covers the foundation of JBoss Security framework and how to secure Enterprise applications and the server management interfaces.

Chapter 11, Taking JBoss AS 7 into the cloud
The mandatory chapter these days covers AS 7 and Red Hat OpenShift platform.

Writing and Style
The general writing is easy to follow, at least for non native speakers like me. I was also feeling very comfortable with the terms used through the book. So no big surprises if you are experienced in the Java EE area. Hints and extra information is placed at the suitable and helpful positions and I also like the examples (configuration and more) a lot. Most of the more complex stuff is additionally explained with images and concept drawings which is also very helpful.

Conclusion and recommendation
This book doesn't require any existing knowledge of AS7 and introduces you from the ground up. Nevertheless, there are hints about upgrading existing installations all over the book. So, it has a lot to offer for both experienced and beginners. The structure is good and beside the mandatory cloud chapter I really enjoyed reading and playing along with the examples. Also very appreciated is the fact that there is not too much Java EE source code in the book and the author really tries to focus on deployment and administration. So don't be afraid about having to read a complete Java EE 6 tutorial along the way. Another point to mention is, that this book is far more complete than any available online documentation today. Especially the clustering and load-balancing parts are very interesting to read and also cover the various load balancing mechanisms. You are with JBoss? Go with this book!
My Photo
Markus Eisele
I am a principle technology consultant working for msg systems ag in Germany. As a software architect, developer and consultant. Also writing for German IT magazines. I works daily with customers and projects dealing with Enterprise Java and other new technologies, on a variety of platforms using different vendors. My main area of expertise are Java EE Middleware Servers.
View my complete profile

Mobify powers the best mobile e-commerce and mobile websites in the world. Tap to learn more

MOBIFY