2004, Ivelin Ivanov, Ryan Campbell, Pushkala Iyer, Clebert Suconic, Mark Little, Andrig Miller, Alex Pinkin
2005
Table of Contents
JBoss does not follow to the letter any of the established development methodologies. JBoss borrows ideas, learns from experience and continuously evolves and adapts its process to the dynamics of a largely distributed, highly motivated, and talented team.
This document explains the background and walks through the tools and procedures that are currently used by JBoss for project management and quality assurance.
The JBoss development process reflects the company core values,which incorporate the spirit of open source, individuality, creativity, hard work and dedication. The commitment to technology and innovation comes first, after which decisions can be based on business, then competition.
A typical JBoss project enjoys active support by the open source community. The ongoing collaboration within the community, naturally validates the viability of the project and promotes practical innovation. This process leads to a wide grassroots adoption of the technology in enterprise Java applications.
While community support is the key factor for the widespread adoption of JBoss technology, there are other factors that lead to its successful commercialization, such as return on investment (ROI) and total cost of ownership (TOC). They require JBoss to offer products with strong brand, long term viability, and low maintenance costs. Companies that rely on JBoss products should be able to easily hire expertise on demand or educate existing engineering resources. They should also feel comfortable that the market share and lifespan of these products will protect their investments in the long run.
The dilemma posed to the JBoss development process is how to enable a sound business model around sustainable and supportable products, without disrupting the fast pased technology innovation. The traditional process of gathering requirements from top customers, analysing, architecting, scheduling and building software does not work in the JBoss realm. It ignores the community element and conflicts with the principle that technology comes first.
On the other hand great technology does not necessarily lend itself to commercialization directly. Professional marketing research is needed to effectively determine the best shape and form to position a technology. It is frequently placed as a building block of a broader offering targeted at identified market segments. Ideally it should be possible to "package" technology into products on demand.
To allow harmony between business and technology, JBoss defines a simple and effective interface between the two. The interface is introduced in the form of integration milestones. At certain points of time, pre-announced well in advance, stable versions of JBoss projects are selected, integrated, tested and benchmarked in a coordinated effort. The result is an integrated Middleware stack that is referred to as the JBoss Enterprise Middleware System (JEMS). JEMS is not a single product but a technology stack that can be used for packaging marketable products.
While core JBoss projects evolve and release versions at their own pace, stable versions are regularly merged into JEMS to fuel its continuous growth as a comprehensive platform. Major JEMS versions are spaced out at about 12 months with intermediate milestones on a quarterly basis. This allows sufficient time for the industry to absorb the new features and build a self-supporting ecosystem.
For example the JEMS 5.0 milestones were announced in December of 2004. The first milestone - JEMS 5.0 Alpha is targeted for Q1Y05. It will introduce a standards based POJO Container, which allows a simplified programming model based on the new EJB 3 standard APIs. JBoss Cache will be one of the projects integrated in JEMS 5 Alpha. JBossCache has three pulblic releases planned in the same timeframe - 1.2.1, 1.2.2 and 1.3. Only one of them will be picked for integration in JEMS 5 Alpha.
The second milestone - JEMS 5.0 Beta is targeted for Q2Y05 and will be the first attempt at a complete integration of core JBoss projects on top of a new JBoss MicroContainer. The JEMS 5.0 Final milestone in Q3Y05 will complete the development cycle by presenting an enterprise grade middleware stack, which is certified and fully supported by JBoss and its authorized partners. Any subset of JEMS 5 could be extracted and deployed in production environment, because its components will have been thoroughly tested to work together and perform well.
The JEMS milestones have minimal impact on the progress of the individual JBoss projects. Their purpose is to set expectations for the timing of the integration phases. The process itself is controlled and executed by the QA team in collaboration with each project development team. There are several phases in the development cycle between JEMS milestones.
Title | Productizing Process for JEMS |
Author | Andrig (Andy) Miller |
Creation Date | February 9, 2006 |
Status | Final |
Revision | 1.0.1 |
Filename | Productizing Process for JEMS |
Date | Revision | Status | Author | Description |
---|---|---|---|---|
February 9, 2006 | 0.1 | Draft | Andrig (Andy) Miller | Initial version. |
February 20, 2006 | 0.2 | Draft | Andrig (Andy) Miller | Incorporated feedback from Pierre Fricke. |
February 21, 2006 | 0.3 | Draft | Andrig (Andy) Miller | Incorporated feedback from Shaun Connolly. |
February 22, 2006 | 0.4 | Draft | Andrig (Andy) Miller | Incorporated feedback from Ryan Campbell. |
February 28, 2006 | 0.6 | Draft | Andrig (Andy) Miller | Incorporated feedback from Scott Stark. |
February 28, 2006 | 0.8 | Draft | Andrig (Andy) Miller | Incorporated feedback from Sacha Labourey. |
March 7, 2006 | 0.9 | Draft | Andrig (Andy) Miller | Incorporated feedback from Ivelin Ivanov. |
March 7, 2006 | 0.9.1 | Draft | Andrig (Andy) Miller | Incorporated feedback from Pierre Fricke. |
March 20, 2006 | 0.9.9 | Release Candidate | Andrig (Andy) Miller | Incorporated feedback from Adrian Brock, Andrew Oliver, Manik Surtani, Ben Wang, and Ben Sabrin. |
March 24,2006 | 1.0.0 | Final | Andrig (Andy) Miller | Incorporated feedback from Andy Oliver, Rich Friedman, Sacha Labourey, and Scott Stark. |
April 11, 2006 | 1.0.1 | Final | Andrig (Andy) Miller | Incorporated Pierre's and Shaun's web related checklist for releasing projects, that came from our lessons learned discussion with JBoss Messaging. |
This document will define the process to turn a release of an open source project into a revenue generating product for JBoss, Inc. It is NOT an all encompassing document in that regard though. This document focuses only on those aspects that are led by the development/engineering organization. It does not delve into what product management and services processes are for productizing a JBoss project. However, there are tasks described in this document that do involve product management and services as contributors to the development process in regards to productizing our projects.
This document will not try to dictate process within the development life-cycle of each project, but instead concentrate on the steps that are not directly related to development, but to product.
Below is an illustration of the development life-cycle of our projects:
The above illustration is just one way to visualize the development life-cycle of a project. Certainly, it is not a true spiral or circle, since all of the tasks above can and do happen in parallel.
At a given point in time, this life-cycle above stops to release something that is considered more than just a work in-progress, but something that contains a feature set, and a level of quality that the lead developer(s) are happy with. This is considered the “stable” release. This release is the one that we will focus on in terms of becoming a product.
The above illustration shows that point-in-time when a “stable” release is dropped. Normally, this is solely decided by the lead developer(s) based on the feature set and quality that they deem fit for the label of “stable” release. In this transition from developing code, testing, community testing and feedback, to a stable release the process for turning this into a product for JBoss, Inc. must run in parallel.
In this transition from community releases to a stable release, or a final release, that is ready for customer consumption, the developer cannot make the decision in isolation about when that stable or final release will be. This must be done in conjunction with all of the stakeholders identified in the following sections. If the productizing steps have not been completed, yet the software is ready, it will not be officially released. We can call it a release candidate, but not stable or final until all the productizing steps, that have been agreed to, are complete.
There are seven main areas that I would like to focus on in regards to taking software that is being developed in this life-cycle, and getting it to a point where it is ready to be released as a product.
Product Road Map Creation and Maintenance
Features
Productizing Tasks
Known Bugs
Improvements to Existing Features
Reference documentation
API Reference
Administration Guide
User Guide
On-line education
Trailblazers
Demonstrations
Training materials
Internal training materials
Support organization
System Engineers
Consultants
External training materials
Customers
Partners
Quality Assurance
Performance testing
Scalability testing
Soak testing
Integration testing
Availability testing
Certification testing
Development Tooling
JBoss IDE support for developers
Release the stable or final release
Community announcements
In the following sections, the “Who Does It?” column describes roles that are played. This is not meant to dictate that those tasks are done by non-development resources. The exception to this is product management and services. All other roles can be performed by the project developers, whether the are JBoss employees or outside contributors, if they so choose to do so. We would like the project to perform their work in as flexible a manner as possible. What we care about is delivering a high-quality product as quickly as possible, not who specifically does the work.
A product road map should be developed and maintained for each release of the project. It should contain at least the following:
List of planned new features for the release
The productizing tasks that are needed for the release
Address the high priority know bugs or issues with the previous release(s)
Improvements to existing features
The list of planned new features for the release should be discussed in the forums, and with developers from dependent projects and with product management. The interdependencies of many of our projects makes this critical. Feedback from our customers, in the form of surveys, support cases that are feature requests, feature requests from the forums, etc., should all be incorporated to come up with this list. The finalized road map should be in sync with product management’s product plans.
The productizing tasks that are discussed throughout this document should be incorporated into the road map. For example, let’s say that you don’t currently have a soak test, and that needs to be developed, tested, and executed to complete the productizing tasks for the next release. Then you would add that to the road map, and that would turn into JIRA tasks.
Where the known bugs and issues are concerned any fixes from previous releases that have to be reconciled due to overlapping development needs to be addressed. The road map shouldn’t necessarily address each one of those individually, but just make sure that the overall task is taken into account in the plan. Also, some of these issues may not be bugs, but merely that code needs to be re-factored or that performance enhancements have been identified in particular areas from either customers or our own testing, etc.
Improvements to existing features can take many forms, depending on the project. It may involve making a particular feature easier to use, it may involve making a feature easier to manage, etc.
A good example of a product road map is the following from the Portal project:
Some questions that are good to ask yourself as you prepare the road map are:
What do you plan to do for this release?
Have you prioritized the work?
Is the work specified?
Have you discussed it with others to validate the ideas?
Have you used feedback from your users?
What do you need from other projects?
Who uses your project? How will they be affected?
What do other projects want from you?
Do you have tasks for all the productizing that needs to be done?
Have you scheduled time for the productizing work, i.e. taken it into account, when estimating, what can be done for a release?
Other potential issues:
What do you plan to deprecate?
What do you plan to remove or retire?
Should you really be doing that in your project?
Is the work already done elsewhere? Don't fall into the "Not Invented Here" trap.
What third-party dependencies do you plan to introduce?
How will that third-party software be supported?
What is the license for that third-party software?
One thing that I would like to stress, is that we want to create releases of reasonable size. The product road map shouldn’t contain every possible feature, fix, issue, etc. Use your best judgment in what can be delivered in a reasonable amount of time and prioritize accordingly. Remember, release early and often is the goal. There are no rules of thumb for how often releases should be made, because that is highly dependent on the project, its maturity, and the market demands.
Task | Description | Who Does It? | When Is It Delivered? |
---|---|---|---|
Road map creation. | Define the features, fixes, issues, productizing tasks, and improvements to be made for a given release of the project. | Development with feedback from other projects, the community, customers and product management. | Delivered before work starts on the release. |
Publish road map. | Post the road map on the jboss.org website (jboss wiki is a good tool for this), and create the JIRA release with associated tasks. | Development. | Delivered before work starts on the release. |
Reference documentation is produced in parallel with developing the code, and should evolve through the community releases, such as Alpha, Beta and Release Candidate releases. Reference documentation is an area that we do quite well, as illustrated by the examples below:
The JBoss 4 Application Server Guide
HIBERNATE - Relational Persistence for Idiomatic Java
JBoss Portal 2.2 Reference Guide
JBoss jBPM 3.1 Workflow and BPM made practical
TreeCache: a Tree Structured Replicated Transactional Cache
JBoss Microcontainer Reference
SEAM - Contextual Components A Framework for Java EE 5
JBoss EJB 3.0 Reference Documentation
These examples, all go over the public API of the software at a minimum. Installation, configuration and on-going administration, if applicable should also be covered. They are almost all published on the docs.jboss.org site, and they should all be accessible from there, even if they are not directly hosted there. Hibernate and Apache Tomcat documents are examples of this. The documents should be published in HTML, for easy on-line viewing, PDF for printing purposes, and if there are any examples, then the source code for those examples should be provided in an archive format such as zip.
Each project will certainly have different levels of documentation that is needed, but a plan for what the minimum for each project should be put together in conjunction with product management. Also, the documentation team, should be involved in creating the documentation, to make it navigable, presentable, and published in the two different formats required. A recommended set of documentation would be:
API Reference
The API reference should contain a definition of the public API. This public API will be backward compatible between minor releases. Private APIs can change between all releases, with the exception that they need to preserve binary compatibility. There is a more complete statement on API stability and compatibility between releases in the JBoss Product Versioning Wiki.
Administration Guide
User Guides
The user guide should contain, if applicable, a list of unsupported or experimental features that may be present, but are not recommended for production use.
Task | Description | Who Does It? | When Is It Delivered? |
---|---|---|---|
Minimum Content Definition. | Define the minimum documentation set that should be produced for the project (e.g. API reference, administration guide, user guide). | Product management and development. | Delivered prior to the first alpha release[a]. |
Documentation Creation. | Create the minimum defined documentation set for the project. | Development and the documentation team. | Delivered with each release, and iteratively updated as the project progresses through alphas, betas, release candidates through to a stable release. |
[a] This may only be applicable to new projects that haven’t had a stable release yet. Of course, a project may go through a significant structural change to warrant a change in the content definition. |
Note: The definition for alpha, beta, and release candidate are in the JBoss Product Versioning Wiki page. It is under the heading, “Current Qualifier Conventions (Post 2006-03-01)”. Here is the link:
On-line education primarily consists of two elements. Trailblazers and demonstrations. The Trailblazers and demonstrations should be produced in parallel with the code and should evolve through the community releases, such as Alpha, Beta and Release Candidate releases. Examples of these are as follows:
JBoss Seam DVD Store Demonstration
Overall page for Trailblazers and Demonstrations
Task | Description | Who Does It? | When Is It Delivered? |
---|---|---|---|
Needs assessment for trailblazer and demonstration. | Determine whether trailblazers and demos are needed to help market the project, and to help with adoption. | Product management and development. | Delivered prior to the first alpha release[a]. |
Trailblazer Creation. | Create the minimum defined documentation set for the project. | Development and the documentation team. | Delivered with each release, and iteratively updated as the project progresses through alphas, betas, release candidates through to a stable release. |
Demonstration Creation. | Write an application or record a demo of the usage of the project, whichever is appropriate. | Development and the documentation team. | Delivered with each release, and iteratively updated as the project progresses through alphas, betas, release candidates through to a stable release. |
[a] This may only be applicable to new projects that haven’t had a stable release yet. Of course, a project may go through a significant structural change to warrant a change in the content definition. |
There are five distinct audiences for training materials. First, and foremost is our customers. This training targets the developers and administrators that will be using our technology to develop applications and support applications respectively. Second, is the support organization. In order for them to be able to be as self-sufficient as possible, they need training. This training needs to be detailed enough that it helps them be able to troubleshoot issues that customers have. Third is our consultants. They need the same level of training as support, in that they will not only be helping to develop solutions in concert with our customers, but they will be the first line of support in solving development related issues (troubleshooting ability is key). Fourth is our system engineers. They need training similar to the consultants, in that they will be in front of prospective customers, and may have to delve into technical details during pre-sales activities. And fifth, is our partners. The training for our customers is what is, and will still be, used for their training.
The training materials for developers, can certainly fill part of the training needs for support, consulting and our partners. It will need to be augmented with training that is helpful for troubleshooting customer problems. This training material should have instructions on where errors are logged, and typical reasons that exceptions are thrown, etc.
Task | Description | Who Does It? | When Is It Delivered? |
---|---|---|---|
Define structure of training materials for customers. | Define the number and types of classes that the training materials need to support (e.g. Beginner, advanced, etc.). | Development with input from services. | The definition should be defined prior the first beta release[a]. |
Needs assessment for support, consulting and sales engineers. | Do a troubleshooting assessment for support/consulting, so that additional training materials, or training sessions determined. At a minimum this should contain a triage list for first line support that identifies what information needs to be collected for problem resolution. | Development and product management with services. | The needs assessment should be complete prior to the first release candidate. |
Develop training materials. | Develop the identified training materials for customers and from the needs assessment for support. | Development and documentation team. | The training materials should be delivered for testing purposes at the same time as the first release candidate[b]. |
Test training materials. | Test the training materials in a real classroom setting to make sure that the training materials are accurate, and that the labs actually work. | Services with feedback to development[c]. | Testing of training materials should be done at the time of the first release candidate. |
[a] This may only be applicable to new projects that haven’t had a stable release yet. Of course, a project may go through a significant structural change to warrant a change in the current structure. [b] This may have exceptions for newer technology or projects that we don’t anticipate will have significant uptake in the market on their initial stable release. These exceptions will be made on a case-by-case basis by product management. [c] The feedback should take the form of JIRA issue for the project, that would be defined as a blocking issue for a stable release. |
Note: The definition for alpha, beta, and release candidate are in the JBoss Product Versioning Wiki page. It is under the heading, “Current Qualifier Conventions (Post 2006-03-01)”. Here is the link:
All JEMS projects should be going through a standard quality assurance process[1]. There are five areas that need to addressed for projects where quality assurance is concerned.
The first area is performance testing. Every JEMS product should have a performance test that measures straight line performance of the product (single virtual user/client). The second area is scalability testing. Every JEMS product should have a scalability test that measures performance under high concurrent usage scenarios (many virtual users, clients, nodes, etc). This will be different depending on the JEMS product you are considering, but we should be able to sustain straight line performance levels (or at least not degrade very much) with high concurrency. The third area is soak testing. Every JEMS product should have a soak test that demonstrates sustained high performance with high concurrency over a long duration of time (catch issues like unintended object retention, leaked file descriptors, garbage collection issues, etc.). This test will run for a minimum of 24 hours. The fourth area is integration testing. All of the JEMS components that can be used in conjunction with each other for an application that our customers may develop or deploy, should be tested under scenarios that have them work together. The fifth area is availability testing. In this test, we should use our scalability scenarios and create fault conditions, so that we have a system under high concurrent usage, and are able to measure the ability to have failures and continue running.
These are all important quality aspects that our customers will expect to have nailed with each and every release of JEMS products. The testing described above is not meant to replace the existing unit test suites that each project already executes through their build process, or is it meant to replace any performance testing that each project may already have in place. What is described could very well leverage existing tests that projects already have.
Task | Description | Who Does It? | When Is It Delivered? |
---|---|---|---|
Define performance test scenarios. | For each JEMS project, there should be a performance test(s) scenarios defined with a goal for what the straight line performance should be (this could be relative to a baseline release, or relative to a competitors number, etc.) | QA and development. | Complete prior to the first release candidate[a]. |
Define scalability test scenarios. | For each JEMS project, there should defined what constitutes high concurrency for that given project, and what levels of concurrency should be the goal. | QA and development. | Complete prior to the first release candidate[b]. |
Define integration test scenarios. | For JEMS as a whole, scenarios should be defined that cross all of the integration points of the JEMS (e.g. Application that has a web tier that uses JSF/Seam, it has a middle tier that uses Stateless and Stateful session beans, and implements a workflow through jBPM, persists through EJB3/Hibernate, etc.) | QA and development. | Complete prior to the first release candidate of the application server (this is where most of the integration comes into play)[c]. |
Define availability test scenarios. | For each project, and for JEMS as a whole, test scenarios that inject failures in a high-availability configuration should be defined. Fault injection can be done through many techniques. Some as simple as unplug the network cable from a system, to some as sophisticated as having an aspect that is deployed that inject exceptions into the running application. | QA and development. | Complete prior to the first release candidate[d]. |
Build performance test. | Develop the appropriate test scripts to automate the performance test. | QA and development. | Complete and ready to execute by the time we offer silver support. |
Build scalability test. | Develop the appropriate test scripts to automate the scalability test. | QA and development. | Complete and ready to execute by the time we offer silver support. |
Build integration test. | Develop the appropriate test scripts to automate the integration test. | QA and development. | Complete and ready to execute by the time we offer silver support. |
Build availability test. | Develop the appropriate test scripts to automate the availability tests[e]. | QA and development. | Complete and ready to execute by the time we offer silver support. |
Execute performance test. | Run the test. | QA and development. | Should be complete prior to moving project to support levels above silver. |
Execute scalability test. | Run the test. | QA and development. | Should be complete prior to moving project to support levels above silver. |
Execute soak test. | Run the test. | QA and development. | Should be complete prior to moving project to support levels above silver. |
Execute integration test. | Run the test. | QA and development. | Should be complete prior to moving project to support levels above silver. |
Execute availability test. | Run the test. | QA and development. | Should be complete prior to moving project to support levels above silver. |
[a] This may not be needed for each and every release, as once they are developed the scenarios may not change. However, the goals of the runtime may change with each release. Therefore, at a minimum we should evaluate the goals with each release. [b] Same as seven. [c] This will probably only have to be done when there is a new JEMS component, once it is done the first time. [d] Same as seven. [e] This may be manual, unless it is identified that aspects are needed to be developed that will inject exceptions into the running application. |
Note: The definition for alpha, beta, and release candidate are in the JBoss Product Versioning Wiki page. It is under the heading, “Current Qualifier Conventions (Post 2006-03-01)”. Here is the link:
Many of the JEMS projects have development and management tooling requirements. Developers in our target customer base, are typically not the highest skilled developers[2], and certainly administrators need information and tools to make them more productive, especially in large deployments. Therefore, there is a need to make our platform as accessible to developers and administrators as possible through appropriate tooling.
Task | Description | Who Does It? | When Is It Delivered? |
---|---|---|---|
Define development tool needs. | Define what developers need to be productive developing against a specific project. | Product management and development. | Complete by the first alpha release. |
Develop tools. | Build the tools that have been defined for developers. | Development. | Complete by the time the release is considered stable[a]. |
Define management needs. | Define what administrators need to be productive managing a production deployment. | Product management and development. | Complete by the first alpha release. |
Develop management tools. | Build the tools, and the features within the product to expose management information for administrators. | Development. | Complete by the time the project moves to support levels above silver. |
[a] This may or may not be a hard requirement depending on the project, and the market adoption rate. Exceptions to this should be approved by product management. |
Note: The definition for alpha, beta, and release candidate are in the JBoss Product Versioning Wiki page. It is under the heading, “Current Qualifier Conventions (Post 2006-03-01)”. Here is the link:
Again, to reiterate what has been said already. The designation of the final release should never be done in a vacuum. This should be coordinated through product management. No one within JBoss should read about a release of our software without knowing about it in advance. This allows us to coordinate all public relations activities, as well as do a final check on whether sales and services are truly ready to go.
Task | Description | Who Does It? | When Is It Delivered? |
---|---|---|---|
Review release content and timing. | Review the plan for what will be released, in terms of web site content, and exactly what the timing will be. | Product management with development. | Approximately four weeks prior to final release. |
Review product data sheet. | Review the product data sheet with product management, to make sure that it is accurate. | Product management with development. | Approximately one to two weeks in advance of the final release. |
Review press release. | Review the press release with product management for accuracy of information. | Development with product management. | Approximately one week in advance of the final release. |
Verify that agreed to productizing steps are complete. | Make sure that all the agreed and applicable productizing steps from sections one through five have been completed. | QA led with project management and development. | Just before final release. |
Verify license conformance for distribution. | Verify that the final distribution conforms to the license that it uses, and that third-party libraries licenses are being complied to. A statement of all licenses involved in the distribution and what they apply to. This should included a source code header check for all the files. | QA led with development[a]. | Just before final release. |
Create/Update JBoss.org product pages. | Create or update the Jboss.org product pages, documentation, and downloads with new release. | Development. | On release day. |
Test links on website. | Test all of the links from the new release information, downloads, etc., to make sure that everything is functional. | Development. | On release day. |
Internal communication. | Development informs QA that the release is complete, and tags the source repository appropriately. | Development. | On release day. |
Announcement. | Certification of the release, and communication is made to internal stakeholders and community forums. | Development and QA with review from product management[b]. | On release day. |
Official corporate announcement. | Marketing collateral, PR, JBoss ON, etc. | Product management. | Determined by product management. |
[a] This could be generated by the build process, and not necessarily have to be done manually. [b] It is important that release announcements are reviewed by product management to make sure that our messaging is in sync across all communication channels. |
Note: The verify license conformance task is a part of producing the final build and distribution, and is not intended to be the time where all license issues are addressed. Issues around whether the licenses are compatible, whether we can use the code within our projects, etc., are a part of due diligence before productizing processes begin. There is a formal license policy in development, that will be linked to here, when it is complete.
Contact Name | Role | E-Mail Address | Related Mailing List |
---|---|---|---|
Andrig (Andy) Miller | Process owner. | ||
Ivelin Ivanov | Development management. | ||
Shaun Connolly | Product management. | ||
Ryan Campbell | Quality Assurance. | ||
Norman Richards | Documentation. | ||
Damon Sicore | JBoss.org website. |
[1] The probable exception to this rule is JBoss IDE, as it is a development tool, and really doesn’t fit the profile for these types of tests. Of course, JBoss IDE should do some form of performance testing around UI responsiveness and memory footprint.
[2] If you have every heard the presentation given by Dave Thomas of The Pragmatic Programmers” titled “Herding Race Horses, and Racing Sheep” you have seen empirical evidence of the fact that most developers are either Novices or Advanced Beginners, and they are NOT competent.
JBoss utilizes JIRA for product lifecycle tracking. It is used during the requirements gathering, task scheduling, QA and maintenance stages of a product lifespan.
JIRA is an overall excellent issue tracking system. However as of version 3.0 Enterprise it does not offer sophisticated project planning and tracking functionality such as calculating critical path, reflecting task dependencies, resolving scheduling conflicts, and resource calendar. These shortcoming can be partially mitigated by splitting development into short iterations (1-2 months) in order to reactively manage diviations from the base line schedule.
To begin the development of a new JBoss project, it needs to be registered in the project management system - JIRA. To do that you need to contact a JIRA Administrator.
Once the project is created, you will need to create a version label for the first (or next) production release. Under this release there will be several "blocking" tasks such as requirements gathering, coding, documentation, training material and QA. As a best practice issues should be only closed by their original reporter.
In addition to the production release there you will need to create versions for the intermediate releases at the end of each iteration. See the project named "README 1st - JBoss Project Template" for a starting point.
The Release Notes for a product version are generated automatically by the Project Management System (JIRA) and additionally edited manually when necessary.
To mazimize the value of the automatically generated Release Notes and minimize the manual work, the following giudelines are in place:
Use concise but descriptive issue names
Open the right kind of issue.
In order for an issue to appear in the release notes for a given version it needs to have its field "Fix Version/s" set to the given version. Usually an issue affects only one particular version and it is fixed within that version. Sometimes however an issue affects multiple versions and it is addressed for each one of them. In the latter case the "Fix Version/s" fields comes handy.
JIRA offers voting mechanism that helps determine the number of people asking for a task as well as who these people are. JBoss Project Leads consult these votes in order to schedule tasks. All other developers in a project coordinate their time and tasks with the project lead. A select number of stakeholders have overriding power for task priorities. The JBoss CTO has the highest authority on development task priorities. When there is ambiguity on task priorities, contact your project lead or development manager.
Possible priorities are:
Due dates are normally used for scheduling project versions. When entering issues, time estimates should be preferred to due dates. Issue due dates limit the project management software capability to level resources and optimize scheduling.
To support the updating of release notes and documentation, the Affects field offers several flags when creating or editing an issue.
Projects such as JBoss Application Server package components from several other projects such as JBoss Cache, Tomcat, JGroups, and Hibernate. To manage the development cycles between these projects the following guidelines apply:
The source code repository of a container project includes the full source for all composing components. For integrated components, the source repository includes integration source code and stable binaries of the related standalone projects. Building a container from source, compiles the source code for its composing parts as well as integration code, but it does not pull in the source for standalone projects.
A container testsuite includes the tests for all composing components as well as the integration tests for embedded compoenents. It does not include the tests that are part of the standalone testsuite for an integrated component. For example JBoss AS testsuite covers the HAR deployer, but it does not include tests from the standalone Hibernate project.
Container projects such as JBAS consist of components, some of which are integral to the container (such as CMP, IIOP) and others are based on external projects (MicroContainer, JBossCache).
For each container version and each component based on external project, there should be an integration tasks created in the container project. The task should specify which version of the external project the container component depends on (e.g. JB AS 4.0.1 depends on JBoss Cache 1.2). Both project leads need to be aware and agree on the dependency at the time the integration task is created.
When new issues are created against the dependent project version (JB Cache 1.2) related to the development of the container project version (JB AS 4.0.1), they should be linked to from the integration task. Example: http://jira.jboss.com/jira/browse/JBAS-56
If the dependent project version is released before the container project is (JB Cache released on Dec 10, while JB AS 4.0.1 is not released until Dec 22), there should be a flexible mechanism to accomodate intermediary patches. One option is for the dependent project to maintain a separate branch (JBCache_1_2_JBAS_4_0_1) for the container integration. Another option is for the dependent project to apply patches against its main branch and release minor increments (JB Cache 1.2.0b).
This reference guide covers how to use the JBossBuild system.
JBossBuild is a declarative build system. Instead of having to define each step in the build process, JBossBuild allows a developer to declare the inputs and outputs of a build. JBossBuild then uses these definitions to dynamically generate the Ant targets needed to implement that definition.
JBossBuild is implemented as a set of Ant types and tasks, and target definitions. The types (components, componentdefs, artifacts, etc.) are declared by the developer in the build.xml. These definitions are then combined with the targetdefs in tasks.xml (under tools/etc/jbossbuild) to produce the generated ant targets.
There are two kinds of build definitions: toplevel, and component. The toplevel builds define the components of a release and where the artifacts of each component should be placed in the release. The component builds define how each artifact is built, the sources of those artifacts, and any dependencies of thoses sources.
A component build is made up of two parts: the component info (component-info.xml) and the component definition (build.xml or jbossbuild.xml). The component info is much like a declaration or manifest of the component. It defines what the expected outputs (artifacts) of the components are. The component definition specifies how these artifacts are built from source code.
Table 4.1. Component
Name: | component |
Purpose: | Declares a project component. |
Attributes: | |
id | The unique identifier for this component. This should be the same as its directory name in the online repository and in the local directory structure. |
module | The CVS module the component source should be checked out from. |
version | The version of the component. This version is used when retreiving artifacts from the repository. Artifacts are stored in the repository under the directory [id]/[version]. |
Table 4.4. Component
Name: | component |
Purpose: | Declares a project component. |
Attributes: | |
id | The unique identifier for this component. This should be the same as its directory name in the online repository and in the local directory structure. |
module | The CVS module the component source should be checked out from. |
version | The version of the component. This version is used when retreiving artifacts from the repository. Artifacts are stored in the repository under the directory [id]/[version]. |
You can now partially build jboss-head from the repository with the new build system.
You probably want this in it's own directory:
mkdir jboss-dir cd jboss-dir
Then, just check out the toplevel build and the tools module:
cvs co jbossas cvs co tools
You will need to set your cvs info in jbossas/local.properties:
cvs.prefix=:ext:rcampbell
Note, you will need ssh-agent setup to run cvs without entering a password for now. Now you are ready to synchronize and build:
ant sychronize ant build output/jboss-5.0.0alpha/bin/run.sh -c all
The synchronize target will checkout the source components from cvs and download thirdparty components from the repository.
In this section, we take a component - JBoss Deployment (jboss-head/deployment) and demonstrate how to incorporate it into the JBossAS release. This document assumes you have checked out the AS as outlined here.
First, we need to add the component to the toplevel build under jbossas/jbossbuild.xml. The ordering of the components is significant; the deployement module must be placed *after* the other source components it depends on (ie, common). The ordering of the components in the file dictates the order the components will be built. So, in this case, we add the component element at the end of the other JBoss components, but before the thirdparty components.
<!-- ============================================================ --> <!-- Deployment --> <!-- ============================================================ --> <component id="deployment" module="jboss-deployment" version="5.0-SNAPSHOT"> </component>
At this point, we know that the deployment module will come from the jboss-deployment module in cvs -- represented by the module attribute. We give it the same version as the other components in jboss-head. With this one definition, we have several new targets in our toplevel build:
bash-2.05b$ ant -projecthelp | grep deployment all.deployment Build All for the component deployment api.deployment Javadoc for the component deployment build.deployment Build for the component deployment clean.deployment Clean for the component deployment commit.deployment Commit for the component deployment doc.deployment Documentation for the component deployment rebuild.deployment Synchronize then build for the component deployment rebuildall.deployment Synchronize then build all for the component deployment runtest.deployment Run tests for the component deployment synchronize.after.deployment After synchronization processing for the component deployment synchronize.deployment Synchronize for the component deployment test.deployment Build and run the tests for the component deployment
These are all dynamically generated by jbossbuild based on the defintion we have provided. At the moment, we are only concerned with the synchronize target since we still don't have the source for this component. So let's see what the synchronize target will do before we try to call it
To see what a target will do before you call it, you can use the "show" target and pass it a property of which target you want to see.
bash-2.05b$ ant show -Dshow=synchronize.deployment Buildfile: build.xml show: <!-- Synchronize for the component deployment --> <target name="synchronize.deployment"> <mkdir dir="C:\projects\newbuild-jboss\thirdparty\deployment"/> <get verbose="true" dest="C:\projects\newbuild-jboss\thirdparty\deployment/component-info.xml" usetimestamp="true" src="http://cruisecontrol.jboss.com/repository/deployment/5.0-SNAPSHOT/component-info.xml"/> </target>
Whoops! Calling this target will download the component to thirdparty, which is not what we want at this point. In order to get the source for this component, we will want to set a property in the jbossas/synchronize.properties file:
checkout.deployment=true
Now, when we show the deployment.synchronize target we see that it intends to pull the source from cvs:
bash-2.05b$ ant show -Dshow=synchronize.deployment Buildfile: build.xml show: <!-- Synchronize for the component deployment --> <target name="synchronize.deployment"> <cvs dest="C:\projects\newbuild-jboss"> <commandline> <argument value="-d"/> <argument value=":ext:rcampbell@cvs.forge.jboss.com:/cvsroot/jboss"/> <argument value="co"/> <argument value="-d"/> <argument value="deployment"/> <argument value="jboss-deployment"/> </commandline> </cvs> </target>
Ok, so let's go ahead and call this target to checkout the module into our tree (../deployment).
bash-2.05b$ ant synchronize.deployment Buildfile: build.xml synchronize.deployment: [cvs] Using cvs passfile: c:\.cvspass [cvs] cvs checkout: Updating deployment [cvs] U deployment/.classpath [cvs] U deployment/.cvsignore ...
We could have also called the toplevel synchronize target if we wanted to update (or checkout) all the other components and thirdparty artifacts.
Ok, now that we have the source, we can get into creating a component-level build. The toplevel build in jbossas/jbossbuild.xml defines all the components, their versions, and the locations of their artifacts. However, the component-level build defines how those artifacts are composed of java classes and other resources.
Let's start out by just creating a minimal definition and see what happens. First, we want to create our component-info.xml under the deployment module. You can think of this file as the interface for this component. It will be uploaded to the repository along with the artifacts of this component so that other components may reference it.
For now, we can copy the entry from jbossas/jbossbuild.xml.
deployment/component-info.xml<project name="deployment-component-info"> <!-- ============================================================ --> <!-- Deployment --> <!-- ============================================================ --> <component id="deployment" module="jboss-deployment" version="5.0-SNAPSHOT"> </component> </project>
Once the component is declared, it needs to be defined. This is the responsibility of the jbossbuild.xml file:
deployment/jbossbuild.xml<?xml version="1.0"?> <!--[snip: license and header comments ]--> <project name="project" default="build" basedir="." > <import file="../tools/etc/jbossbuild/tasks.xml"/> <import file="component-info.xml"/> <componentdef component="deployment" description="JBoss Deployment"> <source id="main"/> </componentdef> <generate generate="deployment"/> </project>
At the top, we see the root project element, which is required for all Ant build files. More interestingly, we see that two files are imported. The tasks.xml is from jbossbuild. This file defines the custom Ant tasks (like componentinfo) and ultimately drives the dynamic creation of Ant targets based on our component definition. The other file is the component-info.xml file we created above.
The second thing we see is the source element. This says that we have a source directory named "main". jbossbuild requires that you put all of your source under the "src" directory, so this resolves to "deployment/src/main".
Finally, we see the generate element. This basically a clue to jbossbuild to tell it we are done defining our component and that it should generate the targets.
Let's see what we've got now:
bash-2.05b$ ant -f jbossbuild.xml -projecthelp Buildfile: jbossbuild.xml Main targets: all Build All api Javadoc build Build build.main Build for the source src/main clean Clean commit Commit doc Documentation rebuild Synchronize then build rebuildall Synchronize then build all runtest Run tests synchronize Synchronize synchronize.after After synchronization processing test Build and run the tests Default target: build
Again, we see that jbossbuild has automatically generated a basic set of targets for us. Additionally, we see that a specific target has been generated for our main source. As we add artifacts and sources to our component definition, jbossbuild will define specific targets for these as well. Let's take a look at how this target is implemented:
bash-2.05b$ ant -f jbossbuild.xml show -Dshow=build.main Buildfile: jbossbuild.xml show: <!-- Build for the source src/main --> <target name="build.main"> <mkdir dir="C:\projects\newbuild-jboss\deployment\output\classes\main"/> <depend destdir="C:\projects\newbuild-jboss\deployment\output\classes\main" srcdir="src/main"> <classpath> <pathelement location="C:\projects\newbuild-jboss\deployment\output\classes\main"/> </classpath> </depend> <javac destdir="C:\projects\newbuild-jboss\deployment\output\classes\main" deprecation="true" srcdir="src/main" debug="true" excludes="${javac.excludes}"> <classpath> <pathelement location="C:\projects\newbuild-jboss\deployment\output\classes\main"/> </classpath> <src path="src/main"/> </javac> </target>
Based on this one <source id="main"> element all of the above is generated by jbossbuild. However, if we were to call this target now, it would fail because of unresolved imports. To fix this, we need to define the buildpath for the main source. The easiest way to do this is to find the library.classpath and dependentmodule.classpath in the deployment/build.xml:
<!-- The combined library classpath --> <path id="library.classpath"> <path refid="dom4j.dom4j.classpath"/> </path> <!-- The combined dependant module classpath --> <path id="dependentmodule.classpath"> <path refid="jboss.common.classpath"/> <path refid="jboss.j2ee.classpath"/> <path refid="jboss.j2se.classpath"/> <path refid="jboss.system.classpath"/> </path>
Based on this we can determine the buildpath for the main source:
<source id="main"> <include component="dom4j-dom4j"/> <include component="common"/> <include component="j2ee"/> <include component="j2se"/> <include component="system"/> </source>
Generally, you should read this as "The main source tree includes these components as input." Concretely, the exported jars from these components are being included in the classpath of the call to javac:
$ ant -f jbossbuild.xml show -Dshow=build.main <javac destdir="C:\projects\newbuild-jboss\deployment\output\classes\main" deprecation="true" srcdir="src/main" debug="true" excludes="${javac.excludes}"> <classpath> <pathelement location="C:\projects\newbuild-jboss\j2ee\output\lib\jboss-saaj.jar"/> <pathelement location="C:\projects\newbuild-jboss\common\output\lib\namespace.jar"/> <pathelement location="C:\projects\newbuild-jboss\system\output\lib\jboss-system.jar"/> <pathelement location="C:\projects\newbuild-jboss\common\output\lib\jboss-common.jar"/> <pathelement location="C:\projects\newbuild-jboss\deployment\output\classes\main"/> <pathelement location="C:\projects\newbuild-jboss\j2se\output\lib\jboss-j2se.jar"/> <pathelement location="C:\projects\newbuild-jboss\thirdparty\dom4j-dom4j\lib\dom4j.jar"/> <pathelement location="C:\projects\newbuild-jboss\j2ee\output\lib\jboss-jaxrpc.jar"/> <pathelement location="C:\projects\newbuild-jboss\j2ee\output\lib\jboss-j2ee.jar"/> </classpath> <src path="src/main"/> </javac>
How are components resolved to jars? jbossbuild searches for the component-info.xml of the included component. First in the root of the project (..) and second in the thirdparty directory (../thirdparty). The component-info.xml includes an export element which specifies which artifacts should be resolved when the component is included by another component. It's probably not a bad analogy to think of this mechanism as replacing buildmagic's modules.ent and libraries.ent
Now we should compile the source to make sure we got it right. We'll just use the build target because we are lazy and don't want to type build.main (rats!).
bash-2.05b$ ant -f jbossbuild.xml build Buildfile: jbossbuild.xml build.etc: [mkdir] Created dir: C:\projects\newbuild-jboss\deployment\output\etc [copy] Copying 1 file to C:\projects\newbuild-jboss\deployment\output\etc build.main: [mkdir] Created dir: C:\projects\newbuild-jboss\deployment\output\classes\main [javac] Compiling 16 source files to C:\projects\newbuild-jboss\deployment\output\classes\main build: BUILD SUCCESSFUL Total time: 7 seconds
Great! Notice that the output for the source (id=main) is being placed in output/classes/main. Now we are ready to add an artifact definition. Looking at the deployment/build.xml, we see there is one artifact named jboss-deployment.jar. First, let's declare the artifact in our component-info.xml:
<component id="deployment" module="jboss-deployment" version="5.0-SNAPSHOT"> <artifact id="jboss-deployment.jar"/> <export> <include input="jboss-deployment.jar"/> </export> </component>
Notice also that we export this jar. When other components import this one, this is the jar they will want on their classpath.
Now, we need to create an artifactdef for this new artifact. The artifacdef defines how the artifact is composed of other inputs:
... </source> <artifactdef artifact="jboss-deployment.jar"> <include input="main"> <include pattern="org/jboss/deployment/**"/> </include> </artifactdef> </componentdef>
This results in the following target being generated:
bash-2.05b$ ant -f jbossbuild.xml show -Dshow=build.jboss-deployment.jar Buildfile: jbossbuild.xml show: <!-- Build for the artifact jboss-deployment.jar --> <target name="build.jboss-deployment.jar"> <mkdir dir="C:\projects\newbuild-jboss\deployment\output\lib"/> <jar destfile="C:\projects\newbuild-jboss\deployment\output\lib\jboss-deployment.jar"> <fileset dir="C:\projects\newbuild-jboss\deployment\output\classes\main"> <include name="org/jboss/deployment/**"/> </fileset> </jar> </target>
Notice that the <includes input="main"/> is resolved to output/classes/main.
Now that we have completed the artifact, we need to define where it should be placed in the overall release structure. This information, as you will recall, is stored in the toplevel build (jbossas/jbossbuild.xml). We define the location in the release using the release tag:
jbossas/jbossbuild.xml:<component id="deployment" module="jboss-deployment" version="5.0-SNAPSHOT"> <artifact id="jboss-deployment.jar" release="client"/> </component>
This will place the artifact in the client directory of the release:
bash-2.05b$ ant show -Dshow=release.jboss-deployment.jar Buildfile: build.xml show: <target name="release.jboss-deployment.jar"> <mkdir dir="C:\projects\newbuild-jboss\jbossas\output\jbossas-5.0.0alpha\client"/> <copy todir="C:\projects\newbuild-jboss\jbossas\output\jbossas-5.0.0alpha\client"> <fileset file="C:\projects\newbuild-jboss\deployment\output\lib\jboss-deployment.jar"/> </copy> </target>
Now, you should be able perform a build of the application server:
$ ant build ...
Congratulations, you've successfully added a new component to jboss AS.
This section describes the steps necessary to add a component to the build repository, currently at http://cruisecontrol.jboss.com/repository
First, you will want to checkout the repository locally.
cvs -d:ext:user@cvs.forge.jboss.com/cvsroot/jboss co repository.jboss.com
You need to decide on a component name. It is best to use something like organization-component so others can quickly tell what the name refers to. The exception is jboss components which are not prefixed with "jboss".
Underneath the directory named after the component is the version number, which contains the component-info.xml. The lib directory below this will hold the jars.
repository.jboss.com + apache-log4j + 1.2.8 + component-info.xml + lib + log4j.jar
In addition to adding the jars, you also need to create a component-info.xml. This file allows other components to reference your jars. We want to make sure that the component-info.xml reflects the version we indicated in the directory structure above.
<project name="apache-log4j-component-info"> <!-- ============================================================ --> <!-- Apache Log4j --> <!-- ============================================================ --> <component id="apache-log4j" licenseType="apache-2.0" version="1.2.8" projectHome="http://logging.apache.org/"> <artifact id="log4j.jar"/> <artifact id="snmpTrapAppender.jar"/> <export> <include input="log4j.jar"/> </export> </component> </project>
You can commit the new version to the repository using cvs commands. There is (will be) a scheduled process which updates the online repository from cvs every 5 minutes. If this fails, please contact qa@jboss.com
Once the component is available in the online build repository, you may configure toplevel (e.g., jbossas/jbossbuild.xml) build to include it:
<component id="apache-log4j" version="1.2.8" > <artifact id="log4j.jar"/> <artifact id="snmpTrapAppender.jar"/> </component>
Source code is available for every JBoss module and any version of JBoss can be built from source by downloading the appropriate version of the code from the JBoss Forge CVS Repository.
The command line version of the CVS program is freely available for nearly every platform and is included by default on most Linux and UNIX distributions. A good port of CVS as well as numerous other UNIX programs for Win32 platforms is available from Cygwin.
The syntax of the command line version of CVS will be examined because this is common across all platforms.
For complete documentation on CVS, check out The CVS Home Page.
Note that the anonymous repository is a mirror of the comitter repository that is synched every 5 minutes.
All JBoss projects' CVS repositories can be accessed through anonymous(pserver) CVS with the following instruction set. The module you want to check out must be specified as the modulename. When prompted for a password for anonymous, simply press the Enter key.
The general syntax of the command line version of CVS for anonymous access to the JBoss repositories is:
cvs -d:pserver:anonymous@anoncvs.forge.jboss.com:/cvsroot/jboss login cvs -z3 -d:pserver:anonymous@anoncvs.forge.jboss.com:/cvsroot/jboss co modulename
The first command logs into JBoss CVS repository as an anonymous user. This command only needs to be performed once for each machine on which you use CVS because the login information will be saved in your HOME/.cvspass file or equivalent for your system. The second command checks out a copy of the modulename source code into the directory from which you run the cvs command.
To avoid having to type the long cvs command line each time, you can set up a CVSROOT environment variable.
set CVSROOT=:pserver:anonymous@anoncvs.forge.jboss.com:/cvsroot/jboss
The abbreviated versions of the previous commands can then be used:
cvs login cvs -z3 co modulename
The name of the JBoss module alias you use depends on the version of JBoss you want. For the 3.0 branch the module name is jboss-3.0, for the 3.2 branch it is jboss-3.2. To obtain more up-to-date information on module naming, refer to JBossAS Modules on our wiki.
To checkout the HEAD revision of jboss (latest code on the main branch), you would use jboss-head as the module name.
Releases of JBoss are tagged with the pattern JBoss_X_Y_Z where X is the major version, Y is the minor version and Z is the patch version. Release branches of JBoss are tagged with the pattern Branch_X_Y. For more information on Release Tagging Standards, refer to Chapter 14
Some checkout examples are:
cvs co -r JBoss_3_2_6 jboss-3.2 # Checkout the 3.2.6 release version code cvs co jboss-head # Checkout the curent HEAD branch code
You can also browse the repository using the web interface . If you are stuck behind a firewall without pserver port access, you can even use fisheye to pull the repo using cvsgrab.
$ cd /tmp/cvsgrab/ $ cvsgrab -webInterface FishEye1_0 -url \ http://anoncvs.forge.jboss.com/viewrep/JBoss/jrunit -destDir
This will create the JBoss/jrunit directory. Just replace jrunit with the module you want. If you want to check out the entire repo with cvsgrab, just omit the module:
$ cd /tmp/cvsgrab/ $ cvsgrab -webInterface FishEye1_0 -url \ http://anoncvs.forge.jboss.com/viewrep/JBoss -destDir
Or, if you want a branch:
$ cd /tmp/cvsgrab/ $ cvsgrab -webInterface FishEye1_0 -url \ http://anoncvs.forge.jboss.com/viewrep/~br=Branch_4_0/JBoss -destDir
Or a tag:
$ cd /tmp/cvsgrab/ $ cvsgrab -webInterface FishEye1_0 -url \ http://anoncvs.forge.jboss.com/viewrep/~br=Branch_4_0,tag=sometag/JBoss -destDir
Write access to the repository is granted only on approval by the Forge Administrator. To request write access send an email to forge-admin@jboss.com asking for committer access.
On approval, you will be given read/write access to the repository and a committer status in JIRA. It is required that you have a committer role in JIRA. The Forge Admin will make sure that you have the proper role and permission status.
To use the committer repository:export CVS_RSH=ssh export CVSROOT=:ext:username@cvs.forge.jboss.com:/cvsroot/jboss
If you are a JBoss employee, your username is the same as your existing cvs.jboss.com username.
If you are not a JBoss Employee, then your username is your existing SourceForge username OR your jboss.com username.
There is NO shell access, only cvs over ssh, similar to SourceForge.
All commiter access is authenticated via SSH. There is no password based committer access. You need to supply an SSH protocol verison 2 public key for access to be granted.
This could be done using the ssh-keygen utility as:ssh-keygen -t dsa -C 'cvs.forge.jboss.com access' -f mykeyor
ssh-keygen -t rsa -C 'cvs.forge.jboss.com access' -f mykey
If you don't know your username or have any trouble, just send an email to forge-admin@jboss.com.
For committer access requests, please include:
This chapter describes the JBoss CVS administration policies for managing the CVS repository. Comments or questions regarding these policies should be directed to the JBoss Development forum.
The CVS branching and release management procedures are outlined in this section. All development of new features occurs on the main trunk. Releases are done on branches off of the main trunk.
Releases are tracked using CVS tags that have the following forms:
Consider events 1-13 in blue on the following figure:
Prior to event 1, the latest alpha development build is Rel_2_1_0_57. At this point it is decided to create a new binary release.
cvs co jboss-head
cvs tag Rel_2_3_0_0
cvs tag -b Branch_2_2
cvs co -r Branch_2_2 jboss
cvs tag Rel_2_2_0_0
New features and bug fixes on unreleased code should go into the main trunk which is the latest development branch. The steps for doing this are:
cvs co jboss-head
cvs commit -m "commit-comment"You don't have to specify the commit msg on the commit command line. If you don't you will be prompted for the commit msg. Note that this will apply the same commit msg to all files you have changed. If you want specific commit msgs for each file then you can perform a seperate commit on each file.
cvs tag Rel_2_3_1_3from within the jboss working directory.
When you have changes that need to go into the codebase of a release branch, you need to check out that branch and make the changes. So for example, if you need to add a patch the the 2.2 branch of the example CVS structure above, you need to first check out the 2.2 branch using the Branch_2_2 tag.
cvs co -r Branch_2_2 jbossThis will create a jboss working directory with a sticky tag that associates the source code with the 2.2 branch. If you look at the jboss/src/main/org/jboss/Main.java file in the jboss working directory that results from the previous command using the cvs status command you will see something like:
bash-2.04$ cd jboss/src/main/org/jboss/ bash-2.04$ cvs status Main.java =================================================================== File: no file Main.java Status: Needs Checkout Working revision: 1.30.2.6 Repository revision: 1.30.2.6 /cvsroot/jboss/jboss/src/main/org/jboss/Main.java,v Sticky Tag: Branch_2_2 (branch: 1.30.2) Sticky Date: (none) Sticky Options: (none)This shows that the "Sticky Tag:" is set to the Branch_2_2 tag as we requested.
cvs commit -m "commit-comment"As already noted, you don't have to specify the commit msg on the commit command line. If you don't you will be prompted for the commit msg. Note that this will apply the same commit msg to all files you have changed. If you want specific commit msgs for each file then you can perform a seperate commit on each file.
bash-2.04$ cvs status -v Main.java =================================================================== File: no file Main.java Status: Needs Checkout Working revision: 1.30.2.6 Repository revision: 1.30.2.6 /cvsroot/jboss/jboss/src/main/org/jboss/Main.java,v Sticky Tag: Branch_2_2 (branch: 1.30.2) Sticky Date: (none) Sticky Options: (none) Existing Tags: Rel_2_3_1_0 (revision: 1.34) Rel_2_2_2_0 (revision: 1.30.2.6) JBoss_2_2_2 (revision: 1.30.2.6) JBoss_2_2_1 (revision: 1.30.2.3) Rel_2_2_1_0 (revision: 1.30.2.3)The Rel_2_2_2_0 tag is the latest tag on the 2.2 branch and indicates that no patches have been made since the JBoss_2_2_2 release. So to tag the changes you have made you need to use Rel_2_2_2_1. Do this using:
cvs tag Rel_2_2_2_1from the top of the jboss working directory.
cvs co jboss
cvs tag Rel_2_3_1_5from within the jboss working directory you just checked out.
When you have changes that need to go into one of the modules other than the jboss cvs module for integration as a jar in a jboss release branch, perform the following steps. The example below describes how to make a change in the jbosscx module for incorporation into the jboss 2.4 release branch.
cvs co -r Branch_2_4 jbosscx
cvs commit -m "commit-comment"
cvs tag Rel_2_4_0_1The Rel_2_2_2_0 tag is the latest tag on the 2.2 branch and indicates that no patches have been made since the JBoss_2_2_2 release. So to tag the changes you have made you need to use Rel_2_2_2_1.
cvs tag Rel_2_2_2_1
cvs commit -m "commit-comment"
cvs tag Rel_2_4_0_1
Source code for specific JBoss projects are located in the JBoss Subversion repository. Please see the project homepage to determine the source location.
The command line version of the Subversion program is freely available for nearly every platform. You can select the appropriate package here: Subversion downloads.
Tortoise SVN is a popular GUI based client and can be found here: Tortoise SVN downloads
The syntax of the command line version of Subversion will be examined because this is common across all platforms.
For complete documentation on Subversion, check out The Subversion RedBook.
Note that the anonymous repository is a mirror of the comitter repository that is synched every 5 minutes.
All JBoss projects' Subversion repositories can be accessed through anonymously with the following instruction set. The project you want to check out must be specified as the project. You will also provide the path which contains either the correct branch, tag, or trunk.
The general syntax of the command line version of Subversion for anonymous access to the JBoss repositories is:
svn co https://svn.jboss.org/repos/project/path
To checkout the HEAD revision of jboss (latest code on the main branch), you would use the projectjbossas/trunk as the project name
Releases of JBoss are tagged with the pattern JBoss_X_Y_Z where X is the major version, Y is the minor version and Z is the patch version. Release branches of JBoss are tagged with the pattern Branch_X_Y. For more information on Release Tagging Standards, refer to Chapter 14
Some checkout examples are:
svn co http://anonsvn.jboss.org/repos/jbossas/tags/JBoss_3_2_6 svn co http://anonsvn.jboss.org/repos/jbossas/trunk # Checkout the curent HEAD branch code
You can also browse the repository using the web interface
Write access to the repository is granted only on approval by the Forge Administrator. To request write access send an email to forge-admin@jboss.com asking for committer access.
On approval, you will be given read/write access to the repository and a committer status in JIRA. It is required that you have a committer role in JIRA. The Forge Admin will make sure that you have the proper role and permission status.
To use the committer repository:svn co https://svn.jboss.org/repos/project
If you are a JBoss employee, your username is the same as your existing cvs.jboss.com username.
If you are not a JBoss Employee, then your username is your existing SourceForge username OR your jboss.com username.
If you don't know your username or have any trouble, just send an email to forge-admin@jboss.com.
For committer access requests, please include:
This chapter describes the JBoss SVN administration policies for managing the SVN repository. Comments or questions regarding these policies should be directed to the JBoss Development forum.
The CVS branching and release management procedures are outlined in this section. All development of new features occurs on the main trunk. Releases are done on branches off of the main trunk.
Releases are tracked using SVN tags that have the following forms:
Consider events 1-13 in blue on the following figure:
Prior to event 1, the latest alpha development build is Rel_2_1_0_57. At this point it is decided to create a new binary release.
svn co https://svn.jboss.org/repos/jbossas/trunk
svn copy https://svn.jboss.org/repos/jbossas/trunk https://svn.jboss.org/repos/jbossas/tags/Rel_2_3_0_0 "Creating a tag"
svn copy https://svn.jboss.org/repos/jbossas/trunk https://svn.jboss.org/repos/jbossas/branches/Branch_2_2 "Creating a branch"
svn co https://svn.jboss.org/repos/jbossas/branches/Branch_2_2
svn copy https://svn.jboss.org/repos/jbossas/branches/Branch_2_2 https://svn.jboss.org/repos/jbossas/tags/Rel_2_2_0_0 "Creating a branch
New features and bug fixes on unreleased code should go into the main trunk which is the latest development branch. The steps for doing this are:
svn co https://svn.jboss.org/repos/jbossas/trunk
svn commit -m "commit-comment"Note that this will apply the same commit msg to all files you have changed. If you want specific commit msgs for each file then you can perform a seperate commit on each file.
The procedure defined below will take a developer through the process of creating a branch, making the necessary changes, and merging those changes into the main branch.
svn copy http://svn.jboss.org/repos/test/tags/JBoss_4_0_3_SP1/ http://svn.jboss.org/repos/test/branches/JBoss_4_0_3_SP1_JBAS-1234 -m "Creating a branch for developing a patch"
svn co http://svn.jboss.org/repos/test/branches/JBoss_4_0_3_SP1_JBAS-1234 jbas-1234_local_dir
svn commit -m "changes required for patch"
At this point you may wish to port this patch to the current code line. To do this we will use the svn merge command. The svn merge command requires 3 pieces of information.
Essentially, you are finding the change set between 1 and 2 and applying them to 3. In our case 1 would be the tagged JBoss-4.0.3.SP1 and 2 would be the JBoss-4.0.3.SP1.PATCH branch that you created. 3 would be the current 4.0 branch (which will you need to check out).
Backporting procedure
svn co http://svn.jboss.org/repos/test/branches/Branch_4_0 jboss-4.0
svn merge http://svn.jboss.org/repos/test/tags/JBoss_4_0_3_SP1 http://svn.jboss.org/repos/test/branches/JBoss_4_0_3_SP1-JBAS-1234 jboss-4.0
svn commit
This section lists some general guidelines followed in JBoss code for coding sources / tests.
All files (including tests) should have a header like the following:
/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */
The header asserts the LGPL license, without which the content would be closed source. The assumption under law is copyright the author, all rights reserved or sometimes the opposite - if something is published without asserting the copyright or license it is public domain.
Use the template files on JIRA for consistency. These template files encapsulate settings that are generally followed such as replacing tabs with 3 spaces for portability amongst editors, auto-insertion of headers etc.
Template files for the Eclipse IDE can be found here: JBoss Eclipse Format. JBoss Eclipse Template.
Template files for other IDEs(IntelliJ-IDEA, NetBeans) should be available here soon.
The process of importing templates into the Eclipse IDE is as follows:
On the IDE, goto Windows Menu => Preferences => Java => Code Style => Code Templates => Import and choose to import the Eclipse template files.
Tools such as Jalopy help to automate template changes at one shot to numerous files.
Fully qualified imports should be used, rather than importing x.y.*.
Use newlines for opening braces, so that the top and bottom braces can be visually matched.
Aid visual separation of logical steps by introducing newlines and appropriate comments above them.
All public and protected members and methods should be documented.
It should be documented if "null" is an acceptable value for parameters.
Side effects of method calls, if known, or as they're discovered should be documented.
It would also be useful to know from where an overridden method can be invoked.
Example 9.1. A class that conforms to JBoss coding guidelines
/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package x; // EXPLICIT IMPORTS import a.b.C1; // GOOD import a.b.C2; import a.b.C3; // DO NOT WRITE import a.b.*; // BAD // DO NOT USE "TAB" TO INDENT CODE USE *3* SPACES FOR PORTABILITY AMONG EDITORS /** * A description of this class. * * @see SomeRelatedClass. * * @version <tt>$Revision: 1.4 $</tt> * @author <a href="mailto:{email}">{full name}</a>. * @author <a href="mailto:marc@jboss.org">Marc Fleury</a> */ public class X extends Y implements Z { // Constants ----------------------------------------------------- // Attributes ---------------------------------------------------- // Static -------------------------------------------------------- // Constructors -------------------------------------------------- // Public -------------------------------------------------------- public void startService() throws Exception { // Use the newline for the opening bracket so we can match top // and bottom bracket visually Class cls = Class.forName(dataSourceClass); vendorSource = (XADataSource)cls.newInstance(); // JUMP A LINE BETWEEN LOGICALLY DISTINCT **STEPS** AND ADD A // LINE OF COMMENT TO IT cls = vendorSource.getClass(); if(properties != null) { try { } catch (IOException ioe) { } for (Iterator i = props.entrySet().iterator(); i.hasNext();) { // Get the name and value for the attributes Map.Entry entry = (Map.Entry) i.next(); String attributeName = (String) entry.getKey(); String attributeValue = (String) entry.getValue(); // Print the debug message log.debug("Setting attribute '" + attributeName + "' to '" + attributeValue + "'"); // get the attribute Method setAttribute = cls.getMethod("set" + attributeName, new Class[] { String.class }); // And set the value setAttribute.invoke(vendorSource, new Object[] { attributeValue }); } } // Test database vendorSource.getXAConnection().close(); // Bind in JNDI bind(new InitialContext(), "java:/"+getPoolName(), new Reference(vendorSource.getClass().getName(), getClass().getName(), null)); } // Z implementation ---------------------------------------------- // Y overrides --------------------------------------------------- // Package protected --------------------------------------------- // Protected ----------------------------------------------------- // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- }
Example 9.2. An interface that conforms to JBoss coding guidelines
/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package x; // EXPLICIT IMPORTS import a.b.C1; // GOOD import a.b.C2; import a.b.C3; // DO NOT WRITE import a.b.*; // BAD // DO NOT USE "TAB" TO INDENT CODE USE *3* SPACES FOR PORTABILITY AMONG // EDITORS /** * A description of this interface. * * @see SomeRelatedClass * * @version <tt>$Revision: 1.4 $</tt> * @author <a href="mailto:{email}">{full name}</a>. * @author <a href="mailto:marc@jboss.org">Marc Fleury</a> */ public interface X extends Y { int MY_STATIC_FINAL_VALUE = 57; ReturnClass doSomething() throws ExceptionA, ExceptionB; }
Persisted diagnostic logs are often very useful in debugging software issues. This section lists some general guidelines followed in JBoss code for diagnostic logging.
The following code snippet illustrates how you can obtain a logger.
package org.jboss.X.Y; import org.jboss.logging.Logger; public class TestABCWrapper { private static final Logger log = Logger.getLogger(TestABCWrapper.class.getName()); // Hereafter, the logger may be used with whatever priority level as appropriate. }
After a logger is obtained, it can be used to log messages by specifying appropriate priority levels.
The log4j configuration is loaded from the jboss server conf/log4j.xml file. You can edit this to add/change the default appenders and logging thresholds.
You can segment logging output by assigning log4j categories to specific appenders in the conf/log4j.xml configuration.
Example 10.1. Assigning categories to specific appenders
<appender name="App1Log" class="org.apache.log4j.FileAppender"> <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/> <param name="Append" value="false"/> <param name="File" value="${jboss.server.home.dir}/log/app1.log"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/> </layout> </appender> ... <category name="com.app1"> <appender-ref ref="App1Log"/> </category> <category name="com.util"> <appender-ref ref="App1Log"/> </category> ... <root> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE"/> <appender-ref ref="App1Log"/> </root>
If you have multiple apps with shared classes/categories, and/or want the jboss categories to show up in your app log then this approach will not work. There is a new appender filter called TCLFilter that can help with this. The filter should be added to the appender and it needs to be specifed what deployment url should logging be restricted to. For example, if your app1 deployment was app1.ear, you would use the following additions to the conf/log4j.xml:
Example 10.2. Filtering log messages
<appender name="App1Log" class="org.apache.log4j.FileAppender"> <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/> <param name="Append" value="false"/> <param name="File" value="${jboss.server.home.dir}/log/app1.log"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/> </layout> <filter class="org.jboss.logging.filter.TCLFilter"> <param name="AcceptOnMatch" value="true"/> <param name="DeployURL" value="app1.ear"/> </filter> </appender> ... <root> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE"/> <appender-ref ref="App1Log"/> </root>
The log4j framework has a number of appenders that allow you to send log message to an external server. Common appenders include:
Documentation on configuration of these appenders can be found at Apache Logging Services.
JBoss has a Log4jSocketServer service that allows for easy use of the SocketAppender.
Example 10.3. Setting up and using the Log4jSocketServer service.
The org.jboss.logging.Log4jSocketServer is an mbean service that allows one to collect output from multiple log4j clients (including jboss servers) that are using the org.apache.log4j.net.SocketAppender.
The Log4jSocketServer creates a server socket to accept SocketAppender connections, and logs incoming messages based on the local log4j.xml configuration.
You can create a minimal jboss configuration that includes a Log4jSocketServer to act as your log server.
Example 10.4. An Log4jSocketServer mbean configuration
The following MBean Configuration can be added to the conf/jboss-service.xml
<mbean code="org.jboss.logging.Log4jSocketServer" name="jboss.system:type=Log4jService,service=SocketServer"> <attribute name="Port">12345</attribute> <attribute name="BindAddress">${jboss.bind.address}</attribute> </mbean>
The Log4jSocketServer adds an MDC entry under the key 'host' which includes the client socket InetAddress.getHostName value on every client connection. This allows you to differentiate logging output based on the client hostname using the MDC pattern.
Example 10.5. Augmenting the log server console output with the logging client socket hostname
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender"> <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/> <param name="Target" value="System.out"/> <param name="Threshold" value="INFO"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1},%X{host}] %m%n"/> </layout> </appender>
All other jboss servers that should send log messages to the log server would add an appender configuration that uses the SocketAppender.
Example 10.6. log4j.xml appender for the Log4jSocketServer
<appender name="SOCKET" class="org.apache.log4j.net.SocketAppender"> <param name="Port" value="12345"/> <param name="RemoteHost" value="loghost"/> <param name="ReconnectionDelay" value="60000"/> <param name="Threshold" value="INFO"/> </appender>
Some of the key subsystem category names are given in the following table. These are just the top level category names. Generally you can specify much more specific category names to enable very targeted logging.
Table 10.1. JBoss SubSystem Categories
SubSystem | Category |
---|---|
Cache | org.jboss.cache |
CMP | org.jboss.ejb.plugins.cmp |
Core Service | org.jboss.system |
Cluster | org.jboss.ha |
EJB | org.jboss.ejb |
JCA | org.jboss.resource |
JMX | org.jboss.mx |
JMS | org.jboss.mq |
JTA | org.jboss.tm |
MDB | org.jboss.ejb.plugins.jms, org.jboss.jms |
Security | org.jboss.security |
Tomcat | org.jboss.web, org.apache.catalina |
Apache Stuff | org.apache |
JGroups | org.jgroups |
When you increase the level of logging for one or more categories, it is often useful to redirect the output to a seperate file for easier investigation. To do this you add an appender-ref to the category as shown here:
Example 10.7. Adding an appender-ref to a category
<appender name="JSR77" class="org.apache.log4j.FileAppender"> <param name="File" value="${jboss.server.home.dir}/log/jsr77.log"/> ... </appender> <!-- Limit the JSR77 categories --> <category name="org.jboss.management" additivity="false"> <priority value="DEBUG"/> <appender-ref ref="JSR77"/> </category>
This sends allorg.jboss.management output to the jsr77.log file. The additivity attribute controls whether output continues to go to the root category appender. If false, output only goes to the appenders referred to by the category.
In order to use your own log4j.xml file you need to do something to initialize log4j in your application. If you use the default singleton initialization method where the first use of log4j triggers a search for the log4j initialization files, you need to configure a ClassLoader to use scoped class loading, with overrides of the jBoss classes. You also have to include the log4j.jar in your application so that new log4j singletons are created in your applications scope.
To use a log4j.properties file, you have to make the change in conf/jboss-service.xml as shown below. This is necessary for the reasons mentioned above. Essentially you are changing the log4j resource file that jBossAS will look for. After making the change in jboss-service.xml make sure you rename the conf/log4j.xml to the name that you have give in jboss-service.xml (in this case jboss-log4j.xml).
<!--================================================================--> <!-- Log4j Initialization --> <!-=================================================================--> <mbean code="org.jboss.logging.Log4jService" name="jboss.system:type=Log4jService,service=Logging"> <attribute name="ConfigurationURL"> resource:jboss-log4j.xml</attribute> <!-- Set the org.apache.log4j.helpers.LogLog.setQuiteMode. As of log4j1.2.8 this needs to be set to avoid a possible deadlock on exception at the appender level. See bug#696819. --> <attribute name="Log4jQuietMode">true</attribute> <!-- How frequently in seconds the ConfigurationURL is checked for changes --> <attribute name="RefreshPeriod">60</attribute> </mbean>
Drop log4j.jar in your myapp.war/WEB-INF. Make the change in jboss-web.xml for class-loading, as shown in the section above. In this case, myapp.war/WEB-INF/jboss-web.xml looks like this:
<jboss-web> <class-loading java2ClassLoadingCompliance="false"> <loader-repository> myapp:loader=myapp.war <loader-repository-config>java2ParentDelegation=false </loader-repository-config> </loader-repository> </class-loading> </jboss-web>
Now, in your deploy/myapp.war/WEB-INF/classes create a log4j.properties.
Example 10.8. Sample log4j.properties
# Debug log4j log4j.debug=true log4j.rootLogger=debug, myapp log4j.appender.myapp=org.apache.log4j.FileAppender log4j.appender.myapp.layout=org.apache.log4j.HTMLLayout log4j.appender.myapp.layout.LocationInfo=true log4j.appender.myapp.layout.Title='All' Log log4j.appender.myapp.File=${jboss.server.home.dir}/deploy/myapp.war/WEB-INF/logs/myapp.html log4j.appender.myapp.ImmediateFlush=true log4j.appender.myapp.Append=false
The above property file sets the log4j debug system to true, which displays log4j messages in your jBoss log. You can use this to discover errors, if any in your properties file. It then produces a nice HTML log file and places it in your application's WEB-INF/logs directory. In your application, you can call this logger with the syntax:
... private static Logger log = Logger.getLogger("myapp"); ... log.debug("############## A debug message from myapp logger #########"); ...
If all goes well, you should see this message in myapp.html.
After jBossAS has reloaded conf/jboss-service.xml (you may have to restart jBossAS), touch myapp.war/WEB-INF/web.xml so that JBoss reloads the configuration for your application. As the application loads you should see log4j debug messages showing that its reading your log4j.properties. This should enable you to have your own logging system independent of the JBoss logging system.
Another way to achieve this is to write a custom RepositorySelector that changes how the LogManager gets a logger. Using this technique, Logger.getLogger() will return a different logger based on the context class loader. Each context class loader has its own configuration set up with its own log4j.xml file.
Example 10.9. A RepositorySelector
The following code shows a RepositorySelector that looks for a log4j.xml file in the WEB-INF directory.
/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.repositoryselectorexample; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.log4j.Hierarchy; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.apache.log4j.spi.LoggerRepository; import org.apache.log4j.spi.RepositorySelector; import org.apache.log4j.spi.RootCategory; import org.apache.log4j.xml.DOMConfigurator; import org.w3c.dom.Document; /** * This RepositorySelector is for use with web applications. * It assumes that your log4j.xml file is in the WEB-INF directory. * @author Stan Silvert */ public class MyRepositorySelector implements RepositorySelector { private static boolean initialized = false; // This object is used for the guard because it doesn't get // recycled when the application is redeployed. private static Object guard = LogManager.getRootLogger(); private static Map repositories = new HashMap(); private static LoggerRepository defaultRepository; /** * Register your web-app with this repository selector. */ public static synchronized void init(ServletConfig config) throws ServletException { if( !initialized ) // set the global RepositorySelector { defaultRepository = LogManager.getLoggerRepository(); RepositorySelector theSelector = new MyRepositorySelector(); LogManager.setRepositorySelector(theSelector, guard); initialized = true; } Hierarchy hierarchy = new Hierarchy(new RootCategory(Level.DEBUG)); loadLog4JConfig(config, hierarchy); ClassLoader loader = Thread.currentThread().getContextClassLoader(); repositories.put(loader, hierarchy); } // load log4j.xml from WEB-INF private static void loadLog4JConfig(ServletConfig config, Hierarchy hierarchy) throws ServletException { try { String log4jFile = "/WEB-INF/log4j.xml"; InputStream log4JConfig = config.getServletContext().getResourceAsStream(log4jFile); Document doc = DocumentBuilderFactory.newInstance() .newDocumentBuilder() .parse(log4JConfig); DOMConfigurator conf = new DOMConfigurator(); conf.doConfigure(doc.getDocumentElement(), hierarchy); } catch (Exception e) { throw new ServletException(e); } } private MyRepositorySelector() { } public LoggerRepository getLoggerRepository() { ClassLoader loader = Thread.currentThread().getContextClassLoader(); LoggerRepository repository = (LoggerRepository)repositories.get(loader); if (repository == null) { return defaultRepository; } else { return repository; } } }
The choice of the actual logging implementation is determined by the org.jboss.logging.Logger.pluginClass system property. This property specifies the class name of an implementation of the org.jboss.logging.LoggerPlugin interface. The default value for this is the org.jboss.logging.Log4jLoggerPlugin class.
If you want to use the JDK 1.4+ java.util.logging framework instead of log4j, you can create your own Log4jLoggerPlugin to do this. The attached JDK14LoggerPlugin.java file shows an example implementation.
To use this, specify the following system properties:
To specify the custom JDK1.4 plugin:
org.jboss.logging.Logger.pluginClass = logging.JDK14LoggerPlugin
To specify the JDK1.4 logging configuration file:
java.util.logging.config.file = logging.properties
This can be done using the JAVA_OPTS env variable, for example:
JAVA_OPTS="-Dorg.jboss.logging.Logger.pluginClass=logging.JDK14LoggerPlugin -Djava.util.logging.config.file=logging.properties"
You need to make your custom Log4jLoggerPlugin available to JBoss by placing it in a jar in the JBOSS_DIST/lib directory, and then telling JBoss to load this as part of the bootstrap libraries by passing in -L jarname on the command line as follows:
starksm@banshee9100 bin$ run.sh -c minimal -L logger.jar
One can say that using a debugger may help to verify the execution of an application. However, in addition to the fact that a debugger decreases performance of an application, it is difficult to use it in a distributed computing environment.
This most basic form of logging involves developers manually inserting code into their applications to display small (or large) pieces of internal state information to help understand what's going on. It's a useful technique that every developer has used at least once. The problem is that it doesn't scale. Using print statements for a small program is fine, but for a large, commercial-grade piece of software there is far too much labor involved in manually adding and removing logging statements.
C programmers know, of course, that the way to conditionally add and remove code is via the C preprocessor and the #ifdef directive. Unfortunately, Java doesn't have a preprocessor. How can we make logging scale to a useful level in Java?
A simple way to provide logging in your program is to use the Java compiler's ability to evaluate boolean expressions at compile time, provided that all the arguments are known. For example, in this code, the println statements will not be executed if DEBUG not set to true.
class foo { public bar() { if(DEBUG) { System.out.println("Debugging enabled."); } } }
A much better way, and the way that most logging is done in environments where the logged output is important, is to use a logging class.
A logging class collects all the messages in one central place and not only records them, but can also sort and filter them so that you don't have to see every message being generated. A logging class provides more information than just the message. It can automatically add information such as the time the event occurred, the thread that generated the message, and a stack trace of where the message was generated.
Some logging classes will write their output directly to the screen or a file. More advanced logging systems may instead open a socket to allow the log messages to be sent to a separate process, which is in turn responsible for passing those messages to the user or storing them. The advantage with this system is that it allows for messages from multiple sources to be aggregated in a single location and it allows for monitoring remote systems.
The format of the log being generated should be customisable. This could start from just allowing setting the Log "level" - which means that each log message is assigned a severity level and only messages of greater importance than the log level are logged - to allowing more flexible log file formatting by using some sort LogFormatter objects that do transformations on the logging information.
The logging service should be able to route logging information to different locations based on the type of the information. Examples might be printing certain messages to the console, writing to a flat file, to a number of different flat files, to a database and so on. Examples of different types information could be for example errors, access information etc.
An appropriate logging library should provide these features:
According to the features (described above) a logging framework should provide, we have considering the most common logging service is use.
Log4j has three main components:
The org.log4j.Category class figures at the core of the package. Categories are named entities. In a naming scheme familiar to Java developers, a category is said to be a parent of another category if its name, followed by a dot, is a prefix of the child category name. For example, the category named com.foo is a parent of the category named com.foo.Bar. Similarly, java is a parent of java.util and an ancestor of java.util.Vector.
The root category, residing at the top of the category hierarchy, is exceptional in two ways:
In the Category class, invoking the static getRoot() method retrieves the root category. The static getInstance() method instantiates all other categories. getInstance() takes the name of the desired category as a parameter. Some of the basic methods in the Category class are listed below:
package org.log4j; public Category class { // Creation and retrieval methods: public static Category getRoot(); public static Category getInstance(String name); // printing methods: public void debug(String message); public void info(String message); public void warn(String message); public void error(String message); // generic printing method: public void log(Priority p, String message); }
Categories may be assigned priorities from the set defined by the org.log4j.Priority class. Five priorities are defined: FATAL, ERROR, WARN, INFO and DEBUG, listed in decreasing order of priority. New priorities may be defined by subclassing the Priority class.
To make logging requests, invoke one of the printing methods of a category instance. Those printing methods are: fatal(), error(), warn(), info(), debug(), log().
By definition, the printing method determines the priority of a logging request. For example, if c is a category instance, then the statement c.info("..") is a logging request of priority INFO.
A logging request is said to be enabled if its priority is higher than or equal to the priority of its category. Otherwise, the request is said to be disabled. A category without an assigned priority will inherit one from the hierarchy.
Log4j also allows logging requests to print to multiple output destinations called appenders in log4j speak. Currently, appenders exist for the console, files, GUI components, remote socket servers, NT Event Loggers, and remote UNIX Syslog daemons.
A category may refer to multiple appenders. Each enabled logging request for a given category will be forwarded to all the appenders in that category as well as the appenders higher in the hierarchy. In other words, appenders are inherited additively from the category hierarchy. For example, if you add a console appender to the root category, all enabled logging requests will at least print on the console. If, in addition, a file appender is added to a category, say C, then enabled logging requests for C and C's children will print on a file and on the console.
More often than not, users want to customize not only the output destination but also the output format, a feat accomplished by associating a layout with an appender. The layout formats the logging request according to the user's wishes, whereas an appender takes care of sending the formatted output to its destination.
For example, the PatternLayout with the conversion pattern %r [%t]%-5p %c - %m%n will output something like:
176 [main] INFO org.foo.Bar �Hello World.
In the output above:
The text after the - indicates the statement's message.
The log4j environment can be fully configured programmatically. However, it is far more flexible to configure log4j by using configuration files. Currently, configuration files can be written in XML or in Java properties (key=value) format.
The following figure summarizes the different components when using log4j. Applications make logging calls on Category objects. The Category forwards to Appender logging requests for publication. Appender are registered with a Category with the addAppender method on the Category class. Invoking the addAppender method is made either by the Application or by Configurator objects. Log4j provides Configurators such as BasicConfigurator, which registers to the category the ConsoleAppender responsible to send logging requests to the console, or the PropertyConfigurator, which registers Appender objects based on Appender classes defined in a configuration file. Both Category and Appender may use logging Priority and (optionally) Filters to decide if they are interested in a particular logging request. An Appender can use a Layout to localize and format the message before publishing it to the output world.
The HP Logging Mechanism consists of a log handler, zero or more log writers, and one or more log channels, as illustrated in Figure below.
The log handler is implemented as a singleton Java Bean. It is accessible from the com.hp.mw.common.util.LogHandlerFactory which returns the single instance of com.hp.mw.common.util.LogHandler.
The following code illustrates how to obtain the LogHandler:
LogHandler handler; handler = LogHandlerFactory.getHandler();
Log channels are virtual destinations; they receive messages and pass them to the log writers that are registered to receive them. They are not aware of the message formatting that might occur and are not aware of the logging tools that are used to view or store the messages. Log writers are registered for channels. When a log channel receives a message, and if that channel has a registered log writer(s), the message is passed along to that writer.
A client may obtain a channel with a specific name as follows.
LogChannel channel; channel = LogChannelFactory.getChannel("myapplication" );
In order to abstract the destination of a log message (e.g., console, file, database), the Logging Mechanism relies on log writers. Log writers are defined by the com.hp.mw.common.util.logging.LogWriter interface and are given messages by the channel(s) they service. They are responsible for formatting messages and outputting to the actual destination.
A log formatter is responsible for formatting a log message into a Java String. Since many log writers do not require the String representation, log formatters are not required for every log writer. As a result, the com.hp.mw.common.util.logging.LogMessageFormat interface would be used for formatting messages into Strings when applicable and necessary.
All log channels are created, initially, with a default log threshold. The threshold is the minimum severity of a log message that should be processed for that log channel. The log levels defined by the HP logging mechanisms are as follows:
Log Level Description
The following figure summarizes the different components when using log4j. Applications make logging calls on Channel objects. The Channel forwards to LogWriter logging requests for publication. LogWriter are registered with the handler associated to a Channel. Both LogChannel and LogWritter may use logging LogLevel to decide if they are interested in a particular logging request. A LogWriter can use a LogFormatter to format the message before publishing it to the output world.
An application is internationalized, if it can correctly handle different encodings of character data. An application is localized, if it formats and interprets data (dates, times, timezones, currencies, messages and so on) according to rules specific to the user's locale (country and language).
Internationalization (I18N) is the process of designing an application so that it can be adapted to various languages and regions without engineering changes. Localization (L10N) is the use of locale-specific language and constructs at run time.
Java Internationalization shows how to write software that is multi-lingual, using Unicode, a standard system that supports hundreds of character sets. The Java Internationalization API is a comprehensive set of APIs for creating multilingual applications. The JDK internationalization features, from its version 1.1, include:
Users of the Java internationalization interfaces should be familiar with the following interfaces included in the Java Developer's Kit (JDK):
The concept of a Locale object, which identifies a specific cultural region, includes information about the country or region. If a class varies its behavior according to Locale, it is said to be locale-sensitive. For example, the NumberFormat class is locale-sensitive; the format of the number it returns depends on the Locale. Thus NumberFormat may return a number as 902 300 (France), or 902.300 (Germany), or 902,300 (United States). Locale objects are only identifiers.
Most operating systems allow to indicate their locale or to modify it. For instance Windows NT does this through the control panel, under the Regional Option icon. In Java, you can get the Locale object that matches the user's control-panel setting using myLocale = Locale.getDefault();. You can also create Locale objects for specific places by indicating the language and country you want, such as myLocale = new Locale("fr", "CA"); for "Canadian French."
The next example creates Locale objects for the English language in the United States and Great Britain:
bLocale = new Locale("en", "US"); cLocale = new Locale("en", "GB");
The strings you pass to the Locale constructor are two-letter language and country codes, as defined by ISO standards.
The first step in making an international Java program is to isolate all elements of your Java code that will need to change in another country. This includes user-interface text -- label text, menu items, shortcut keys, messages, and the like.
The ResourceBundle class is an abstract class that provides an easy way to organize and retrieve locale-specific strings or other resources. It stores these resources in an external file, along with a key that you use to retrieve the information. You'll create a ResourceBundle for each locale your Java program supports.
The ResourceBundle class is an abstract class in the java.util package. You can provide your own subclass of ResourceBundle or use one of the subclass implementations, as in the case of PropertyResourceBundle or ListResourceBundle.
Resource bundles inherit from the ResourceBundle class and contain localized elements that are stored external to an application. Resource bundles share a base name. The base name TeT_Bundle, to display transactional messages such as T�ransaction Commited� might be selected because of the resources it contains. Locale information further differentiates a resource bundle. For example, TeT_Bundle_it means that this resource bundle contains locale-specific transactional messages for Italian.
To select the appropriate ResourceBundle, invoke the ResourceBundle.getBundle method. The following example selects the TeT_Bundle ResourceBundle for the Locale that matches the French language, the country of Canada.
Locale currentLocale = new Locale("fr", "CA"); ResourceBundle introLabels = ResourceBundle.getBundle("TeT_Bundle", currentLocale);
Java loads your resources based on the locale argument to the getBundle method. It searches for matching files with various suffixes, based on the language, country, and any variant or dialect to try to find the best match. Java tries to find a complete match first, and then works its way down to the base filename as a last resort.
You should always supply a base resource bundle with no suffixes, so that your program will still work if the user's locale does not match any of the resource bundles you supply. The default file can contain the U.S. English strings. Then you should provide properties files for each additional language you want to support.
Basically, a resource bundle is a container for key/value pairs. The key is used to identify a locale-specific resource in a bundle. If that key is found in a particular resource bundle, its value is returned.
The jdk API defines two kinds of ResourceBundle subclasses -- the PropertyResourceBundle and ListResourceBundle.
A PropertyResourceBundle is backed by a properties file. A properties file is a plain-text file that contains translatable text. Properties files are not part of the Java source code, and they can contain values for String objects only. A simple default properties file, named hpts_Bundle.properties, for messages sent by HPTS could be.
# Sample properties file for demonstrating PropertyResourceBundle # Text to inform on transaction outcomes in English (by default) trans_committed = Transaction Committed trans_rolledback=Transaction Rolled Back # �
The equivalent properties file, hpts_Bundle_fr_FR.properties, for French would be:
# Sample properties file for demonstrating PropertyResourceBundle # Text to inform on transaction outcomes in French trans_committed = La Transaction a �t� Valid�e trans_rolledback = La Transaction a�t� Abandonn�e # �
The following example illustrates how to use the internationalization API allowing separating the text with a language specified by the user, from the source code.
import java.util.*; import Demo.*; import java.io.*; import com.arjuna.OrbCommon.*; import com.arjuna.CosTransactions.*; import org.omg.CosTransactions.*; import org.omg.*; public class TransDemoClient { public static void main(String[] args) { String language; String country; if (args.length != 2) { language = new String("en"); country = new String("US"); } else { language = new String(args[0]); country = new String(args[1]); } Locale currentLocale; ResourceBundle messages; currentLocale = new Locale(language, country); trans_message = ResourceBundle.getBundle( "hpts_Bundle", currentLocale); try { ORBInterface.initORB(args, null); OAInterface.initOA(); String ref = new String(); BufferedReader file = new BufferedReader(new FileReader("DemoObjReference.tmp")); ref = file.readLine(); file.close(); org.omg.CORBA.Object obj = ORBInterface.orb().string_to_object(ref); DemoInterface d = (DemoInterface) DemoInterfaceHelper.narrow(obj); OTS.get_current().begin(); d.work(); OTS.get_current().commit(true); System.out.println(tran_message.getString("trans_committed")); } catch (Exception e) { System.out.println(tran_message.getString("trans_rolledback")); } } }
In the following example the language code is fr (French) and the country code is FR (France), so the program displays the messages in French:
% java TransDemoClient fr FR La Transaction a �t� valid�e
The following ant task is provided in buildsystem.jar to automate the creation of resource bundles: com.hp.mw.buildsystem.doclet.resbundledoclet.ResourceBundleDoclet, which is a doclet for the JavaDoc tool that ships with the JDK. It produces resource bundle property files from comments placed in Java source. The comments have the following format:
/** * @message [key] [id] [text] * e.g., @message foo foo This is a message: {0} */
Where [key] is the key used to look up the corresponding message ([text]) in the resource bundle. The [id] field is typically the same as [key] but need not be: it is output with the internationalized message and is meant to be used by technical support in order to identify the [key][message] pair in a language independent manner.
It takes the following runtime options:
The task can be declared within ant in the following way:
<doclet name="com.hp.mw.buildsystem.doclet.resbundledoclet.ResourceBundleDoclet"> <path> <pathelement path="${com.hp.mw.ext.depends.classpath}"/> </path> <param name="-basedir" value="${com.hp.mwlabs.ts.arjuna.dest}"/> <param name="-resourcebundle" value="${com.hp.mwlabs.ts.arjuna.resourcebundle}"/> </doclet>
Below is a sample of the internationalized messages used in the Transaction Service.
/** * BasicAction does most of the work of an atomic action, but does not manage * thread scoping. This is the responsibility of any derived classes. * * @author Mark Little (mark@arjuna.com) * @version $Id: internationalization.xml,v 1.1 2006/03/07 17:59:23 mlittle Exp $ * @since JTS 1.0. * * * * @message com.arjuna.ats.arjuna.coordinator.BasicAction_1 * [com.arjuna.ats.arjuna.coordinator.BasicAction_1] - Action nesting * error - deletion of action id {0} invoked while child actions active * @message com.arjuna.ats.arjuna.coordinator.BasicAction_2 * [com.arjuna.ats.arjuna.coordinator.BasicAction_2] - Aborting child * {0} * @message com.arjuna.ats.arjuna.coordinator.BasicAction_3 * [com.arjuna.ats.arjuna.coordinator.BasicAction_3] - Destructor of * still running action id {0} invoked - Aborting * @message com.arjuna.ats.arjuna.coordinator.BasicAction_4 * [com.arjuna.ats.arjuna.coordinator.BasicAction_4] - The Arjuna * licence only allows a single resource to be registered. Please apply * for a new licence. * @message com.arjuna.ats.arjuna.coordinator.BasicAction_5 * [com.arjuna.ats.arjuna.coordinator.BasicAction_5] - Activate of * atomic action with id {0} and type {1} unexpectedly failed */
Which, when processed by the doclet, generates the following within the resource bundle:
com.arjuna.ats.arjuna.coordinator.BasicAction_1=[com.arjuna.ats.arjuna.coordinator.BasicAction_1] - Action nesting error - deletion of action id {0} invoked while child actions active com.arjuna.ats.arjuna.coordinator.BasicAction_2=[com.arjuna.ats.arjuna.coordinator.BasicAction_2] - Aborting child {0} com.arjuna.ats.arjuna.coordinator.BasicAction_3=[com.arjuna.ats.arjuna.coordinator.BasicAction_3] - Destructor of still running action id {0} invoked - Aborting com.arjuna.ats.arjuna.coordinator.BasicAction_4=[com.arjuna.ats.arjuna.coordinator.BasicAction_4] - The Arjuna licence only allows a single resource to be registered. Please apply for a new licence. com.arjuna.ats.arjuna.coordinator.BasicAction_5=[com.arjuna.ats.arjuna.coordinator.BasicAction_5] - Activate of atomic action with id {0} and type {1} unexpectedly failed
Factory for Log objects. LogFactory returns different subclasses of logger according to which logging subsystem is chosen. The log system is selected through the property com.arjuna.common.utils.logger. Supported log systems are:
The underlying log system can be selected via the following property name:
The allowed values for the property are:
To set log4j (default log system), provide the following System properties:
-Dcom.arjuna.common.util.logger=log4j -Dlog4j.configuration=file://c:/Projects/common/log4j.properties
Simple use example:
import com.arjuna.common.util.logging.*; public class Test { static Log mylog = LogFactory.getLog(Test.class); public static void main(String[] args) { String param0 = "foo"; String param1 = "bar"; // different log priorities mylog.debug("key1", new Object[]{param0, param1}); mylog.info("key2", new Object[]{param0, param1}); mylog.warn("key3", new Object[]{param0, param1}); mylog.error("key4", new Object[]{param0, param1}); mylog.fatal("key5", new Object[]{param0, param1}); // optional throwable Throwable throwable = new Throwable(); mylog.debug("key1", new Object[]{param0, param1}, throwable); mylog.info("key2", new Object[]{param0, param1}, throwable); mylog.warn("key3", new Object[]{param0, param1}, throwable); mylog.error("key4", new Object[]{param0, param1}, throwable); mylog.fatal("key5", new Object[]{param0, param1}, throwable); // debug guard to avoid an expensive operation if the logger does not // log at the given level: if (mylog.isDebugEnabled()) { String x = expensiveOperation(); mylog.debug("key6", new Object[]{x}); } // ****************************************************** // fine-grained debug extensions mylog.debug(CommonDebugLevel.OPERATORS, CommonVisibilityLevel.VIS_PUBLIC, CommonFacilityCode.FAC_ALL, "This debug message is enabled since it matches default�+ Finer Values"); mylog.setVisibilityLevel(CommonVisibilityLevel.VIS_PACKAGE); mylog.setDebugLevel(CommonDebugLevel.CONSTRUCT_AND_DESTRUCT); mylog.setFacilityCode(CommonFacilityCode.FAC_ALL); mylog.mergeDebugLevel(CommonDebugLevel.ERROR_MESSAGES); if (mylog.debugAllowed(CommonDebugLevel.OPERATORS, CommonVisibilityLevel.VIS_PUBLIC, CommonFacilityCode.FAC_ALL)) { mylog.debug(CommonDebugLevel.OPERATORS, CommonVisibilityLevel.VIS_PUBLIC, CommonFacilityCode.FAC_ALL, "key7", new Object[]{"foo", "bar"}, throwable); } } }
Independent of the log system chosen, it is possible to log all messages over a given severity threshold into a file. This is useful to guarantee that e.g., error and fatal level messages are not lost despite a user has not set up a log framework, such as log4j
Usage of this feature is simple and can be controlled through a set of properties. These can be provided through the Property Manager or as System properties.
Table 11.1. Properties to control default file-based logging (default values are highlighted)
Property Name | Values | Description |
---|---|---|
com.arjuna.common.logging.default | true/ false | Enable/disable default file-based logging |
com.arjuna.common.util.logging.default.level | Info /error/fatal | Severity level for this log |
com.arjuna.common.util.logging.default.showLogName | true/ false | Record the fully qualified log name |
com.arjuna.common.util.logging.default.showShortLogName | true /false | Record an abbreviated log name |
com.arjuna.common.util.logging.default.showDate | true /false | Record the date |
com.arjuna.common.util.logging.default.logFile | error.log (default) | File to use for default logging. This can be an absolute filename or relative to the working directory |
com.arjuna.common.util.logging.default.logFileAppend | true /false | Append to the log file above in case that this file already exists |
Finer-grained logging in CLF is available through a set of debug methods:
public void debug(long dl, long vl, long fl, Object message); public void debug(long dl, long vl, long fl, Throwable throwable); public void debug(long dl, long vl, long fl, String key, Object[] params); public void debug(long dl, long vl, long fl, String key, Object[] params, Throwable throwable);
All of these methods take the three following parameters in addition to the log messages and possible exception:
dl - The debug finer level associated with the log message. That is, the logger object will only log if the DEBUG level is allowed and dl is either equal or greater than the debug level assigned to the logger Object. See the table below for possible values.
vl - The visibility level associated with the log message. That is, the logger object will only log if the DEBUG level is allowed and vl is either equal or greater than the visibility level assigned to the logger Object. See the table below for possible values.
fl - The facility code level associated with the log message. That is, the logger object will only log if the DEBUG level is allowed and fl is either equal or greater than the facility code level assigned to the logger Object. See the table below for possible values.
The debug message is sent to the output only if the specified debug level, visibility level, and facility code match those allowed by the logger.
Possible values for debug finer level, visibility level and facility code level are declared in the classes DebugLevel, VisibilityLevel and FacilityCode respectively. This is useful for programmatically using fine-grained debugging.
Table 11.2. Possible settings for finer debug level (class DebugLevel)
Debug Finer Level | Value | Description |
---|---|---|
NO_DEBUGGING | 0x0000 | No debugging |
CONSTRUCTORS | 0x0001 | Only output for constructors |
DESTRUCTORS | 0x0002 | Only output for finalizers |
CONSTRUCT_AND_DESTRUCT | CONSTRUCTORS | DESTRUCTORS | |
FUNCTIONS | 0x0010 | Only output for methods |
OPERATORS | 0x0020 | Only output for methods such as equals, notEquals etc. |
FUNCS_AND_OPS | FUNCTIONS | OPERATORS | |
ALL_NON_TRIVIAL | CONSTRUCT_AND_DESTRUCT | FUNCTIONS | OPERATORS | |
TRIVIAL_FUNCS | 0x0100 | Only output from trivial methods |
TRIVIAL_OPERATORS | 0x0200 | Only output from trivial operators |
ALL_TRIVIAL | TRIVIAL_FUNCS | TRIVIAL_OPERATORS | |
ERROR_MESSAGES | 0x0400 | Only output from debugging error/warning messages |
FULL_DEBUGGING | 0xffff | Output all debugging messages |
Table 11.3. Possible settings for visibility level (class VisibilityLevel)
Visibility Level | Value | Description |
---|---|---|
VIS_NONE | 0x0000 | No visibility |
VIS_PRIVATE | 0x0001 | Only from private methods |
VIS_PROTECTED | 0x0002 | Only from protected methods |
VIS_PUBLIC | 0x0004 | Only from public methods |
VIS_PACKAGE | 0x0008 | Only from package methods |
VIS_ALL | 0xffff | Output all visibility levels. |
Table 11.4. Possible settings for facility code level (class FacilityCode)
Facility Code Level | Value | Description |
---|---|---|
FAC_NONE | 0x0000 | No facility |
FAC_ALL | 0xffffffff | Output all facility codes |
At runtime, the fine-grained debug settings are controlled through a set of properties, listed in the table below:
The JBoss Testsuite module is a collection of JUnit tests which require a running JBoss instance for in-container testing. Unit tests not requiring the container reside in the module they are testing.
The setup and initialization of the container is performed in the testsuite's build.xml file. The testsuite module also provides utility classes which support the deployment of test artifacts to the container.
A source distribution of JBoss must be available to run the testsuite. This document applies only to JBoss 3.2.7 and above.
Before building the testsuite, the rest of the project must be built:
Unix
cd build ./build.sh
Windows
cd build build.bat
To build and run the testsuite, type the following. Note that you no longer are required to seperately start a JBoss server instance before running the testsuite.
Unix
cd ../testsuite ./build.sh tests
Windows
cd ../testsuite build.bat tests
The build script will start and stop various configurations of JBoss, and then run tests against those configurations.
To run an individual test, you will need to start the appropriate configuration. For most tests, this will be the "all" configuration:
build/output/jboss-5.0.0alpha/bin/run.sh -c all
And then tell the testsuite which test you want to run:
cd testsuite ./build.sh one-test -Dtest=org.jboss.test.package.SomeTestCase
Most of the tests are against a single server instance started on localhost. However, the clustering tests require two server instances. By default, the testsuite will bind one of these instances to localhost, and the other will be bound to hostname. You can override this in the testsuite/local.properties file.
node0=localhost ... node1=MyHostname
The nodes must be bound to different IP addresses, otherwise there will be port conflicts. Also, note these addresses must be local to the box you are running the testsuite on, the testsuite will need to start each server process before running the tests.
You can also use the udpGroup property to prevent your clustering tests from interfering with others on the same network using the udpGroup property. This can be passed at the command line or in the local.properties file. This will be passed to the servers under test using the -u option:
./build.sh -DudpGroup=128.1.2.3 tests ... [server:start] java org.jboss.Main -c minimal -b localhost -u 128.1.2.3
The testsuite build.xml has been refactored to allow automated testing of multiple server configurations. The testsuite build scripts include facilities for customizing server configurations and starting and stopping these configurations. Most notably, this improvement allows clustering unit tests to be completely automated.
Tests are now grouped into targets according to which server configuration they require. Here is a summary of the targets called by the top-level tests target:
Table 12.1. Build Targets and Descriptions
Target | Description |
---|---|
jboss-minimal-tests | Tests requiring the minimal configuration. |
jboss-all-config-tests | Runs the all configuration. Most tests can go here. |
tests-security-manager | Runs the default configuration with a security manager. |
tests-clustering | Creates two custom configurations based on the all configuration. Tests run in this target should extend JBossClusteredTestCase to access cluster information. |
tomcat-ssl-tests | Creates and runs a configuration with Tomcat SSL enabled. |
tomcat-sso-tests | Creates and runs a configuration with SSO enabled. |
tomcat-sso-clustered-tests | Creates and runs two nodes with SSO enabled. |
The testsuite build scripts have been reorganized. The code generation and jar targets have been extracted to their own files in testsuite/imports. These targets are imported for use by the main build.xml file. Also, it is important to note that module and library definitions are in different files.
Table 12.2. Summary of build files
Build File | Description |
---|---|
testsuite/build.xml | Contains test targets. This file imports the macros and targets from the files below. |
testsuite/imports/server-config.xml | Contains macros for creating and starting different server configurations. |
tools/etc/buildmagic/modules.xml | Similar to modules.ent, this file contains the Ant classpath definitions for each JBoss module. |
tools/etc/buildmagic/thirdparty.xml | Like thirdparty.ent, this contains the Ant classpath definitions for each third party library. |
testsuite/imports/code-generation.xml | Xdoclet code generation. This file has the following targets: compile-bean-source, compile-mbean-sources, compile-xmbean-dds, compile-proxycompiler-bean-source. |
testsuite/imports/test-jars.xml | All jar tasks. The top-level jars target calls each module's _jar-* target (eg: _jar-aop). |
Functional tests need to be located in the module which they test. The testsuite needs to be able to include these in the "tests" target.
To contribute functional tests to the testsuite, each module should contain a tests directory with with a build.xml. The build.xml should contain at least one target, functional-tests, which executes JUnit tests. The functional-tests target should build the tests, but should assume that the module itself has been built. The tests/build.xml should use the Ant <import/> task to reuse targets and property definitions from the module's main build.xml.
Functional test source code belongs in the tests/src directory. The package structure of the tests should mirror the module's package structure, with an additional test package below org/jboss.
For example, classes under org.jboss.messaging.core should have tests under org.jboss.test.messaging.core.
The testsuite/build.xml will include a functional-tests target which uses the <subant> task to call the funtional-tests target on each module's tests/build.xml. The testsuite will only override properties relevant to the junit execution, and the module's tests/build.xml must use these properties as values for the corresponding attributes:
The following properties are not set by the testsuite:
Example 12.1. Example Build Script for Functional Tests
<?xml version="1.0" encoding="UTF-8"?> <!-- ====================================================================== --> <!-- --> <!-- JBoss, the OpenSource J2EE webOS --> <!-- --> <!-- Distributable under LGPL license. --> <!-- See terms of license at http://www.gnu.org. --> <!-- --> <!-- ====================================================================== --> <!-- $Id: testsuite.xml,v 1.4 2006/02/22 20:56:55 rgenova Exp $ --> <project default="tests" name="JBoss/Messaging"> <!-- overridden to resolve thirdparty & module deps --> <dirname property="remote.root" file="${basedir}"/> <dirname property="project.root" file="${remote.root}"/> <import file="../../tools/etc/buildmagic/build-common.xml"/> <import file="../../tools/etc/buildmagic/libraries.xml"/> <import file="../../tools/etc/buildmagic/modules.xml"/> <!-- ================================================================== --> <!-- Configuration --> <!-- ================================================================== --> <!-- Module name(s) & version --> <property name="module.name" value="jms"/> <property name="module.Name" value="JBoss Messaging"/> <property name="module.version" value="5.0.0"/> <!-- ========= --> <!-- Libraries --> <!-- ========= --> <!-- The combined library classpath --> <path id="library.classpath"> <path refid="apache.log4j.classpath"/> <path refid="oswego.concurrent.classpath"/> <path refid="junit.junit.classpath"/> <path refid="jgroups.jgroups.classpath"/> <path refid="apache.commons.classpath"/> </path> <!-- ======= --> <!-- Modules --> <!-- ======= --> <!-- The combined dependent module classpath --> <path id="dependentmodule.classpath"> <path refid="jboss.common.classpath"/> <path refid="jboss.jms.classpath"/> </path> <!-- ===== --> <!-- Tasks --> <!-- ===== --> <property name="source.tests.java" value="${module.source}"/> <property name="build.tests.classes" value="${module.output}/classes"/> <property name="build.tests.lib" value="${module.output}/lib"/> <property name="build.tests.output" value="${module.output}/reports"/> <property name="build.performance.tests.output" value="${module.output}/reports/performance"/> <property name="build.tests.archive" value="jboss-messaging-tests.jar"/> <path id="test.classpath"> <path refid="library.classpath"/> <path refid="dependentmodule.classpath"/> </path> <!-- Compile all test files --> <target name="compile-test-classes"> <mkdir dir="${build.tests.classes}"/> <javac destdir="${build.tests.classes}" optimize="${javac.optimize}" target="1.4" source="1.4" debug="${javac.debug}" depend="${javac.depend}" verbose="${javac.verbose}" deprecation="${javac.deprecation}" includeAntRuntime="${javac.include.ant.runtime}" includeJavaRuntime="${javac.include.java.runtime}" failonerror="${javac.fail.onerror}"> <src path="${source.tests.java}"/> <classpath refid="test.classpath"/> <include name="**/*.java"/> </javac> </target> <target name="tests-jar" depends="compile-test-classes" description="Creates the jar file with all the tests"> <mkdir dir="${build.tests.lib}"/> <!-- Build the tests jar --> <jar jarfile="${build.tests.lib}/${build.tests.archive}"> <fileset dir="${build.tests.classes}"> <include name="org/jboss/test/messaging/**"/> </fileset> </jar> </target> <!-- The values from imported files or set by the calling ant tasks will take precedence over the values specified below. --> <property name="junit.printsummary" value="true"/> <property name="junit.haltonerror" value="true"/> <property name="junit.haltonfailure" value="true"/> <property name="junit.fork" value="true"/> <property name="junit.includeantruntime" value="true"/> <property name="junit.timeout" value=""/> <property name="junit.showoutput" value="true"/> <property name="junit.jvm" value=""/> <property name="junit.jvm.options" value=""/> <property name="junit.formatter.usefile" value="false"/> <property name="junit.batchtest.todir" value="${build.tests.output}"/> <property name="junit.batchtest.haltonerror" value="true"/> <property name="junit.batchtest.haltonfailure" value="true"/> <property name="junit.batchtest.fork" value="true"/> <property name="junit.test.haltonfailure" value="true"/> <property name="junit.test.haltonerror" value="true"/> <target name="prepare-testdirs" description="Prepares the directory structure required by a test run"> <mkdir dir="${build.tests.output}"/> </target> <target name="tests" depends="tests-jar, prepare-testdirs" description="Runs all available tests"> <junit printsummary="${junit.printsummary}" fork="${junit.fork}" includeantruntime="${junit.includeantruntime}" haltonerror="${junit.haltonerror}" haltonfailure="${junit.haltonfailure}" showoutput="${junit.showoutput}"> <classpath> <path refid="test.classpath"/> <pathelement location="${build.tests.lib}/${build.tests.archive}"/> <pathelement location="${module.root}/etc"/> </classpath> <formatter type="plain" usefile="${junit.formatter.usefile}"/> <batchtest fork="${junit.batchtest.fork}" todir="${junit.batchtest.todir}" haltonfailure="${junit.batchtest.haltonfailure}" haltonerror="${junit.batchtest.haltonerror}"> <formatter type="plain" usefile="${junit.formatter.usefile}"/> <fileset dir="${build.tests.classes}"> <include name="**/messaging/**/*Test.class"/> <exclude name="**/messaging/**/performance/**"/> </fileset> </batchtest> </junit> </target> <target name="test" depends="tests-jar, prepare-testdirs" description="Runs a single test, specified by its FQ class name via 'test.classname'"> <fail unless="test.classname" message="To run a single test, use: ./build.sh test -Dtest.clasname=org.package.MyTest"/> <junit printsummary="${junit.printsummary}" fork="${junit.fork}" includeantruntime="${junit.includeantruntime}" haltonerror="${junit.haltonerror}" haltonfailure="${junit.haltonfailure}" showoutput="${junit.showoutput}"> <classpath> <path refid="test.classpath"/> <pathelement location="${build.tests.lib}/${build.tests.archive}"/> <pathelement location="${module.root}/etc"/> </classpath> <formatter type="plain" usefile="${junit.formatter.usefile}"/> <test name="${test.classname}" fork="${junit.batchtest.fork}" todir="${junit.batchtest.todir}" haltonfailure="${junit.test.haltonfailure}" haltonerror="${junit.test.haltonerror}"> </test> </junit> </target> <target name="performance-tests"/> <target name="functional-tests" depends="tests"/> <!-- Clean up all build output --> <target name="clean" description="Cleans up most generated files."> <delete dir="${module.output}"/> </target> <target name="clobber" depends="clean"/> </project>
Custom JBoss configurations can be added using the create-config macro as demonstrated by this tomcat-sso-tests target. The create-config target has the following attributes/elements:
In addition, if you need to override configuration settings or add new content, this can be done by creating a directory with the same name as the newconf attribute value under the testsuite/src/resource/tests-configs directory. In this case, there is a tomcat-sso directory which adds some security files to the conf directory, removes the jbossweb sar dependencies it does not need, and enables the sso value in the server.xml:
$ ls -R src/resources/test-configs/tomcat-sso src/resources/test-configs/tomcat-sso: CVS/ conf/ deploy/ src/resources/test-configs/tomcat-sso/conf: CVS/ login-config.xml* sso-roles.properties* sso-users.properties* src/resources/test-configs/tomcat-sso/deploy: CVS/ jbossweb-tomcat50.sar/ src/resources/test-configs/tomcat-sso/deploy/jbossweb-tomcat50.sar: CVS/ META-INF/ server.xml* src/resources/test-configs/tomcat-sso/deploy/jbossweb-tomcat50.sar/META-INF: CVS/ jboss-service.xml*
The full tomcat-sso-tests target is shown here.
<target name="tomcat-sso-tests" description="Tomcat tests requiring SSO configured"> <!-- Create the sso enabled tomcat config starting with the default config --> <create-config baseconf="default" newconf="tomcat-sso"> <patternset> <include name="conf/**" /> <include name="deploy/jbossweb*.sar/**" /> <include name="deploy/jmx-invoker-adaptor-server.sar/**" /> <include name="lib/**" /> </patternset> </create-config> <start-jboss conf="tomcat-sso" /> <wait-on-host /> <junit dir="${module.output}" printsummary="${junit.printsummary}" haltonerror="${junit.haltonerror}" haltonfailure="${junit.haltonfailure}" fork="${junit.fork}" timeout="${junit.timeout}" jvm="${junit.jvm}"> <jvmarg value="${junit.jvm.options}"/> <sysproperty key="jbosstest.deploy.dir" file="${build.lib}"/> <sysproperty key="build.testlog" value="${build.testlog}"/> <sysproperty key="log4j.configuration" value="file:${build.resources}/log4j.xml"/> <classpath> <pathelement location="${build.classes}"/> <pathelement location="${build.resources}"/> <path refid="tests.classpath"/> </classpath> <formatter type="xml" usefile="${junit.formatter.usefile}"/> <batchtest todir="${build.reports}" haltonerror="${junit.batchtest.haltonerror}" haltonfailure="${junit.batchtest.haltonfailure}" fork="${junit.batchtest.fork}"> <fileset dir="${build.classes}"> <patternset refid="tc-sso.includes"/> </fileset> </batchtest> </junit> <stop-jboss /> </target>
This section describes how to write tests that depend on a deployed artifact such as an EAR.
Deployment of any test deployments is done in the setup of the test. For example, the HibernateEjbInterceptorUnitTestCase would add a suite method to deploy/undeploy a har-test.ear:
public class HibernateEjbInterceptorUnitTestCase extends JBossTestCase { /** Setup the test suite. */ public static Test suite() throws Exception { return getDeploySetup(HibernateEjbInterceptorUnitTestCase.class, "har-test.ear"); } ... }
If you need to perform additional test setup/tearDown you can do that by extending the test setup class like this code from the SRPUnitTestCase:
/** Setup the test suite. */ public static Test suite() throws Exception { TestSuite suite = new TestSuite(); suite.addTest(new TestSuite(SRPUnitTestCase.class)); // Create an initializer for the test suite TestSetup wrapper = new JBossTestSetup(suite) { protected void setUp() throws Exception { super.setUp(); deploy(JAR); // Establish the JAAS login config String authConfPath = super.getResourceURL("security-srp/auth.conf"); System.setProperty("java.security.auth.login.config", authConfPath); } protected void tearDown() throws Exception { undeploy(JAR); super.tearDown(); } }; return wrapper; }
We use the ant-task <junit> to execute tests. That task uses the concept of formatters. The actual implementation uses the XML formater by specifying type="xml" in the formatter attribute.
If we need to execute the same test more than once, using this default formatter will always overwrite the results. For keeping these results alive, we have created another formatter. So, use these steps to keep JUnit results between different runs:
Define the sysproperty "jboss-junit-configuration" during the jUnit calls. Change the formatter and set a different extension for keeping the files between different executions:
Set the class by classname="org.jboss.ant.taskdefs.XMLJUnitMultipleResultFormatter
Here is a complete example of the changes:
<junit dir="${module.output}" printsummary="${junit.printsummary}" haltonerror="${junit.haltonerror}" haltonfailure="${junit.haltonfailure}" fork="${junit.fork}" timeout="${junit.timeout}" jvm="${junit.jvm}" failureProperty="tests.failure"> .... <sysproperty key="jboss-junit-configuration" value="${jboss-junit-configuration}"/> <formatter classname="org.jboss.ant.taskdefs.XMLJUnitMultipleResultFormatter" usefile="${junit.formatter.usefile}" extension="-${jboss-junit-configuration}.xml" /> ..... </junit>
If a test cannot be fixed for some reason, it should be added to the bad.test excludes. This is maintained near the top of the testsuite/build.xml. The patternset will be used to exclude tests all calls to JUnit within the testsuite.
<!-- Tests that are currently broken --> <patternset id="badtest.excludes"> <exclude name="org/jboss/test/aop/test/RemotingUnitTestCase"/> <!-- The media ejb is not active --> <exclude name="org/jboss/test/media/**"/> <!-- Needs apache ? --> <exclude name="org/jboss/test/cluster/httpsessionreplication/**"/> <exclude name="org/jboss/test/cache/test/local/ConcurrentTransactionalUnitTestCase.class"/> </patternset>
Click the "Start Progress" link
Check out appropriate cumulative patch branch, for instance for the AS4.0.2 release
svn checkout https://svn.jboss.org/repos/jbossas/branches/JBoss_4_0_2_CP
Implement the fix and check it in.
If a bug is in app server component such as Web Services or Hibernate, a cumulative patch branch must be created off of the component release that was included into the app server. For instance, for Hibernate 3.1.3:
Click "Resolve". In the "Patch Instructions" field enter installation instructions on how to apply the patch.
Use the Support Patch Template as a basis for these instructions.
QA will tag cumulative patch branch upon code freeze. For instance,
svn copy https://svn.jboss.org/repos/jbossas/branches/JBoss_4_0_2_CP https://svn.jboss.org/repos/jbossas/tags/JBoss_4_0_2_CP03
Click the "Start Progress" link to enter the CVS or SVN branch information.
Enter the branch for the patch. Using that JIRA ID and the tag for the release being patched, create a branch for the patch using the format ReleaseTag_JiraID.
For instance, if the patch issue ID is JBAS-1234 for JBoss 4.0.3, the patch's branch should be JBoss_4_0_3_JBAS-1234.
# svn svn copy https://svn.jboss.org/repos/jbossas/tags/JBoss_4_0_3_SP1/ http://svn.jboss.org/repos/jbossas/branches/JBoss_4_0_3_SP1_JBAS-1234 svn checkout https://svn.jboss.org/repos/jbossas/branches/JBoss_4_0_3_SP1_JBAS-1234 jbas-1234_local_dir # cvs cvs rtag -r JBoss_4_0_3 -b JBoss_4_0_3_JBAS-1234 jboss-4.0.x cvs co -r JBoss_4_0_3_JBAS-1234 jboss-4.0.x
If the project being patched is not in JBoss hosted CVS, attach the source diff to this case.
Build the patched jars from the above branch and attach them to the JIRA issue.
Click "Hand Over to QA". In the "Patch Instructions" field enter installation instructions on how to apply the patch to an existing installation.
Use the Support Patch Template as a basis for these instructions.
Below is the skeleton of the directions that should be entered in the "Patch Instructions" field in JIRA.
PATCH NAME: [Not needed for the Cumulative Patch tasks] JBAS-XXXX PRODUCT NAME: [Not needed for the Cumulative Patch tasks] JBoss Application Server VERSION: [Not needed for the Cumulative Patch tasks] 4.0.2 SHORT DESCRIPTION: [What problem this patch fixes.] ex: "A NullPointerException is no longer thrown when the password field is left blank." LONG DESCRIPTION: [Detailed explanation of the problem.] ex: "Prior to this fix, blah happened. With this fix blah will happen instead. This is because blah blah blah. MANUAL INSTALL INSTRUCTIONS: [How a user should manually install this patch.] ex: "Rename %JBOSS_HOME%/lib/someJar.jar to "someJar.replacedBy.JBAS-xxxx.jar.old" Copy the new someJar.jar to %JBOSS_HOME%/lib/" COMPATIBILITY: [known usages and known combinations that don't work] ex: "portal 2.x in a given jems bundle does not work with this change" DEPENDENCIES: [list any patches this patch is dependent on. Not needed for the Cumulative Patch tasks] ex: 4.0.2-SP2 SUPERSEDES: [list any patches this patch supersedes] ex: JBAS-1450 SUPERSEDED BY: [list any patches this patch is superseded by. Not needed for the Cumulative Patch tasks] ex: 4.0.2-SP3 CREATOR: [author of this patch] DATE: [date these instructions were written]
# Howto test a patch in the QA lab # create a directory which corresponds to the JIRA id for the patch mkdir JBAS-1234 cd JBAS-1234 #set your JAVA_HOME correctly #if jboss 3.x, use jdk 1.3 #export JAVA_HOME=/opt/jdk1.3.1_13/ #if jboss 4.x use jdk 1.4 export JAVA_HOME=/opt/j2sdk1.4.2_09/ #Make sure you are testing on a 32-bit box # get the source distribution of the *targetted* version of jboss tar xvzf /opt/src/jboss-4.0.2-src.tar.gz # build Jboss & save the original output cd jboss-4.0.2-src/build; sh build.sh cp -r output output-orig cd ../.. # download the patched binaries into a binaries directory mkdir binaries; cd binaries; #download them locally & upload via scp (if in qa lab) #TODO: look at using wget to retrieve patches # capture the md5 of each jar and add it as comment to jira md5sum *.jar #use 'find' to locate where in the output each jar is, in #order to create the right mirror of the install distro find ../jboss-4.0.2-src/build/output -name jboss.jar # create a mirror of the install distro using the patched jars #for each configuration 'find' listed mkdir -p jboss-4.0.2/server/all/lib cp jboss.jar jboss-4.0.2/server/all/lib # copy the jars to the output, verifying each jar copies correctly # NOTE: make sure to replace all the jars in the source tree # not just the ones under build/output since the client classpath # is affected by the jars under /thirdparty and */output/lib. cp -ivr jboss-4.0.2 ../jboss-4.0.2-src/build/output # save the patched version for later (to switch back & forth) cd ../jboss-4.0.2-src/build cp -r output output-patched # run the testsuite against the patched version (let node0 default to localhost) cd ../testsuite sh build.sh -Dnode1=$MYTESTIP_1 tests # tests will take 1-2 hours, to save time, verify pass rate every 15 min # if you see several failures, make sure no one else is on who ps --columns 1000 -ef | grep run.jar # once tests complete, save the text report and take a look at it cp output/reports/text/TESTS-Testsuite.txt ../../TESTS-patched.txt # verify any failures also fail on the unpatched version # upon any failure contact QA Lead for help cd ../build; mv output output-patched cp -r output-orig output #find out which mode the failing tests were using and #start the server using the particular configuration #the example below starts the 'all' mode cd output/jboss-4.0.2/ ./bin/run.sh -c all #different shell cd jboss-4.0.2-src/testsuite sh build.sh -Dtest=org.jboss.tests.the.FailingTest one-test # if failure only occurs on patched version, reject the patch # Attach necessary information to JIRA issue # ie, testuite/output/reports/TESTS-org.jboss.test.the.FailingTest.txt # When failing the patch notify developer that patched client jars # were note used, ie, add this as a comment: # NOTE: Patched client jars were not used to validate compatibility. # Upon failure tar up the issue directory and copy to /home/issues cd $ISSUE_HOME/.. tar cvzf JIRA-1234.tar.gz JIRA-1234 cp JIRA-1234.tar.gz /home/issues #note path to tar.gz on JIRA issue
Every JBoss employee sends a weekly status report to his / her manager on the first working day of every week.
This reporting scheme has been established to monitor work progress on outstanding issues and bottlenecks if any.
The format is as follows:
This includes:
JBoss Inc. provides a wide selection of documentation that provides in-depth coverage across the federation of Professional Open Source projects. All documentation is now free. Several versions of our documentation require registration to the JBoss website, which is also free. If you cannot find the answers that you are looking for, you can get additional support from the following sources:
A complete listing of the documentation by project can be found on the Document Index.
For JBoss developers and documentation writers, JIRA and docbook are the two key tools to integrate the documentation process in the development workflow. Now let's clarify documentation responsibilities and adopt a simple process to guarantee our documentation is always accurate and up-to-date.
The development team is responsible for product-specific documentation. Core developers need to maintain the following documents.
Tasks related to producing those documents are managed within the development project's JIRA module. Most of these tasks are assigned to developers within the project but some of them are assigned to documentation team, as we will see in a minute.
The documentation team (Michael Yuan and Norman Richards) is responsible for all "cross-cutting" documents that cover several projects, as well as tutorial / technical evangelism materials. Examples of such documents are as follows.
Tasks related to those documents are managed inside the "documentation" JIRA module. Developers are welcome to raise issues there if you see errors and/or coverage gaps in existing documents.
Before each product release, the documentation team needs to review all the documents maintained by project's core developers (e.g., reference guide and Javadoc). Please create a review task for each document within your project and assign it to a member in the documentation team. The documentation team will read the draft and use that JIRA task to track any issues.
Since our technology is evolving fast, it is crucial for us to keep the documents up-to-date. If you have any development task that might affect the external interface or observed behavior of the product, please check the appropriate "affects" check box at the bottom of the JIRA task information page.
The documentation team also serves as our internal editors for technical articles and books in the JBoss book series. If you are interested in writing articles or books, please let us know. Even if you do not have time to write a whole book, we might still find books / articles you can contribute to. So, it is important to keep us informed about your interests in this area.
The documentation team will help develop proposals and manage the relationship with outside editors. If you sign up to write the article / book, a JIRA task in the documentation module would be created and assigned to you to keep track of the progress.
Writing JBoss documentation using the centralized docbook system is really easy. You first need to check out the docbook-support top level module:
cvs -d:ext:yourname@cvs.sf.net:/cvsroot/jboss co docbook-support.
In the module, you can find the docs/guide directory. Copy that directory to the docs/ directory in your own project and use it as a template for your own docbooks.
For more information about how the directories and build tasks are organized, check out the guide doc in the docbook-support module:
The PDF version is docs/guide/build/en/pdf/jboss-docbook.pdf
The HTML version is docs/guide/build/en/html/index.html
This chapter provides JBoss developers with a guide to the QA lab. It covers usage guidelines as well as setup documentation
Use ssh to log into dev02.pub.qa.atl.jboss.com. All of the servers in the Q/A lab have the same public keys and accounts as cvs.jboss.com. If you can't connect, contact it-ops@jboss.com. Dev02 is available for ad-hoc testing by developers and support engineers. No scheduling is required for this box.
To avoid port conflicts with others, bind listeners to your assigned address. Currently, each account has 4 IP addresses available. These are available as environment variables: $MYTESTIP_1,$MYTESTIP_2, $MYTESTIP_3, $MYTESIP_4. For instance:
./run.sh -b $MYTESTIP_1 -c default
You will find assorted tools and JVM's under /opt. If you can't find something you need, please raise the issue on the JBIT project in JIRA.
Each node has a public IP address for services such as SSH and HTTP, in addition to 256 private IP addresses. The local network is Gigabit Ethernet, with connectivity to the public Internet via a DS3.
To prevent port conflicts, each listener/server process must be bound to a specific private IP address. These IP addresses are delineated per node and per user in the /opt/etc/assigned-ips file, available on each machine. Currently, each user is assigned 4 IP addresses per node. These are automatically set as the environment variables $MYTESTIP_1 $MYTESTIP_2, etc. All ports for a given private IP address may be accessed via SSH tunneling.
The /opt and /home directories on each machine are mapped to the corresponding directories on dev01 via NFS. So your home directory is the same on each machine. The /opt directory holds common packages needed across all nodes, preventing unecessary duplication. Under /opt, you will find several useful packages such as:
Many jdks, including IBM, Sun & JRockit
JDBC drivers under /opt/jdbc-drivers
Under /opt/src, several binary & src distros of JBoss
Several versions of Ant
This section documents the available databases in the QA lab, how to start, stop & access them. JDBC drivers are available under /opt/jdbc-drivers.
For sudo acces to sybase & oracle, see "How do I get DB admin access?"
Table 16.1. Available Databases
Database | Connection URL | Start/Stop | New User |
---|---|---|---|
Oracle 10.1.0.3 | jdbc:oracle:thin:@dev01-priv:1521:qadb01 | /etc/init.d/dbora start|stop | $ sudo su - oracle $ export ORACLE_SID=qadb01 $ sqlplus "/ as sysdba " CREATE USER your_username_here IDENTIFIED BY your_password_here DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp QUOTA UNLIMITED ON users; GRANT DBA to your_username_here; |
MySQL 4.1.10a | jdbc:mysql://dev01-priv/your_database_here | /etc/init.d/mysql.server start|stop | $ /opt/mysql/bin/mysql -u qa -h qadb01 -p Enter password: #request from JBIT in JIRA GRANT ALL PRIVILEGES ON your_db_here.* TO 'your_username_here'@'%' IDENTIFIED BY 'your_password_here'; flush privileges; create database your_database_here; |
Sybase ASE 12.5.2 | jdbc:sybase:Tds:dev01-priv:4100 | sudo su - sybase startserver -f \ ~/sybase/ASE-12_5/install/RUN_sybase01 #stop sudo su - sybase isql -Usa -P -Ssybase01 shutdown go | $ sudo su - sybase $ isql -Usa -P -Ssybase01 sp_addlogin "yourusernamehere", "yourpasswordhere" go create database yourdbhere go use yourdbhere go sp_changedbowner yourusernamehere go |
This section lists the available servers and their configurations. We can move between Windows/SLES/RHEL very quickly. Please ask if you need a different OS. All servers have the domain name of dev??.pub.qa.atl.jboss.com.
Table 16.2. Available Servers
Host | Purpose | OS | CPU | Memory |
---|---|---|---|---|
dev01 | Database/NFS server | RHEL 3/2.4 Kernel | 2 x 3.06 GHz P4 Xeon | 4GB |
dev02 | General usage | RHEL 3/2.4 Kernel | 2 x 3.06 GHz P4 Xeon | 2GB |
dev03 | General usage | RHEL 3/2.4 Kernel | 2 x 3.06 GHz P4 Xeon | 2GB |
dev04 | General usage | SLES 9/2.6 Kernel | 2 x 3.06 GHz P4 Xeon | 2GB |
dev05 | Cruisecontrol - no general access | RHEL 3/2.4 Kernel | 2 x 3.06 GHz P4 Xeon | 2GB |
dev07 | General usage | RHEL 4/2.6 Kernel/64 bit | 2 x 1.4 GHz IA-64 Itanium 2 | 2GB |
dev08 | General usage | RHEL 4/2.6 Kernel/64 bit | 2 x 1.4 GHz IA-64 Itanium 2 | 2GB |
dev12 | General usage | Solaris 8 | 2 x 1.2HGz Sun V210 | 4GB |
dev13 | General usage | Solaris ? | 2 x 1.8GHz Sun V20z | |
dev14 | General usage | RHEL 4/2.6 Kernel/64 bit | 1 x 1.5 GHz IA-64 Itanium 2 | 4 GB |
dev15 | General usage | RHEL 4/2.6 Kernel/64 bit | 1 x 1.5 GHz IA-64 Itanium 2 | 4 GB |
dev16 | SPECJ | RHEL 4/2.6 Kernel/64 bit | 2 x 3.6 GHz Xeon EM64T | 4 GB |
dev17 | SPECJ | RHEL 4/2.6 Kernel/64 bit | 2 x 3.6 GHz Xeon EM64T | 4 GB |
dev18 | General usage | RHEL 4/2.6 Kernel/64 bit | 4 x 1.5 GHz IA-64 Itanium 2 | 4 GB |
dev19 | General usage | RHEL 4/2.6 Kernel/64 bit | 4 x 1.5 GHz IA-64 Itanium 2 | 4 GB |
How should I run the testsuite in the QA Lab?
./build.sh tests -Dnode0=$MYTESTIP_1 -Dnode1=$MYTESTIP_2
I'm getting port conflicts, how do I fix this?
sudo /usr/sbin/lsof -i -P | grep 1099
This should display who has JBoss instances running. If they have not bound to their private IP address, it will conflict even if you are doing so.
How do I get DB admin access?
On dev01, check to see if you are in the oracle_admin group:
$ groups
If you don't see oracle_admin listed, open a JIRA issue in the JBIT project requesting access to this group.
How do I add disk space for Sybase?
Get into the isql prompt as above, and
disk resize name="master", size="200M" go
This section describes the JBoss Project Release procedure.
Tags on JBoss projects should consist of two parts - project identifier and version number. A list of existing modules can be found on the CVS Modules page. The version number must follow JBoss Versioning Conventions . A correctly tagged project would be JBoss_4_0_2, which is the tag for the JBoss Application Server, version 4.0.2. Note that all '.' from the version have been replaced with '_'.
Product versions follow this format X.YY.ZZ.Q* (i.e. 1.2.3.GA, 1.2.3.CR1, 1.2.3.Alpha1-20060205091502)
Major versions are usually developed in multiple iterations. Each iteration concludes with an intermediate version release. Intermediate versions are annotated with appropriate suffixes. This shows the progression of release versions. A given release may not have all stages of releases shown here.
The following version conventions were put in place to have interop with eclipse/OSGI bundle version conventions.
The standard qualifiers are the required prefixes. Additional information may be included in the qualifer as a suffix to incorprate information such as the build id to allow for distinction between nightly builds for example. If a given branch of a project is at 1.2.3.Beta1, the full version used could include a build id based on a GMT timestamp, the actual number of builds, etc. using a full qualifier syntax like Beta1-NNN where NNN is the numeric build id.
The key thing is that all version usage be consistent for a given project. A project cannot include a build id in the nightly builds, and then fail to include a build id of greater value when 1.2.3.Beta1 is actually released. The reason is that 1.2.3.Beta1 cannot be seen to be older than some previous 1.2.3.Beta1-NNN nightly build.
When creating jars as a result of a project's build, do not include the version element in the jar name. An example of that would be the current JBoss Messaging component of the Application Server - jbossmq.jar and not jbossmq-1.1.jar
The standard Java version information and OSGI bundle version headers and their usage needs to be defined. The standard java jar manifest headers should include:
where:
A few tasks need to be completed before a project is handed off for release QA.
Once all items on the Pre-Release Checklist have been completed, the project is ready for release testing.
When a project is ready for a release and the Pre-Release Checklist has been completed, the QA team follows a standard release procedure outlined below.
For more detailed release process on existing JBoss Projects, refer to JBoss QA Wiki page
The Project Management System (JIRA) automatically generates Release Notes for a project. This is covered in Release Notes section of the JIRA chapter
The best way to achieve performance on Serialization, is to use Externalization without using writeObject.
Example 18.1. Externalization Code
public class FirstClass implements Externalizable
{
SecondClass secondClass;
public void writeExternal(ObjectOutputStream out)
{
secondClass.writeExternal(out);
}
public void readExternal(ObjectInputStream inp)
{
secondClass = new SecondClass();
secondClass.readExternal(inp);
}
}
class SecondClass implements Externalizable
{
String str;
public void writeExternal(ObjectOutputStream out) { out.writeUTF(str); }
public void readExternal(ObjectInputStream inp) { str = inp.readUTF(); }
}
This is because writeObject will call a heavy discovery meta data for reflection and serialization's constructors. Dealing directly with the life cycle of objects on externalization routines (calling new) will save you from execution this meta code.
Of course there are situations where you have to use writeObject/readObject, specially if the written object was already described as part of other object (for solving circular references), but most of times when doing writeExternal routines you can guarantee the life cycle of objects.
A rule of thumbs is always define serialVersionUID.
If you don't specify the uniqueID for the object, you can't guarantee version compatibility as minor changes could end up in different serialVersionUID, as they would be calculated according to rules specified on this URL:
http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/class.html#4100
For an externalizable class, writeExternal and readExternal will have to control its version compatibility.
ObjectInputStream encapsulates Streaming in such way that if you try to read more fields from a readExternal method, you would get an EOFException. You could use the exception to determine if the end of a streaming was reached, like in the example
Example 18.3. writeExternal/readExternal among different versions
public void writeExternal(ObjectOutputStream out)
{
out.writeUTF("FirstString");
// code added in a newer version
out.writeUTF("SecondString");
}
public void readExternal(ObjectInputStream inp)
{
String str1 = inp.readUTF();
try
{
String str2 = inp.readUTF();
}
catch (EOFException e)
{
}
}
On the example above if an older version was used to write the object an EOFException would happen and it would be ignored. This would guarantee compatibility between different versions.
Any change made to an Externalizable class will be compatible as long as its read and writeExternal methods are compliant.
Serialization's specification describe lots of scenarios on exchanging information between different class versions:
http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/version.html
Basically there is one simple and basic rules that will summarize the list above
Add fields, don't delete them.
You need to take extra care when adding fields if the same Class is used back and forth different versions. For example a Class that is for communications on both sides.
The following URL lists all the possible situations where a class will and won't be compatible.
http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/version.html
This chapter discusses the process of updating the JBoss Development Process Guide.
The Process Guide is written using DocBook schema. To be able to keep it updated, a basic knowledge of DocBook is assumed. For reference and style manuals check the DocBook website
The Development Guide Project has two modules that need to be checked out separately.
To checkout the guide modules:
cvs -d:ext:username@cvs.jboss.com:/opt/cvs/private/development/management co -r guide
To checkout the build scripts:
cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/jboss export -r HEAD docbook-support
The guide module includes the docBook content of the Development Guide - modules, stylesheets and images and the docbook-support module includes DocBook build scripts and is used to generate the html, single-html and pdf versions of the Development Guide. For any questions on accessing the cvs repository, refer to CVS Access .
Building the modules is done through the guide's build.xml file, which is using targets defined in the docbok-suport module's build.xml . Make sure the following line in guide/build.xml is correct:
<import file="docbook-support/support.xml" />
The current version assumes the docbook module is in a directory called docbook-support inside your guide project folder. By executing the default target you will generate three different formats of the Guide - single-html, html and pdf, located in the build directory of your project. Currently the master.xml file specifies which modules of the guide are to be included in the build. If you add new files to the modules directory, you need to specify them in this file.
<! ENTITY qalab SYSTEM "modules/qalab.xml">
This line declares a module entity later to be added to the build list.