Expert one-on-one J2EE Design and Development phần 2 pptx

70 374 0
Expert one-on-one J2EE Design and Development phần 2 pptx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

o Ant is extensible. It's relatively easy to define custom Ant "tasks" in Java. However, Ant comes with so many tasks to perform common operations - many of them J2EE-related - and so many third-party tasks are available that few developers will need to implement their own Ant tasks. Ant is used widely in commercial and open source projects, so it's essential for any professional Java developer to understand it. Ant can be used for many tasks other than simply building source code. Optional tasks (available as an additional download from the main download site) support building WAR, EJB, and EAR deployment units. I never type in a classpath if there's any likelihood that I will run the command again: I create an Ant build.xml file or add a new task to an existing build file for every Java-oriented command, no matter how small. This not only means that I can immediately get something to work if I return to it later, but also that I can comment anything unusual I needed to do, so I won't waste time in future (I even used Ant to back up the source code and documents composing this book). If you aren't familiar with it, learn and use Ant. Continue to use your favorite IDE, but ensure that each project action can be accomplished through an Ant target. Spend a little time upfront to write Ant build files and reap the rewards later. See http://jakarta.apache.org/ant/ant/ant_anger.html for guidelines on using Ant effectively. Code Generators There's little need to auto-generate code for ordinary Java objects and web-tier classes. However, the many artifacts required in EJB development made code generation tools attractive, especially where entity beans are concerned. EJB code generators are lower tech compared to IDEs, but can be very effective for EJB development. As discussed, it's impossible to produce and maintain all the required deployment descriptors (both standard and vendor-specific) manually if we are using CMP entity beans. The following free tools use special Javadoc tags in EJB bean implementation classes to drive generation of other Java required source files (home and component interfaces) and deployment descriptors for several servers. Unlike an IDE "EJB Wizard", this is a scriptable approach and is compatible with any IDE or editor. o EJBGen http://www.beust.com/cedric/ejbgen/ This tool, written by BEA developer Cedric Beust, is bundled with WebLogic 7.0. o XDoclet http://xdoclet.sourceforge.net/ This similar, but more ambitious, tool, written by Rickard Oberg, is available in open source, and can be used to perform other tasks as well as EJB generation. An alternative EJB code generation approach is to define the necessary data in an XML document, enabling use of XSLT to generate the multiple output files required. Again, this is really only necessary for handling the complexity of entity bean CMP. One of several such products is the LowRoad code generator from Tall Software (http://www.tallsoftware.com/lowroad/index.html). 66 Brought to you by ownSky J2EE Projects: Choices and Risks Version Control It’s vital to have a good version control tool; along with a good build tool such as Ant, a version control tem is the cornerstone of every successful release-management strategy. CVS is widely used in the open source community and provides a reliable basic level of functionality. Several simple, free GUIs integrate with CVS (the best I've seen is WinCvs, available from WWW.wincvs.org, although there are also some platform-independent Java GUI clients). Popular IDEs like Forte and Eclipse also provide TVS integration. Any professional organization should already have a version control system in place before undertaking a complex development project such as aJ2EE enterprise solution. Identifying and Mitigating Risks I2EE is a relatively new technology. Enterprise applications involve a mix of technologies such asJ2EE, RDBMS, and mainframes, making interoperability a challenge. For these and other reasons it's vital to tackle risks early. When Java was less mature, I once worked on a project for a software house that developed mainframe software. My role was to lead the development of a Java web interface for a key mainframe product. As the project unfolded, I was impressed by the professionalism of the mainframe developers. They were an established team and were experts in their technologies, which they'd been working in for many years. It was clear that they assumed that "things always worked as documented". The project involved Swing applets and needed to run in both, IE and Netscape. We encountered serious rendering problems, and it took days to find workarounds for some of the more serious problems. Initially, my comments such as "this is known not to work in IE" inspired disbelief. Then I remembered my first experience as a C programmer, and the shock of working with early C++ implementations. C worked. If something didn't work, it was the programmer's fault. Yet, early C++ implementations (not compilers, but C++ to C translators) would occasionally produce semantic nonsense from correct statements. Java has come a long way since then. However, the early years of J2EE brought up many problems that seemed to be ignored by J2EE writers. For example, class loading in web applications had severe problems that required drastic workarounds in several leading products as recently as early 2001. Most books and articles I've read on J2EE paint too rosy a picture of J2EE development. They fail to convey the pain and suffering that many developers go through. It's important to note that such problems don't afflict just J2EE technology. Having worked in Microsoft shops, I've encountered many irritations and "known issues" with their web technology (Microsoft products no longer have "bugs"). In the last two years, things have improved enormously for J2EE, but there's still some way to go. Discussing the bugs in particular products isn't helpful, since such a discussion might well be out of date before this book is on the shelves. However, it is important to acknowledge the fact that there probably will be problems and that getting around them will soak up some development time. J2EE specifications are complex and implementations fairly new. Problems may arise from: o Server bugs (in deployment and administration as well as run-time behavior) o Areas in which the J2EE specifications are sketchy (class loading is a rich source of such problems, discussed below) 67 Brought to you by ownSky o Poorly understood areas of the specification o Interaction with other enterprise software applications In the worst case, such problems may demand a design workaround. For example, the decision to use EJB 2.0 entity beans with CMP may bring to light that the chosen application server's EJB QL implementation, cannot cope with the complexity of some of the queries or that EJB QL itself cannot meet the requirements efficiently. These risks can be headed off by a proof of concept early in the development project, which can prompt a decision to avoid EJB QL or choose a different application server. In less serious cases, such as a problem with a server administration tool, they might involve a server bug that slows down the development process. Successful risk management depends on early identification of risks, enabling action to be taken before resources have been heavily committed to a given approach. The following general principles are valuable in J2EE project risk management: o Attack risks as early as possible. This is one of the key points of the Unified Software Development Process. We can adopt this approach without adopting the entire methodology. o Ensure that the design is flexible. For example, if we design our application so that we can replace CMP entity beans with another persistence strategy without rewriting large amounts of business logic, problems with EJB QL would have a less severe impact. o Allow contingency time in the project plan to handle unexpected problems. o Involve more developers when a problem becomes apparent. This promotes lateral thinking, at the cost of a greater total number of developer days. o Develop a good relationship with your application server vendor. Once you are sure there is a problem, report it. A fix may be on the way. Other users may also have encountered it, and the vendor may be able to suggest a good workaround even if no fix is available. o Learn to distinguish things that are your fault from things that aren't. It's hard to overestimate the importance of this point, which is one of the many reasons why any significant project needs at least one true J2EE expert. Erring either way can dramatically increase the time required to track down a problem. o Use the Internet. There is a vast wealth of knowledge online about things that do and don't work. Regular search engines like Yahoo! and Google can uncover it. Benefit from it. No matter how obscure your problem may be, there's a good chance that someone has reported something similar in a newsgroup somewhere. The following is a list of some of the significant risks encountered in J2EE projects, along with appropriate risk mitigation strategies for each. While we haven't yet discussed the concepts behind some of these problems, they should provide useful practical illustrations of risk management: Risk Mitigation strategies Your development team lacks J2EE Purchase J2EE consulting services to kick-start the project, skills, threatening to result in poor choices early in the project lifecycle Hire a strong J2EE expert on a long-term basis to and making it impossible to predict contribute to the project and mentor other developers. project timescales. Send key developers on training courses. 68 Brought to you by ownSky J2EE Projects: Choices and Risks Ri sk Mitigation strategies Your application is dependent on a proprietary feature of your application server. Your application server may no longer be supported, forcing migration to another server. Your application server may not meet your scalability or reliability requirements. Your application may not meet your performance or scalability goals. Your application may fail to scale as required, because while it works correctly on a single server, it exhibits incorrect behavior in a cluster. A server bug makes a J2EE feature that your application requires unworkable. If the feature fills a gap in the J2EE specifications, it's likely that other application servers will offer a similar feature, accessed through a different API. So isolate the proprietary functionality behind a platform-independent abstraction layer, ensuring that you only need to reimplement one or more interfaces to target a different server. Use an abstraction layer, as described above, to insulate your application from proprietary features of the server. Consider the viability of the server vendor when selecting a server, and regularly review the market. Regularly check the compliance of your application to the J2EE specifications as described above. Enlist the help of the server vendor in building a simple proof of concept that can be load tested, before it is too late to switch to a different server. Build a "vertical slice" of the application early in the development lifecycle to test its performance. If clustering is a possibility, consider the implication of session management and session replication in all design decisions. Test your application in a clustered environment long before it is released in a clustered environment. Seek assistance from your server vendor; they (and their documentation) will provide crucial information about their clustering support, which you'll need to understand to achieve good results. Implement a vertical slice of your application as early as possible to check the implementation of crucial technologies. Report the problem to the server vendor and hope for assistance or a patch. Modify application design to avoid the problem technology. Switch to a superior application server while it is still possible. Table continued on following page 69 Brought to you by ownSky Risk Mitigation strategies Your application requires third-party libraries (such as a particular XML library, for example) which may conflict with libraries shipped with your application server. An integrated J2EE application using EJBs and web modules encounters class loading issues that reduce productivity. When the same class is loaded by two class loaders the two copies are considered to be different classes if compared; ClassNotFoundExceptions may be encountered when one class depends on other classes that have been loaded by a classloader not visible to its classloader. This may happen, for example, when a class used in a web application but actually loaded by the EJB classloader attempts to load classes loaded by the WAR classloader, which the EJB class loader cannot see in most servers. Class loading is a complex area discussed in more detail in Chapter 14. Application deployment causes unnecessary downtime. This risk must be addressed as early as possible through the implementation of a vertical slice. Seek guidance from the server vendor (and their documentation) in ensuring compatibility (for example, it may be possible to configure class loading to avoid the conflict). Understand the Java class loading hierarchy (documented in the Java. lang.ClassLoader Javadoc) and the class loading architecture of your target application server. Unfortunately, class loading strategies vary between servers, meaning that this is an area in which portability falls down. Take care in packaging deployment units to ensure that classes are loaded by the correct classloader (WAR or EJB classloader, for example). This requires careful development of build scripts to ensure that classes are included in the correct deployment unit, rather than in all deployment units. Consider especially carefully which classloader loads classes that load other classes by name. Code to interfaces, not concrete classes. This makes it easier to keep groups of implementation classes within the appropriate class loader. Implement a vertical slice as early as possible to verify that class loading poses no risk. In the event of intractable problems, consider whether the use of EJB is really necessary. Class loading issues are much simpler in web applications. As a last resort, consider adding classes required throughout your application to the server's global classpath. This violates the J2EE specification, but can save a lot of time. Master the deployment process on your chosen application server. Develop a release management strategy that meets your needs. 70 Brought to you by ownSky J2EE Projects: Choices and Risks In this chapter we've considered some of the most important choices to be made in J2EE development projects other than the architectural decisions we considered in Chapter 1. We've looked at: Summar o How to choose an application server. One of the strengths of the J2EE platform is that it allows a choice of competing implementations of theJ2EE specifications, each with different strengths and weaknesses. Choosing the appropriate application server will have an important influence on a project's outcome. We've looked at some of the major criteria in choosing an application server, stressing the importance of considering the specific requirements, rather than marketing hype. We've seen the importance of choosing an application server early in the project lifecycle, to avoid wasting resources getting up to speed with multiple servers. We've considered the issue of total cost of ownership, of which license costs are just a part. o Managing the technology mix in an enterprise. While an unnecessary proliferation of different technologies will make maintenance more expensive forever, it's important to recognize thatJ2EE isn't the best solution to all problems in enterprise software development. We should be prepared to use other technologies to supplement J2EE technologies where they simplify implementation. o Practical issues surroundingJ2EE portability. We've seen how to ensure that we don't unintentionally violate the J2EE specifications, by regularly running the verification tool supplied with Sun's J2EE Reference Implementation, and how to ensure that application design remains portable even if we have good reason to use proprietary features of the target platform. o Release management practices. We've seen the importance of having distinct Development, Test, and Production environments, and the importance of having a well-thought-of release management strategy. o Issues in building and managing a team for aJ2EE project. We've considered the implications of using a "Chief Architect," as opposed to a more democratic approach to architecture, and considered two common team structures: the "vertical" structure, which uses generalists to implement whole use cases, and the "horizontal" structure, which focuses developers on individual areas of expertise. We've considered a possible division of roles in the "horizontal" team structure. o Development tools. We've briefly surveyed the types of tools available to J2EE developers. We've stressed the importance of the Ant build tool, which is now a de facto standard for Java development. o Risk management. We've seen that successful risk management is based on identifying and attacking risks early in the project lifecycle. We've discussed some overall risk management strategies, and looked at several practical risks to J2EE projects, along with strategies to manage them. As this is a practically focused book, I haven't discussed choosing a development methodology, or deciding when one is required. However, this is another important choice. We've seen the importance of tackling risks early. I recommend using a methodology forJ2EE development that emphasized this. Both the Rational Unified Process and Extreme Programming (XP) meet this requirement. Personally, I prefer "lightweight" or "agile" methodologies (see http://agilealliance.org/principles.html), although the degree of formality appropriate tends to increase the larger the project. I recommend the following resources as starting points for readers unfamiliar with these methodologies: The Unified Software Development Process from Addison-Wesley (ISBN: 0-201-57169-2J, and http://www.extremeprogramming.org ("Extreme Programming: A Gentle Introduction"). In the next chapter we look at testing J2EE applications. Testing is an important concern throughout the software development lifecycle and is accorded particular importance in both these methodologies. 71 Brought to you by ownSky Testing J2EE Applications In Chapter 2 we saw that decisions made early in the project lifecycle can determine a project's success or failure. Testing is another critical area in which we must develop a strategy and establish good practices from the outset of a project. Testing is often treated as an uninteresting activity that can be undertaken after development is largely complete. No one seriously believes that this is a good approach, but it's the usual outcome when there's no coherent testing strategy from project commencement. Most developers are aware of the many problems such reluctant testing brings, such as the fact that the cost of rectifying bugs escalates rapidly, the longer they take to emerge. In this chapter we consider a positive approach to testing. We'll see that testing is something we should do, not just out of fear of the consequences of not doing it, but because it can be used to improve the way we develop code. If we view testing as an integral part of our development process, we can not only raise the quality of our applications and make them much easier to maintain, but also increase productivity. Testing should occur throughout the development lifecycle. Testing should never be an afterthought. Integrating testing into the development process brings many benefits. Testing enterprise applications poses many challenges: o Enterprise applications usually depend on resources such as databases, which will need to be considered in any testing strategy. o Testing web applications can be difficult. They don't expose simple Java interfaces that we can test, and unit testing is complicated by the dependence of web tier components on a web container. 73 Brought to you by ownSky o Testing distributed applications is difficult. It may require numerous machines and may be hard to simulate some causes of failure. o J2EE components - especially EJBs - are heavily dependent on server infrastructure. o A J2EE application may involve many architectural layers. We must test that each layer works correctly, as well as perform acceptance testing of the application as a whole. In this chapter, we discuss these challenges and approaches to meet them. We'll look at: o Testing goals and concepts. o The Extreme Programming (XP) approach to testing, which is based on test-first development. XP elevates testing into the centerpiece of the development process. Tests are regarded as essential application deliverables. Tests are written before code and always kept up to date. Whether or not we consider adopting XP overall, this is a very effective approach. While all good programmers test their code often, there are real advantages from proceeding from an ad hoc approach to a more formal approach, in which tests are documented and easily repeatable. o The JUnit testing framework, which provides a good basis for our testing strategy. JUnit is a simple but highly effective tool, which is very easy to learn, and which enables tests to be written with a minimum of hassle. o The Cactus J2EE testing framework, which builds on JUnit to enable J2EE components such as EJBs to be tested within an application server. o Techniques for testing web interfaces. o The importance of automating tests, so that all tests for an application can be run in a single operation. We'll see how the Ant build tool can be used to automate JUnit tests. o Complementary approaches to testing, such as assertions, which we can use as part of an integrated QA strategy. What Can Testing Achieve? It's impossible for testing to guarantee that a program is correct. However, testing can provide a high level of confidence that a program does what we expect of it. Often "bugs" reflect ignorance about what code should really do. As our knowledge of what a program should do grows, we can write tests that tighten its requirements It is important to recognize the limitations of testing - testing won't always expose concurrency issues. Here, an ounce of prevention is truly worth a pound of cure (for example, testing may well fail to pick up problems relating to instance data in a servlet being modified concurrently by multiple threads). However, such code will surely fail in production, and no competent J2EE developer should write it in the first place. The longer a bug takes to appear, the more costly it will be. One study found that the cost of eventually fixing a bug multiplied by 10 with each phase of a project - requirements, design, implementation, and post-release - that passed before the bug was spotted. Testing is no substitute for careful thought before writing code; testing can never catch all bugs. 74 Brought to you by ownSky Testing J2EE Applications While in this chapter we'll focus on testing code, it's important to remember that a sound QA strategy is needed from requirements analysis onwards. Definitions Let's briefly define some of the concepts we'll discuss in this chapter: o Unit tests Unit tests test a single unit of functionality. In Java, this is often a single class. Unit tests are the finest level of granularity in testing, and should test that each method in a class satisfies its documented contract. o Test coverage This refers to the proportion of application code that is tested (usually, by unit tests). For example, we might aim to check that every line of code is executed by at least one test, or that every logical branch in the code is tested. o Black-box testing This considers only the public interfaces of classes under test. It is not based on knowledge of implementation details. o White-box testing Testing that is aware of the internals of classes under test. In a Java context, white-box testing considers private and protected data and methods. It doesn't merely test whether the class does what is required of it; it also tests how it does it. I don't advocate white-box testing (more of this later). White-box testing is sometimes called "glass-box testing". o Regression tests These establish that, following changes or additions, code still does what it did before. Given adequate coverage, unit tests can serve as regression tests. o Boundary-value tests These test unusual or extreme situations that code under test should be able to handle (for example, unexpected null arguments to a method). o Acceptance tests (sometimes called Functional tests) These are tests from a customer's viewpoint. An acceptance test is concerned with how the application meets business requirements. While unit tests test how each part of an application does its job, acceptance tests ignore the implementation details and test the ultimate functionality, using concepts that make sense to a user (or customer, in XP terminology). o Load tests These test an application's behavior as load increases (for example, to simulate a greater population of users). The aim of load testing is to prove that the application can cope with the load it is expected to encounter in production and to establish the maximum load it can support. Load tests will often be run over long periods of time, to test stability. Load testing may uncover concurrency issues. Throughput targets are an important part of an application's non-functional requirements and should be defined as part of business requirements. 75 Brought to you by ownSky o Stress tests These go beyond load testing to increase load on the application beyond the projected limits. The aim is not to simulate expected load, but to cause the application to fail or exhibit unacceptable response times, thus demonstrating its weak links from the point of view of throughput and stability. This can suggest improvements in design or code and establish whether overloading the application can lead to erroneous behavior such as loss of data or crashing. Testing Correctness Let's now examine some issues and techniques around testing the correctness of applications: that is, testing that applications meet their functional requirements. The XP Approach to Testing In Chapter 2 I mentioned Extreme Programming (XP), a methodology that emphasizes frequent integration and comprehensive unit testing. The key rules and practices of XP that relate to testing are: o Write tests before code o All code must have unit tests, which can be run automatically in a single operation o When a bug is reported, tests are created to reproduce it before an attempt is made to fix the bug The pioneers of XP didn't invent test-first development. However, they have popularized it and associated it with XP in common understanding. Among other methodologies, the Unified Software Development Process also emphasizes testing throughout the project lifecycle. We don't need to adopt XP as a whole in order to benefit from these ideas. Let's look at their benefits and implications. Writing test cases before writing code - test-first development - has many benefits: o The test cases amount to a specification and provide additional documentation. A working specification, compliance to which can be checked daily or even more often, is much more valuable than a specification in a thick requirements document that no one reads or updates. o It promotes understanding of the requirements. It will uncover, and force the resolution of, uncertainty about the class or component's functionality before any time has been wasted. Other components will never be affected by forced reworking. It's impossible to write a test case without understanding what a component should do; it is possible to waste a lot of coding time on the component itself before the lack of understanding becomes apparent. A common example concerns null arguments to methods. It's easy to write a method without considering this possibility, with the result that a call with null arguments can produce unexpected results. A proper test suite will include test cases with null arguments, ensuring that the method is only written after the behavior on null arguments is determined and documented, j o Test cases are more likely to be viewed as vital, and updated throughout the project lifecycle. o It's much more difficult to write tests for existing code than to write tests before and while writing code. Developers implementing application code should have complete knowledge of what it should do (and therefore how to test it); tests written afterwards will always play catch-up. Thus test-first development is one of the best ways to maximize test coverage. 76 Brought to you by ownSky [...]... improve error messages and handling If a test fails, and it wasn't immediately obvious what went wrong, try first to make the problem obvious (through improved error handling and messages) and then to fix it 77 Brought to you by ownSky All these rules move much of the responsibility of testing onto the development team In a traditional large organization approach to software development, a specialized... that should render the response, and provide model data it should display The ModelAndView object returned by the above method contains both view name and model data This decoupling of controller and view is not only good design practice, but greatly simplifies unit testing We can ignore markup generation and simply test that the controller selects the correct view and exposes the necessary model data... always best at j writing test cases (although they can learn) However, the distinction between development and technical testing is artificial On the other hand, acceptance testing is likely to be conducted at least partly outside the development team There shouldn't be an artificial division between development and testing roles Developers should be encouraged to value the writing of good test cases as... client and server, JUnitEE provides a senlet that allows test cases to be chosen and output generated on the server It's very easy to implement tests using JUnitEE, because test cases are simply JUnit test cases All the JUnitEE infrastructure does is to provide a J2EE- aware means of running the test cases Test cases will simply be implemented with the knowledge that they will run within the J2EE server... functionality covered by previous tests, and a measure of confidence that that bug won't reappear Write unit tests before writing code, and update them throughout the project lifecycle Bug reports and new functionality should first prompt the writing and execution of failing tests demonstrating the mismatch between what the application does and what it should do Test-first development is the best way to guarantee... Code? Testing is such an important part of the development process that it is legitimate for the testing strategy we use to affect how we write application code - with certain reservations 90 Brought to you by ownSky Testing J2EE Applications First, the reservations: I don't favor white-box testing and don't advocate increasing the visibility of methods and variables to facilitate testing The "parallel"... do, and will make it much easier to provide different implementations if necessary Integration and Acceptance Testing Acceptance testing is testing from a customer perspective Inevitably this will involve some hands-on testing, in which testers play the role of users, and execute test scenarios However, we can also automate aspects of acceptance testing Integration testing is slightly lower level, and. .. EJBs and test them like ordinary Java classes EJBs are managed objects; the EJB container manages their lifecycle at run time and they depend on container services such as connection pools Furthermore, the container controls access to their functionality, and the behavior added by container interception (such as transaction management and security restrictions) is part of the application itself and. .. that unit testing is important How should we go about it in J2EE projects? 78 Brought to you by ownSky Testing J2EE Applications main() Methods The traditional approach to unit testing in Java is to write a main () method in each class to be tested However, this unnecessarily adds to the length of source files, bloats compiled byte codes and often introduces unnecessary dependencies on other classes,... objects, will certainly interact (although not necessary directly) with a database, and will depend on J2EE data sources Hence we'll have to consider the effect of our tests on data in the database and the data they require There are several strategies here The most radical is to do away with the database at test time and replace actual JDBC classes with mock objects (see http://www.mockobjects.com/papers/jdbc_testfirst.html . products as recently as early 20 01. Most books and articles I've read on J2EE paint too rosy a picture of J2EE development. They fail to convey the pain and suffering that many developers. Your development team lacks J2EE Purchase J2EE consulting services to kick-start the project, skills, threatening to result in poor choices early in the project lifecycle Hire a strong J2EE expert. problems and that getting around them will soak up some development time. J2EE specifications are complex and implementations fairly new. Problems may arise from: o Server bugs (in deployment and

Ngày đăng: 13/08/2014, 12:21

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan