Requirements for Java EE Configuration
In this post I want to outline, what I see a reasonable scope for a Java EE Configuration JSR. I would first try to summarize different use cases, people want to have to be covered. In a next part I try to deduce corresponding requirements. Finally I will try to discuss, what is needed to cover these aspects.Use Cases
First I will summarize the use cases that were mentioned so far in the discussion, especially also here.- An enterprise wants to centrally control their deployments (could be cloud, but is not restricted to). Hereby according configuration should be managed externally within a database or some other kind of data container. The configuration should be accessible with an administrative console and it should be possible to change configuration and remotedly update running instances affected by a configuration change. Especially administrative resources, such as data sources, thread pools, users, roles etc. should be configurable, so these aspects can be managed in a central location.
- For different stages or runtime environments different CDI components should be loaded that reflect the different runtime environments. This includes configuring CDI, Enterprise Java Beans and similar aspects (basically everything that can be configured with some kind of deployment constructor deployed with an ear or war archive).
- Depending on the stage and some additional aspects different configuration must be used (including, but not limited to deployment profiles). Advanced use cases include network zones, host, tier, server instance and more. Hereby the possible properties are not constraint and may heavily differ between applications and especially enterprises (runtime environments). Many applications use a file based configuration approach, which is often cumbersome and error prone.
- It should be possible to adapt/reconfigure COTS applications to better accomodate them into the concrete environments. It should be possible to deploy them on different stages, without having to redeploy anything (also because traceability of the deployment may be a legal requirement).
- It should be possible to provide configuration that goes beyond what is defined by the current EE standards. E.g. there are companies that redeploy the exact some jars in different setups, but reconfigure them (e.g. in multitenant, multiproduct scenarios). Thinking in extremes that could mean having a single application deployed multiple times very differently.
- Configuration should be accessible both using a Java API, as well as being injected using CDI mechanisms.
- It should be easily possible to use this configuration mechanism also in testing. Developers should be capable of easily configure their test setup that should be loaded.
- Similar to views in relational database configuration should be scoped, so only a subset is visible. This can be required for example because of security concerns.
- Finally for multi-tenant/SaaS setups configuration must be dynamic, so a SaaS provider can lookup configuration programmatically.
Requirements
This list is quite probably not complete. Nevertheless it is possible to deduce some requirements:
- It must be possible to store and manage configuration at an arbitrary place independent of the target deployments.
- It must be possible to store and manage configuration along the target deployments.
- It must be possible to provide configuration with deployment artifacts (jars, wars, ears)
- It must be possible to mix configuration from different locations.
- It must be possible to priorize configuration.
- It must be possible to combine configuration in different ways (override, extend, ignore duplicates, ...).
- It must be possible to change configuration and trigger the changes to interesting observers.
- Configuration must be loadable within early boot of a system, where no EE context is available.
- Configuration must be adaptable depending on the current runtime context.
- Configuration must be accessible/injectable using CDI mechanisms.
- Configuration must be accessible using a Java based API.
- Configuration services must allow to configure administrative resources, such as datasources, users, roles.
- The runtime environment must be modeled in a flexible way, because different companies have different deployment/runtime and consequenty configuration needs.
- Configuration must support staging. It would be possible to define a minimal staging type applied to an environment, But such a concept would require to allow concrete sub-stages to be modellable to accommodate also complex runtime environments.
- Configuration should be able to support a wide range of EE. If some areas would be excluded, we would stop half-way and destroying much of the benefits a powerful configuration mechanism can provide, since we in such case have to do workarounds as before.
- Configuration may be marked as read-only. An simple accessor should allow to determine if configuration is mutable.
- Configuration must be thread-safe.
- Whereas configuration itself must not be serializable, it must be possible to extract a current state of configuration and serialize this state (e.g. as immutable configuration). This enables sending configuration easily over network connections.
- Configuration must also support aspects that are currently not part of any EE standard, especially it must be possible to also configure application aspects. Without that you simply have to do workarounds to mitigate this lacking feature, distrying the benefits of other configurations.
The list is for sure not yet complete, but I tend to say the most important aspects are covered. If you are something missing, let me know, so I can extend the list above.
Conclusions
Give the huge areas of use cases above, one may think, it it will not be possible to define a useful scope for a JSR. I would fully agree, if one would try to solve everything from scratch modeling every aspect explicitly. But why should we? Configuration is per se quite an abstract concept, as we have seen also in previous blogs here. So lets keep configuration abstract and simple (KISS principle):
Configuration = Map<String,String> + Metadata
Metadata = Name + Map<String,String>
Finally we need some mechanism to access configuration. Given the requirements above it is not useful, to define configuration access as an interface injectable by CDI only, since it will prevent us of using configuration at early stages, where CDI is not loaded yet. Similarly it would not be possible to use configuration for configuring administrative resources. But if we would fail to cover these requirements, it is quite useless to file a JSR for this topic.
So basically configuration must IMO be provided by some simple accessor as a global service. Now some people might argue that this is not EE styled. So I would ask, what EE is all about, so I look up Wikipedia:
"Java EE provides an API and runtime environment for developing and running enterprise software, includingnetwork and web services, and other large-scale, multi-tiered, scalable, reliable, and secure network applications. Java EE extends the Java Platform, Standard Edition (Java SE), providing an API for object-relational mapping, distributed and multi-tier architectures, and web services."
So there is nothing said that is can not be done in that way. Additionally, we have some additional benefits:
- There is a clear separation of concerns. The configuration service has a clear, well defined API, how it can be accessed and what kind of features it must provide. The configuration service itself does basically not neet to know anything about its EE context, which is the way how modularized systems are built as of today.
- We have defined a simple interface, that allows us to hook in different kind of implementations and solutions to provide configuration. For example any vendor can provide additional configuration management tools and products that allow to manage your application server configuration centrally thus also creating additional benefits (and profits).
- Basically it can be left over to the implementations how the configuration provided is stored and retrieved. The JSR should focus on the mechanisms and interfaces, not on how different companies should manage their configurations.
- The interface and the access points must be defined by the JSR, but the effective data feeds can still be adapted to the needs of the concrete usage scenarios.
- Said that, this also enables us to support configuration in SE cases, especially for testing. Especially when CDI would also be configurable, it can be used (e.g. with CDI 2.0) in combination with configuration services also in test environments very effectively, which would simplify testing of EE a lot.
- Over the years the former complex and over-engineered EJB design was transformed into a lean POJO (POJI) based easy to use component model, where additional services and cross-cutting concerns can simply be annotated. It would look very strange if we would add a configuration mechanism that again only works within a EE context.
Additionally there are other possible things that might be useful:
- Finally configuration of application aspects is also required. Defining a generic and easily accessible configuration service allows to cover these aspects similarly, without having to file another JSR. Defining some additional annotations to be used with CDI for injecting configuration into managed beans should be rather trivial. Advanced use cases hereby may be ommitted and left over for future releases.
- provide configuration in different versions.
- node and cluster based configuration.
So there is IMO a strong coincidence for going that way, because
- the mechanism is simple
- the mechanism builds on top of existing interfaces (Map), hereby also supporting features such as Lambdas and streams out of the box
- the mechanism is not intersecting with CDI, because it is constraint.
- String keys and values, can easily be combined and filtered, so aspects included above can be easily supported:
- filtering/views
- templating/defaults
- intersections
- unions
- ...
One of the following key aspects would be, how we then integrate this new functîonality with all the existing JSRs? Basically there are two different possible ways to go forward:
- The JSR itself defines, how each JSR must support adaptable configuration.
- The JSR defines the basic mechanism. The JSRs to be included into EE8 should define on their own, how they want to make use the new mechanisms. Hereby the EE8 umbrella JSR group, lead by Bill Shannon and Linda DeMichels are coordinating the initiative.
I personally think going for (1) is not the way, it should be. I think each JSR itself has the best know how, what might be feasible points of integration with the new configuration services. Additionally this would also help to reduce the scope of the Configuration JSR drastically. As a precondition to that, the API to be used should be defined and stable very early in the overall process. My idea would be to have it defined the latest until end of this year (assuming we can file the JSR within next few weeks). So we have two years time to integrate it into the existing standards, which looks for me a reasonable time frame. At the same time I would suggest the configuration JSR provides some kind of guidelines and corresponding templates, so the other JSRs can easily adopt the new mechanism. As an example a template could look something like that:
Configuration for JSR 220 | |||
Configuration Key | Configuration Type | Availability | Expected Content |
javax.jpa.persistenceUnits | java.lang.String | application/ear startup | Comma separated lists of names of the registered persistence units. |
javax.jpa.persistenceUnit.[name] | java.io.InputStream | application/ear startup | Deployment Descriptor (persistence.xml) as described in section XXX |
Of course, the example above is not probably not complete. But it shows the main intend quite well. Nevertheless it would still be feasible that the configuration JSR makes proposals, how the other JSRs can be accomodated to the new mechanism, if other JSRs wish us to do so.
If then finally a JSR is nevertheless failing to use the new APIs, the configuration JSR and the EE overall planning would not be affected. This greatly reduces the risk for EE8 to be delayed, but still there is a reasonable chance that we can achieve a new flexible EE platform.
Proposal and Scope
Given all the aspects described above a configuration JSR will cover the following:
Next:
- Open the JSR
Until end of 2014:
- Define the Java API as far as possible
- Define a simple map based configuration abstraction, as basically outlines in my previous posts.
- Define a simple, but flexible and extendible Configuration Service (accessor) API. It consists of an accessor that is also loadable outside of CDI.
- Define a SPI to configure/back the implementation used by the accessor API.
- Define a configuration description template. Send it out to each JSR included in EE8, so each JSR can individually decide how the want to be configurable. The results then can be included either into updated JSRs, or/and in summarized form into the configuration JSR.
Basically this implies that the JSR will publish its early draft review until end of 2014.
In 2015:
- Discuss and coordinate configuration support with other JSRs.
- Define configuration keys/support for administrative resources.
- Provide fully functional reference implementation, so it can be included into Glassfish as one of the first modules.
In 2016:
- Finish the TCK and RI work and go final.
Comparing with the Proposal done by Mike Keith at last Java One
Mike Keith did several presentations during the last years at different conferences, e.g. at JavaOne 2013.
Mike hereby did quite a good job, basically the use cases match very well, what is defined above here. So also this proposal does not target things completely differently, but given the experience of Credit Suisse, CloudBees and other companies that run huge application server farms and cloud or cloud like infrastructures, there is one crucial aspect, where I really disagree:
- Do not define another archive format to provide configuration!
There are a couple of reasons, why I think, we should not follow the idea of configuration archives as proposaed by Mike Keith:
- We already know from experience with ears and wars that handling of archives is not always easy and often is cumbersome (especially during development). Whereas also in Credit Suisse, application configuration is delivered along the code as jar artifacts, it is only covering parts of the deployment.
- Additionally the JSR would have to define the format of the archive, define the stages and other sub schemas/folders so the archive covers all kind of aspects required. This would add complexity to the configuration JSR, without benefit for users.
- In Credit Suisse Datasources, thread pools, users, security roles, certificates and other resources are not managed as file resources primarly. Within the deployment process according files are generated, by this makes the deployment inflexible, since even simple changes require a complete redeployment. Similary the deployment packages have to rebuilt for each stage.
- Often configuration changes are assummed to be more easily deployable, but experience has shown that this must not be the case. Additionally there might be cases, where operations has to adapt a configuration, e.g. because a database or other resources has been moved. With archives the application has to be redeployed, which is exactly, what we want to prevent.
Instead, I would let it open, how the Configuration Service effectively is locating its configuration. As shown above we can define different configurations (and locations) more effectively as shown above in the template, but without implying constraints how this configuration must be deployed.
This point has been proven in discussions during the last year with leading engineers and Java Champions. Also other colluegues have written blogs on this e.g. see http://blog.loof.fr/2013/10/configuration-management-jsr.html
So my proposal would be slightly adapted:
Higher Level Services
As already mentioned also at https://java.net/jira/browse/JAVAEE_SPEC-19 and also as commented by Arjan Tijms, we must be capable of providing higher level services on top of that very basic concepts. Most prominent use cases are the deployment descriptors, located at several different locations throughout the system, but there are also other settings that may be considered. I outlined in the previous blog, some of the possibilities to add additional functionalities using generic functional extensions. Tijms has outlined, that an additional higher level API for providing configuration information to the different components in a EE container would be useful, which may go beyond adding specialized configuration adapters. I would not disagree on that topic.Basically the template idea above could then similarly extended, so instead of modelling the template entries on the low level, as described above, we would model a high level API (e.g. as an interface?), that is finally provided by the configuration JSRs. This would shield existing components from the details of the configuration service.
Since different components in a EE container are configured rather differently, it might require to define multiple interfaces to be provided. I would like to postpone this at this point, since this topic is worth it's own blog entry ;-)
Feedback Required
I would like to encourage all readers to give feedback on this blog. Please do not only raise your concerns, but also give positive feedbacks, what you think. It would help to see, if this would be a feasible way to go ahead.
Hi
ReplyDeleteThat's basically aligned with DeltaSpike Configuration part ( http://deltaspike.apache.org/configuration.html ). You have an utility method to be able to use it everywhere (not only CDI), an abstraction with nice defaults to read from in the app or outside and an integration with frameworks to make it easy to use.
Something else to look at, if you haven't seen that is Typesafe config: https://github.com/typesafehub/config
ReplyDeleteThe general idea seems not too different.
What about the ability to use EL in all Java EE deployment descriptors (web.xml, faces-config.xml, persistence.xml, etc)
ReplyDeleteJBoss has an option to enable this and it works really well.
Or, we would support EL within the configuration services, which may have a similar effect...?
ReplyDeleteLooking at the way, Parfait uses JSR 275 http://code.google.com/p/parfait/wiki/IntroductionToParfait to support monitoring systems like PCP (http://oss.sgi.com/projects/pcp/faq.html) which, originally started by SGI, many Open Source and Cloud players like Red Hat also use, you may get an idea, how JSR 363 could be used for more "semantic" configuration via the right PropertyAdapter... Typesafe tried that on a very low and vague level, i.E. supporting the "usual suspects" like "100 ms" via Java Concurrency or "3 GB" for storage, but it seems even "42 cel" (using UCUM) for a maximum temparature of 42 °C a device may still be safely operated under, won't work? ;-) Integrating JSR 363 you can define configurations in all units, the Unified Code for Units of Measure (UCUM) allows, or define your own custom unit system if required.
ReplyDeleteI think there are two different aspects of Java EE configuration.
ReplyDeleteWhat's presented here (and if I'm not mistaken is pretty much what had been presented from the get go) is essentially a default key-value store, specifically targeted for configuration purposes.
Such a key-value store is certainly needed in Java EE. The current "standard" mechanism is a combination of context-params and env-params in web.xml, system properties etc. The fact that there are many sources each having their own API is painful. context-params are a pain since you e.g. can't easily switch them between stages and you need the ServletContext to be able to access them, but this ServletContext can not be obtained statically from everywhere.
Key-value stores for configuration are mostly used to get values into user code. For example a setting for the "feedbackEmail" that a web app displays somewhere on the site it renders.
Java EE itself occasionally also makes use of the same mechanism. E.g. JSF uses context-params like "javax.faces.FACELETS_REFRESH_PERIOD". In rare occasions a value can also be set via JNDI (e.g. "javax.faces.PROJECT_STAGE").
The other aspect to Java EE configuration concerns the structured deployment descriptors and annotations, which may additionally be backed by programmatic configuration during EE startup (like adding Servlets and Filters programmatically).
For instance the following fragment from web.xml:
400
/WEB-INF/errorpages/400.xhtml
This is also configuration, but it's not strictly key-value based. Additionally, where the key-value store is mostly consumed by the application's user code, deployment descriptors (excluding their key-value features) are almost exclusively consumed by the Java EE platform itself.
For this aspect of Java EE configuration things like swapping between deployment descriptors, augmenting and overlays matter. Placeholders within the descriptors and annotations (EL based or not) are also important here.
The key-value store has a couple of existing standalone implementations. The above mentioned DeltaSpike Configuration and Typesafe config are good examples.
The deployment descriptor aspect doesn't have any existing standalone implementations. This is not surprising, as the processing of deployment descriptors happens via very container specific code in each Java EE server, and there's no SPI or hook via which external code can provide extensions for this mechanism.
However, various vendors like Red Hat and Caucho among others have indeed extended the deployment descriptor loading and processing mechanism and provide value added features like the above mentioned overlays and substitutions.
In https://java.net/jira/browse/JAVAEE_SPEC-19 I called this second aspect "configurable deployment descriptors".
What I'm seeing as a bit of an issue is that this proposed JSR still seems to be very focussed on the "key-value store" idea, but doesn't really take the "configurable deployment descriptors" idea into account.
I'm not sure that asking each individual JSR to leverage the key-value store as they see fit is really going to work. I mean, we have had simple system properties since the beginning that could have been used to provide alternative deployment descriptors (like in the javax.jpa.persistenceUnit.[name] example above), or JNDI could have been used for that, but it never happened.
If the umbrella EE 8 coordinates this there might be some hope of at least getting something done, but I still feel something more is needed.
Hi Arjan
DeleteI also would say the most easy way of integration is providing the existing deployment descriptors using the configuration mechanism, e.g. as InputStreams or Strings.This still allows us to store them at any kind of location (deployed with ear, from the file system or any remote location, including databases etc). The according value in the store simply defines the location, where the descriptor is stored. An according resolver/adapter then models the bridge between the descriptor and the effective deployment descriptor(s) (and even could additional dynamic behaviour as useful).
Or we can use EL ${expression}. In Credit Suisse, we have such a mechanism in place, e.g. for
- redirecting entries to other entries, including considering stage specific entries.
- decryption of encrypted configuration values, e.g. passwords or sensitive links.
I am personally open, which of the different variants would be used, because all at the end allow to configure an application externally. The EL mechanism is needed anyway, and easy to understand, so it would be probably sufficient.
Given that, a (single) web.xml e.g. could be accessed as
String webXml = config,getText("javax.servlet.web.xml.myappid");
With web fragments things get a bit more complicated, but the principle remaind the same, e.g.
(one way to achive this):
int fragmentCount = config.getInteger(javax.servlet.web.xml.myappid.count",0);
for(int i=0; i< fragmentCount; i++){
String webXml = config,getText("javax.servlet.web.xml.myappid." + i);
}
A true Java EE aware configuration service might provide each JSR with central but some more high level services like loading deployment descriptors in various ways, with transparent resolution of overlays, includes and substitutions, but also providing an SPI where user applications can register a callback to function as an additional source for a specific deployment descriptor (like the ApplicationConfigurationPopulator in JSF, see http://jdevelopment.nl/jsf-22/#533)
ReplyDeleteMost Java EE configuration items in deployment descriptors are backed by annotations, following the rule that XML overrides annotations when both are specified. Possibly the high-level service can provide some support for this mechanism as well.
All in all the really low-level key-value store is absolutely needed, but something more high level really dealing with configurable deployment descriptors is IMHO also needed. Existing implementations of this concept as done by Red Hat (JBoss) and Caucho (Resin) can be taken as a source of inspiration.
Hi Arjan (sorry for the mispelling previously)
Deletesince my aswer is a bit lengthy, I will extend the blog above. But as said, I agree.
Regards,
Anatole
Anatole,
ReplyDeleteOn your request at GeeCON 2014, my feedback is now available at:
http://blog.arungupta.me/2014/05/configuration-jsr-javaee8-what-should-be-covered/
Enjoy!
This comment has been removed by the author.
ReplyDeleteFor all of the Spring aficionados, it's worth having a look at the related Configuration and Profile information as starting guide on how many of us perform the type of configuration outlined in the above post:
ReplyDeletehttp://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/context/annotation/Configuration.html
There is also the PlaceHolderConfigurerSupport class that a lot of us use to perform this type of configuration:
http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.html
I blogged last year about how I typically configure this to allow externalised config to be specified at deployment/run-time:
http://www.javacodegeeks.com/2013/07/overriding-a-packaged-spring-application-properties-file-via-an-external-file.html
(removed original post due to incorrect link and formatting issues)
In general I like the proposal Anatole, and the KISS-based key/value approach appears the correct method IMHO
ReplyDeleteArun also makes some great points in his post; +1 especially for the REST API and learning from other modern web frameworks
In my experience with implementing this type of configuration, some challenges will exist:
- Can configuration be updated at runtime, and what's the expected behaviour (I assume this would be JSR-specific?)
- How are config updates managed and coordinated in a distributed (cloud?) environment
- Providing 'sensible defaults' is often very challenging in my opinion
- Hierarchy, overriding and namespace collision are also a big concern. If library developers include config in their JAR with a namespace that collides with others, how is this handled? Is the order of loading an issue?
I'm not sure XML is the correct medium for this kind of config, and a lot of modern web frameworks have moved towards the Map/properties model, e.g. Play 2 framework (based on the Typesafe work already mentioned) and Spring Boot:
http://www.playframework.com/documentation/2.0/Configuration
http://docs.spring.io/spring-boot/docs/1.0.2.RELEASE/reference/htmlsingle/#using-boot-configuration-classes
The typesafe config library (used by Play2) has a nice pattern for defaults [1] and overriding.
DeleteIMO it's worth separating an effort to define text based key/vale config formats with an effort to define an API - bundling them together will probably make it harder to extend the number of backends available.
[1] https://github.com/typesafehub/config#how-to-handle-defaults
Yep, default handling is done nice there, I would even add one variant: pass it along the method accessing the config value, e.g. instead of accessing
Deleteget("foo");
you may write
get("foo", "defaultFooValue");
The same would also work with adapters for type safe configuration.
Basically IMO Java configuration must be multi-format capable. It may define a default format to be used, so there is at least one existing format that developers know they can use. Given a useful extension SPI, other third parties should be enabled to provide additional formats as needed. What will end up as part of the API/spec and what will be just an implementation aspect (RI) will for sure be a topic for the expert group.
Hi Daniel
Deleteabout your points:
"Can configuration be updated at runtime, and what's the expected behaviour (I assume this would be JSR-specific?)"
-> Mutable configuration is a requirement. There are different use
cases:
1) the configuration is updated (if it is not read-only),
e.g. using put, remove programmatically.
2) the configuration is updated by internal changes,
e.g. changes on the remote representation.
-> In both cases, according ConfigurationChangeEvent must be
provided to all interested parites, also containing a detailed list of the changes done.
-> Listeners can then react as needed.
-> I suggest configuration can be serialized, so it guaranteed, that a
configuration state can be transferred over the network. The same
must be true for the change events.
"How are config updates managed and coordinated in a distributed (cloud?) environment"
-> I do not think, that the JSR can cover these aspects directly. But
the mechanisms above help definitively implementing such
features relatively easily...
"Providing 'sensible defaults' is often very challenging in my opinion"
-> what makes sense is typically a problem issue, not a design
issue.
-> defaults can added within the code that consumes the according
configuration in the CP
-> defaults can be defined externally
-> defaults can be even dynamic.
-> How far will end up in the final spec, is open. The current GitHub
repo already supports all the scenarios above.
*Hierarchy, overriding and namespace collision are also a big concern. If library developers include config in their JAR with a namespace that collides with others, how is this handled? Is the order of loading an issue?*
-> By default I would definitively not try to handle it implicitly or
based on order, but see it as a deployment problem.
-> a feasible resolution mechanism makes sense
-> but what will be part of the final spec, and what will be part of the
RI, also here will be to be discussed.
In general aspects, where it is difficult to find common sense, are good candidates to not include into the spec, but allow/support for flexibility in the implementation. If best practives evolve over time, additional aspect may go into the standard in a later release. Also given the timeline, we must be careful, what will effectively end up in the final JSR's scope.
From my point of view the basic configuration mechanism, its Java API, common parts of the SPI (access and annotations) and all EE related services should be part of the JSR, since these are all the aspects application programmers and server developers will have to deal with. More special or arguable aspects nevertheless may end up as part of the RI and not (yet) specfified.
David/Anatole - cheers, the typesafe approach to defaults looks very sensible.
DeleteAnatole - thanks for clarification on the other points as well, and sorting conflicts via a resolution mechanism (at deploy time) makes a lot of sense
About integration of the Config JSR with the EE stack there is also an interesting discussion with Antoine Sabot-Durant if CDI 2.0 would be capable of being the main container/integration architecture for EE8, see https://t.co/redirect?url=http%3A%2F%2Ft.co%2FO6UQaGiTZJ&t=1&sig=a49db8f8752c30bdae3a73723f791915b597913a&iid=73dbe1d4b8724d01a95c8b5d0fe5c000&uid=1038110960&nid=136+1028
ReplyDeleteGreat Article
ReplyDeleteJava Online Course | Java EE Training
Java Training Institutes in Chennai | java j2ee training institutes in chennai | Java Training in Chennai | J2EE Training in Chennai | Java Course in Chennai
Java Interview Questions | Java Training Institutes | IT Technical Articles
You have provided a nice article, Thank you very much for this one. And I hope this will be useful for many people. Salesforce Training India
ReplyDeleteNice information, valuable and excellent design, as share good stuff with good ideas and concepts, lots of great information and inspiration, both of which I need, thanks to offer such a helpful information here.
ReplyDeleteibm full form in india |
ssb ka full form |
what is the full form of dp |
full form of brics |
gnm nursing full form |
full form of bce |
full form of php |
bhim full form |
nota full form in india |
apec full form