Java 2 Bible Enterprise Edition phần 10 docx

66 307 0
Java 2 Bible Enterprise Edition phần 10 docx

Đ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

Remember that although J2EE is a great collection of functionalities, it won't solve every problem in the world; quite frequently you will need to use proprietary systems. One of the more common you are likely to come across is BEA's Tuxedo application−server software. Tuxedo is a precursor to the J2EE systems and has many things in common with them. Deploying the system With the application completed, you need to start deploying it into the real world. Following the line of thought we have been presenting so far, the hardest way for you to deploy an application is to rock up right at the end of the project and dump it into the customer's lap. No matter how heavily you test the application in your development, the real world will always produce more problems. Why not try to get bits of the application onto the customer's site as early as possible? In this way, you can fight the small bugs right at the front before they compound into impossible−to−deal−with monsters hidden in a huge collection of code. Each time you add a new piece to the customer's version of the code, new bugs can only come from that small area. Tracking them down should be much more manageable with a small chunk of code than with one monolithic lump. Dividing up the Workload By its very nature, J2EE encourages you to build highly modular systems. Of course, the word "enterprise" in its name also suggests that the main focus is on large application areas. When you put these two thoughts — modularity and large systems (read: scalability) — together, it should become immediately apparent that you are going to need more than one box to put all this code on. When you have to deal with the multiple computers containing your code, you must consider many other issues. Do you have many small computers serving up your business functionality or a couple of big ones? If you have many small computers, how do you divide the code among them? These are all issues that you may have to deal with as a J2EE programmer and architect. Assigning work to the various tiers of your application One of the eternal dilemmas of enterprise programmers is deciding where to place various parts of the application's functionality. Some code works equally well as a database trigger, as a middleware object, or on the client. Where should it all go? Taking stock of your code During your analysis of the requirements, you've worked out what the customer wants to see on the desktop (Web browser or standalone application), what sort of functionality he needs (business logic), and the data that must be stored (structures and relationships). During this process, the customer has probably indicated the type of computing systems that all of this is to run on. That is, the customer will tell you that he has a couple of big Sun/HP/IBM boxes that he doesn't want to throw away just yet, and it would be really good if you could make use of them. Wink, wink, nudge, nudge, say no more! Now that you have to apply your code to that hardware, how should you divide up the functionality? Well, the easiest way to approach the situation is to look at exactly what you have to do. Some tasks are much more suited to a particular tier of the application. In order to refresh your memory, Figure 24−1 represents a Chapter 24: System Architecture Issues 631 "typical" enterprise application and the tiers that you might encounter. Figure 24−1: The layout of the tiers of a typical enterprise application Client versus Web server versus middleware versus database Now let's look at how you might divide your application among these various tiers. Please note that these should only be considered rules of thumb, not hard and fast rules. We've derived them from years of experience in developing all sorts of applications, and so we feel that they should aid you in building a well−balanced application right from the start. Client code should present the user interface. No application logic should exist here. What client code should concentrate on is providing basic input verification (did the user type a floating point number where only an integer is allowed?). The main job of the client is to make sure that navigation among the various screens presented to the user is consistent. • Web servers, if present, should provide verification services and translate between the user's data and interface with the application logic presented by the middleware. For example, secure connections retrieve credit−card data, verify that the credit−card number is roughly valid (this can be done without needing to contact an outside provider) and then pass the data on to the bank and merchant. Basically, this server is a formatter — it takes the low−level data and formats it in a way suitable for the client to use (HTML, XML, raw binary and so on). • Middleware is where all the good work is done. Here is where you find all the business logic (can I add this part to that product?). Because you need the logic to act on data, it will also act as the abstraction of the data you're applying the logic to. • Databases store data and retrieve it on request. Ideally, you keep all logic out of them (no stored procedures, for example), as you might want to change the logic depending on the viewer (which is the role of the middleware). All databases should be concerned about is maintaining the integrity of the data. • In earlier iterations of the "enterprise" application, the middleware tier was usually either missing or combined with the database. Most of the time, data correctness and business logic were built around stored procedures in the database. The movement now is to remove all the logic from the database wherever possible. The idea is that you can customize the business logic for a particular system user more easily that way than you would if the logic were embedded in the database. This new way of thinking is typically referred to as the separation of presentation and data layers. When one is not enough When you have more than one computer in the system, keeping them all in harmony becomes an issue. The system shown in Figure 24−1 is not a typical realistic system. Sure it shows the basic arrangements, but it is Chapter 24: System Architecture Issues 632 very rare to have only one computer at each tier. Figure 24−2 presents a more realistic view of the potential situation, in which you can expect to find multiple computers at each level. Figure 24−2: When you look at real systems, multiple computers exist at each tier. You might be wondering just how to keep everything working. The answer lies in a number of different, but similar, terms. The typical approach is called load balancing or distributed server management. In reality you, as a programmer, should not need to know about the complex arrangements that typically come with these setups. That should be the job of the system administrator. However, as we mentioned a number of times in the EJB chapters, these systems also influence the way you need to design your code, and so you should at least be aware of the basic principles. What is load balancing? Depending on who you talk to, the term load balancing can have many different meanings. In real terms, what we are discussing is how to manage a number of servers at a single−tier. These servers should provide the "same" service regardless of which machine does the actual code−crunching. (We'll explain why "same" is in quotes in the next section.) On the simplest level, load balancing is about making sure each physical box is running at the same amount of load — CPU and memory usage, I/O to the database, services handled, and so on. So when it comes to that server farm for Web servers, each Web server will contain exactly the same information. Regardless of which server your request gets handed off to, you should always see the same result, and everyone making requests should have the same response time. Load balancing solutions vary in complexity. The following list is a simplistic summary of how these different types are implemented, starting from the simplest: Round−robin DNS — This type uses the simple query of turning a domain name into an IP address into a simplistic load balancer. For any given address, such as http://www.hungryminds.com/, the DNS server will have a pool of IP addresses. Each time a request is made to turn the domain name into the IP address, the next number from the pool is given out. • External balancer — In this model, a single machine acts as the input and then funnels the requests to the server machines to do the real processing. The input handler uses feedback information from the servers, typically by serial cable, to decide where to send the next request. This method provides a much more even balance: If one machine gets hung up processing an overly long request, more requests do not queue up for it in the meantime. • Software managed — The software itself maintains a watch over the load being used on each system. If one machine finds itself overloaded, it will forward the request onto another, more lightly loaded machine. This software is above the operating system — typically only in the J2EE environment (or its equivalent, for other systems like CORBA) does this work. • Chapter 24: System Architecture Issues 633 The J2EE approach For the environment, you are mostly dealing with — J2EE — the typical approach is for the J2EE environment implementer to take care of all the load balancing issues for you. There are many good reasons for this, mostly having to do with the EJB specification. When you are managing an EJB, the server is responsible for the bean's lifecycle management. It may create and destroy individual bean instances as needed. Naturally, if many machines exist at the same tier, the server has many choices in terms of where to create that bean. It would be a waste if every machine had a new bean instance created when you only need one extra instance. Because the J2EE server has to create just that one extra instance, it must choose which machine to put it on. The result is a load−management system, because the server will choose to put the new bean instance on the machine with the lightest load. Deciding on the appropriate machine to send the next request to or start the new bean instance on is something that is not specified by J2EE. However, most vendors offer similar approaches. You will typically see two features: partitioning of services to specific machines and partitioning services on one machine. In real life, it is unlikely that every bean you write will do exactly the same amount of work. One entity bean might do very little while a session bean is doing hundreds of calculations. Obviously, in this situation you might want to allocate more resources to the hard−working bean so that the overall service remains balanced. Here the J2EE environment enables you to partition the workload by machine. Although all machines are kept at the same tier, they will be grouped according to the beans deployed on them, as shown in Figure 24−3. Figure 24−3: Grouping several servers within a single tier enables better load management when your system is providing many different services. Choosing a Design With so many different options to choose from, making the right choice of APIs for your project can sometimes feel like a bit of a lottery. After all, you've just finished a few hundred pages of information detailing lots of different options. The head can be quite muddled after all this if you don't have much experience. So this section is about putting together a quick summary of the pros and cons of the major decision areas. Chapter 24: System Architecture Issues 634 Large−scale technology options As you go through the design process, you need to keep in mind the technology options that are available. Often the technology requirements of the system you are incorporating your project with will drive the overall design and architecture. That is, if you have to integrate with an existing CORBA system then that is the Java technology that is going to be the centerpiece of your application's architecture. Before visiting some architecture options, let's firstly recap on the various technology options that we have presented to you in this book. The middleware options In the middleware tier, you have four basic options: RMI, EJB, CORBA, and proprietary code. Other options, such as JINI, also exist, but these do not seem to be in widespread use, so we'll ignore them for the purposes of this summary. RMI — The original Java option for providing remote−object capabilities. Although it forms the basis of the EJB specification, RMI's capabilities are relatively simplistic. You can provide a remote−object instance to clients, but no scalability exists in the system: One instance means one instance. If you have 100 or more clients, that single instance must serve all of them. On the other hand, RMI has a number of useful features, such as the ability to register remote listeners to an object and a distributed garbage−collection system to maintain that Java−centric view of the world. • EJB — The technology with the most hype. Built to be a better CORBA, EJB has many of its useful features, but restricts you to a Java−only design solution. This is great if you have a new system that you can write entirely in Java, but not particularly useful if you have to interface mainly with a lot of pre−existing code. Designed to provide small functional components that are glued together to service a single application or applications. • CORBA — A language−neutral set of technologies that provide remote−object capabilities. CORBA is the original attempt at building a large−scale, distributed computing framework. It hasn't been spectacularly successful (at least in marketing−hype terms), but CORBA is a solid system that offers a far wider variety of services than its pure−Java relatives. The difference lies in the fact that CORBA seems to be mainly a service−abstraction system, whereas EJBs try to concentrate on providing data abstractions. That is, a CORBA interface will provide a single "class" with a lot of methods for doing one thing — displaying or analyzing map data, for example — while EJBs will provide a class that does nothing but represent a single customer within a huge database. • Proprietary — Before the network and open standards was proprietary code. That is, the programmers wrote their own interfaces over the top of some very low−level connections — IP sockets or UNIX−domain sockets. This choice is best when you want a tightly bound system that offers the highest performance. The trade−off is longer development time, as you must write all the basic systems — such as load balancing, transaction support, and high−level abstractions — yourself. • The client−access options You also have a range of options when presenting data to the end user. Sometimes you have no choice over the presentation mechanism — when the client must be a Web browser, for example. When you do, there are a variety of ways for you to take the data presented by the middleware tier and format it into something that the client might use: servlets, JSPs, JMS, and Web services such as SOAP and XML−RPC all can be used for this task. Note For the purposes of this summary, we are ignoring situations in which you have client applications directly interacting with the middleware — in the case of a POS terminal, for example. Chapter 24: System Architecture Issues 635 JSP — This option is best when the output must be HTML, as it doesn't give you much of an ability to present any other form of data. JSPs enable you to customize the output somewhat by interacting with Java code, which can then communicate with middleware systems. JSP is great for building simple interactive sites, such as the front end to an online shop, but it falls down when you're trying to build heavily customized interactions that completely change the output each time a service is called. • Servlet — The latest incarnation of the method of adding dynamic code to a Web server. The original systems started with CGI calls and native code, followed by Perl and ASP. Servlets take the best parts of these systems (such as the module−extension mechanism available in the Apache Web server to provide mod_perl), and put a Java spin on them. They are capable of providing output in any format, although the most common interaction that they process is to deal with HTTP requests and replies. Servlets are the most flexible of the options. • Web services — Many different technologies can be classified as "Web services," which is the latest marketing spin on the ability to provide remote procedure–call capabilities without requiring the programmer to build a complete middleware system. Web services typically use HTTP as the basic communication mechanism and then build another layer in which the body of the message is encoded in XML structures. Sometimes servlets are used as the implementation of the server side of Web−services systems. Remote procedure–call systems have existed for decades — the most notable being the RPC system on UNIX machines. Web services are just the latest fad associated with this very old idea. Two technologies to watch for: SOAP and XML−RPC. • JMS — When dealing with large, pre−existing enterprise systems, you will almost certainly come across many interesting challenges. The most prominent of these will be the fact that, owing to the mainframe legacy, the communications mechanisms will not be designed around real−time, interactive capabilities. That is, you send the request off, and at some later time, maybe after several minutes or more, you might get an answer back; even if you do it won't go directly to the "user" that sent the request in the first place. What these systems lack in interactive capabilities they make up for in robustness. Those messages are stored and processed. If the receiver goes down, not to worry: When it comes back up again, the messages will still be waiting to be processed. From the Java perspective, your view into this world, as either a sender or receiver, is through JMS. • Design options After you've formulated a rough idea about the technologies that will be needed to implement your application, you need to come up with an architecture. This architecture needs to accommodate both the hardware and the software you will use in your application: How many tiers should there be, what sort of scalability is required, and just how many computers are needed to get it all done? All of these questions and more need to be answered as part of your architecture — and you haven't even started on the software part yet! Deciding on a software design is a personal decision based on your years of experience as a programmer. You know what works and what doesn't work. Moving to designing products using the J2EE environment is not a gigantic leap. A J2EE design process should take the same approach as designing any other application: Understand your basic technology building blocks and then apply standard design principles to come up with the overall design and architecture. Just because you are now designing an enterprise application rather than a desktop or an applet, you should not forget everything that you've learned in the past. Modeling tools should be used to create Use Cases and UML designs. Your architecture should be influenced by standard designs such as using design patterns. Of course, some design patterns work better in the enterprise environment than others. Basically, just keep calm and do what you have always done. Cross−Reference Chapter 24: System Architecture Issues 636 Various options for design patterns in the J2EE environment are covered in depth in Chapter 25. Implementing Security Lastly we cover the most important topic of all in enterprise applications: security. There's no point in having the world's best business model if you don't have any customers because they've all been scared off by someone breaking into your system. Providing a secure system means thinking about the consequences right from the beginning. Simply tacking on some security at the end will guarantee that holes are waiting to be exploited. Don't ever be fooled into thinking that you don't need to worry about security because everyone can be trusted. They can't. Although you know who is using the system now, what about in a year's time? What if a user decides to turn vindictive toward a colleague? While you can't protect against every circumstance, building levels of access into the system and taking simple security measures will eliminate all but the most dedicated attackers. While the intricacies of designing and implementing proper securing your application are something that only you will know about, the following is a short cookbook dealing with areas you should pay attention to. In short, these are minimal steps that must be undertaken for every enterprise application. Securing the connections The typical first method of attack is sniffing the network traffic. Brain−dead protocols like POP3, IMAP, and SMB allow any user on the network to watch user names and passwords go past without the watcher ever needing to directly attack a system. Once the attacker has a user name and password, your application is history, as this can be used as a beachhead for more sophisticated attacks. At a minimum, all external connections should use a secure connection mechanism whenever sensitive information is being sent. For example, if a password or access key is required to access your system, you must use encryption on the connection to pass the data. For Web−based data, use HTTPS rather than HTTP connections. If your application uses sockets, make use of JSSE, the secure−sockets extension for Java. It will provide you with SSL−based connections for secure communications. You should use secure connections wherever your application must interact with external applications — such as through an extranet to other suppliers or customers. While it is pointless to secure your basic homepage, shopping−cart checkouts definitely require security. Similarly, within your middleware network, using secure connections so the beans can communicate from the server to the client is not necessary, but using secure connections for messages sent through JMS dealing with customer−account information is. Securing the data In an enterprise, your most important asset is the data — information about your clients, products, sales, and almost anything else that can be stored in computerized form. Just imagine what can happen when someone decides to change the sale price of one of your most popular items. You could very quickly be looking at some large debts, possibly sending your company into bankruptcy. Note You think that outside users can't play with your pricing information? One very well−known attack against Web−based commerce has been to order one product, save the confirmation Chapter 24: System Architecture Issues 637 HTML page, make some modifications to it, and then submit the confirmation using a much cheaper price on the goods being purchased. Although these attacks were first seen more than two years ago, they are still being used effectively against e−commerce Web sites that don't implement any form of data protection. Securing your raw data means making sure that every access to it is authorized. Never accept anonymous connections to a database. Make sure that you know that those who do connect are the correct users, and restrict their access to the system. Finally, once they have connected, validate every transaction. Make sure that pricing information is correct, and if it is not, get the authorization details of the user making the approval. Most importantly — log everything. If something does happen, you can always do some forensic analysis to catch the malicious person. Securing the system Data security also involves some level of physical security. Is the company's vital e−commerce server a box sitting on the programmer's desk, where the cleaner can come in and accidentally turn it off? There's really no point having a secure application if any Joe Random User has direct access to the server machine. These machines are the life of your company: Do you want the accounts staff firing up a game of Quake Arena on your server? Probably not! If any user can gain access to the server machines, then any user has a way of directly attacking your application. Many sabotage attempts are made by disgruntled employees who want to leave a parting message after being given the pink slip. If you use a firewall to keep the outsiders out, why should you let everyone on your staff have a better level of access to the machine? Surely your accountant doesn't need direct access to the server. Besides, if external attackers make it through the first line of defense, do you really want to open up your entire network to them? At each critical point in your system, you should firewall the communications. In a minimal system, this would mean installing a firewall between each of the tier levels. Middleware machines have no need to contact a database server on any port other than the SQL socket connection. Web servers need only to access the beans and so only need the IIOP ports. Not only that, but your firewall should limit connections to those established between known IP addresses. Don't put a firewall between the middleware and the database and then allow any random machine to make a connection to the database. That still allows a malicious internal machine to directly access the database and make unauthorized changes. Securing the users Finally, there are the users themselves. Do you really want the marketing manager to be allowed to delete the entire database of products? No, we didn't think so. Even if everything else is protected, you still have the problem of the user not knowing what he or she is doing, or just being really tired and making a stupid, catastrophic mistake. There's an old cliché that is worth remembering — don't attribute to malice that which can be attributed to stupidity. In most cases, this is applicable to end users. When someone is tired at the end of the day's work, a mouse click that's off by a few pixels can be the difference between success and disaster. When you are in the requirements−gathering stage, work out just who needs to access the system and what tasks those users must perform. In the design phase, craft access levels to the system that follow those requirements. As you have seen in the EJB specification, each bean, and even each method within a bean, can have an individual user assigned to it. Make use of the ability to prevent the users from making that dumb mistake. If one person needs to occupy a number of different roles, make her assume those roles as necessary. Chapter 24: System Architecture Issues 638 Don't just give users open carte blanche to the full application. By forcing them to change, you not only protect against inappropriate use of the system, but you also help them remember which roles they are currently playing (not to mention the appropriate visual cues on the user interface). Summary Building enterprise applications requires much more than just slapping a bunch of code together. Even at its most fundamental level, you need to consider many different issues in both design and the final deployment of the system. During this chapter, we have walked you through, and given you pointers about, areas you should keep in mind when designing an enterprise application: Design issues for building enterprise applications• How to build applications that work across more than one machine• Tips on which API is the most appropriate in a given situation• Securing the system to prevent unauthorized interference with your most vital assets• Chapter 24: System Architecture Issues 639 Chapter 25: J2EE Design Patterns Overview As IT technology evolves, new ideas are created and added to the programmer's toolkit. Each level is more complex than the last, making the simpler things seem so trivial that you barely think about them. First came assembly language, and then higher−level languages like ALGOL and FORTRAN. Complex projects brought forth object−oriented programming and structured design (the classic waterfall model). As programmers got used to describing more and more complex structures, CASE design tools made an appearance. While at that time programmers tended to reuse their own structures, the light−bulb moment came with the release of a small book called Design Patterns. Programmers the world over exclaimed "Yes!" and since then the term design patterns has taken off. Now we even have patterns specific to J2EE applications, and these are what we are going to introduce you to in this chapter. Design−Pattern Basics Design patterns are more than just the latest fad in software development. The whole concept is based on years of knowledge being accumulated, sorted, and presented in easy−to−digest and easy−to−implement packages. Before diving into design patterns specific to J2EE development, take a step back and go through the basics of design patterns. What is a design pattern? As we just stated, design patterns come from years of programming experience. The programmers responsible for design patterns have taken their knowledge and come up with a collection of reusable chunks of knowledge that can be applied to any design. Each chunk of knowledge contains one idea, a small piece of an application design. Think of using these chunks as knowledge re−use, much like the code re−use provided by object−oriented development languages. Design patterns in the design process Not surprisingly, design patterns are used at design time. As you are analyzing the requirements specification and attempting to come up with an architecture, you can apply one or more design patterns to create the whole application. Compare using design patterns to code re−use. In your code, you create a class that does one thing — say representing a circular list. As you are coding another part of the application, you find that you need a buffer. Looking up your documentation, you discover that you have this circular−list code that would perfectly suit the buffer. In order to use the list, you import the class, create an instance of it, and then call the various methods. Design patterns act in a similar way at design time. When designing an application, you will rarely have to create something completely original. Although you may be inventing The Next Big Thing, when you look at the nuts−and−bolts level, really all you are doing is arranging a collection of existing small ideas into a new form. Your design consists of lots of smaller designs. Each of these pieces has been used many times before — in fact, most of them will occur to you because you remember having used them in previous practice. This process of reusing knowledge is the beginning of a design pattern. 640 [...]... released a book and a series of tutorials on its Java Developer Connection Web site (http:/ /java. sun.com/blueprints/patterns/j2ee_patterns/) 644 Chapter 25 : J2EE Design Patterns There you can find a complete range of patterns specifically aimed at the J2EE developer Standard enterprise design patterns for J2EE When you consider what goes into a typical J2EE application, you will find that the core set... The J2EE v1 .2. 1 reference implementation does not recognize the J2SE v1.4 environment It is not possible to run the two together So let's start with the simple things — you are going to need to download and install both the standard J2SE development environment (the JRE alone is not sufficient) and the reference implementation (you can find the reference implementation from http:/ /java. sun.com/j2ee/)... installing the J2SE environment The J2EE environment checks for the presence of a J2SE full installation and will not install unless it finds one Next, install all the optional packages and drivers you want to use To install the reference implementation, run the installation program that you downloaded If you are a Win 32 user, double−click the installation program (for example, j2sdkee−1 _2_ 1−win.exe)... called J2EE_HOME Where JAVA_ HOME describes the root directory of your J2SE installation (for example, c:\jdk1.3), J2EE_HOME describes the installation directory of your J2EE environment The reference implementation uses this description to locate many different parts of its runtime information, such as configuration files When compiling code, you may want to set the CLASSPATH to contain the J2EE JAR... for enterprise development, some of the standard patterns are very useful in the enterprise space, too For example, the Pet Store example (http:/ /java. sun.com/) that Sun uses to show off J2EE uses the MVC pattern as its basic architecture The model is the store data, the view is JSPs, and the controller is a series of EJBs — both session and entity To bolster the use of design patterns within J2EE−based... directory (or at least make it not−findable in the CLASSPATH), should fix most problems Tip Using the J2EE environment does not require that you install Sun's J2EE SDK implementation We have successfully used the enterprise applications in combination with IBM's Java runtime environment on both Win 32 and Linux Optional packages and drivers Apart from the issues you just saw, you may want to download... Sun's Java site Keep in mind that because most of the APIs are pluggable with driver implementations, once you have downloaded drivers to use with the J2EE reference implementation, these drivers will be usable with any commercial environment that you may purchase at a later date Implementations of the reference environment for Solaris, Linux, and Win 32 are available from Sun at http:/ /java. sun.com/j2ee/download.html... yourself using most of the forthcoming patterns every day The role of enterprise design patterns Within the enterprise environment, a number of patterns have emerged as standard and particularly suited to enterprise application development These enterprise patterns take the most common tasks that you need and turn them into a pattern Enterprise patterns are typically more concerned with the large−scale... wondering how on earth you can test all the code we've presented Well, Sun has made life easy for you by providing a reference implementation of the complete Java 2 Enterprise Edition environment You can use the reference environment to get a fast start on J2EE development without needing to spend large amounts of money on a fully fledged commercial environment Note A reference implementation of the specification... for compiling it will make life much easier All the classes defined by the J2EE environment can be found in the file $J2EE_HOME/lib/j2ee.jar If you are using command−line compiling, then adding the j2ee.jar file to the CLASSPATH will make life simpler for you Of course, the other option is to use the –classpath option on the Java compiler, but typing it out can get annoying If you are running an IDE, . within J2EE−based development, Sun has released a book and a series of tutorials on its Java Developer Connection Web site (http:/ /java. sun.com/blueprints/patterns/j2ee_patterns/). Chapter 25 : J2EE. Figure 24 −1 is not a typical realistic system. Sure it shows the basic arrangements, but it is Chapter 24 : System Architecture Issues 6 32 very rare to have only one computer at each tier. Figure 24 2. role of enterprise design patterns Within the enterprise environment, a number of patterns have emerged as standard and particularly suited to enterprise application development. These enterprise

Ngày đăng: 12/08/2014, 19: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