Ajax cho các nhà phát triển Java: Khảo sát bộ công cụ Web của Google

Phát triển các ứng dụng Ajax từ một cơ sở mã Java

Google Web Toolkit (GWT-Bộ công cụ Web của Google) mới được phát hành là một bộ toàn diện về các API và các công cụ cho phép bạn tạo các ứng dụng Web động gần như hoàn toàn trong mã Java™. Philip McCarthy quay lại loạt bài Ajax cho các nhà phát triển Java được ưa thích của mình để cho bạn thấy GWT có thể làm gì và giúp bạn quyết định xem nó có đúng với bạn không. (Lưu ý: bạn có thể tải về tệp ZIP đã cập nhật có chứa mã nguồn của bài viết này.)

Philip McCarthy, Nhà Phát triển, SmartStream Technologies Ltd

Philip McCarthy là nhà tư vấn phát triển phần mềm chuyên về các công nghệ Java và Web. Ông hiện đang làm việc trong dự án nền tảng phương tiện kỹ thuật số của Hewlett Packard tại HP Labs, Bristol. Trong những năm gần đây Phil đã phát triển một số các khách hàng Web phong phú sử dụng truyền dẫn máy chủ không đồng bộ và kịch bản lệnh DOM. Ông vui mừng vì chúng ta có một tên cho chúng. Bạn có thể liên lạc với Phil tại philmccarthy@gmail.com.



04 12 2009

GWT (xem Tài nguyên) có một cách tiếp cận khác thường để phát triển ứng dụng Web. Thay vì sử dụng sự phân tách thông thường của các cơ sở mã phía máy khách và phía máy chủ, GWT cung cấp một Java API để cho phép bạn tạo các giao diện đồ họa người dùng (GUI) dựa trên thành phần, rồi biên dịch chúng để hiển thị trong trình duyệt Web của người dùng. Việc sử dụng GWT rộng rãi để phát triển trên Swing hay SWT hơn là phát triển ứng dụng Web theo kinh nghiệm thông thường và nó thử trừu tượng hóa cách xa giao thức HTTP và mô hình HTML DOM. Thật vậy, thực tế mà ứng dụng cuối cùng được biểu hiện trong một trình duyệt Web cảm thấy gần như ngẫu nhiên.

GWT đạt được những thành tích thông qua việc tạo mã, với trình biên dịch của GWT tạo JavaScript từ mã Java phía máy khách của bạn. Nó hỗ trợ một tập con của các gói java.langjava.util, cùng với các API được chính GWT cung cấp. Một ứng dụng GWT đã biên dịch bao gồm các đoạn mã HTML, XML và JavaScript. Tuy nhiên, các đoạn mã này gần như không thể giải được và ứng dụng đã biên dịch này tốt nhất được coi như là một hộp đen -- sự tương đương của GWT của mã byte (bytecode) Java.

Trong bài này, tôi sẽ chạy qua việc tạo một ứng dụng GWT đơn giản để lấy báo cáo thời tiết từ một Web API từ xa và hiển thị nó trong trình duyệt. Theo cách này, tôi sẽ trình bày ngắn gọn về càng nhiều khả năng của GWT càng tốt và tôi sẽ đề cập đến một số vấn đề tiềm năng mà bạn sẽ gặp phải.

Khởi đầu đơn giản

Liệt kê 1 cho thấy mã nguồn Java của gần như một ứng dụng có thể đơn giản nhất mà bạn có thể thực hiện bằng cách sử dụng GWT:

Liệt kê 1. Ví dụ GWT đơn giản nhất
public class Simple implements EntryPoint {

   public void onModuleLoad() {
     final Button button = new Button("Say 'Hello'");

     button.addClickListener(new ClickListener() {
        public void onClick(Widget sender) {
        Window.alert("Hello World!");
        }
     });

     RootPanel.get().add(button);
   }
}

Điều này trông rất giống mã GUI mà bạn có thể có đã viết bằng Swing, AWT hoặc SWT. Như các bạn có thể đoán, Liệt kê 1 tạo một nút để hiển thị một thông báo "Hello World!" khi bạn nhấn vào nó. Nút này được thêm vào RootPanel, một trình bao (wrapper) GWT quanh phần thân của một trang HTML. Hình 1 cho thấy ứng dụng đang hoạt động, đang chạy bên trong GWT Shell (trình bao). Shell này là môi trường lưu trữ gỡ rối, kết hợp một trình duyệt Web đơn giản, có trong GWT SDK.

Hình 1. Ví dụ GWT đơn giản nhất đang hoạt động
Ví dụ GWT đơn giản nhất đang hoạt động

Xây dựng ứng dụng của Bản tin dự báo thời tiết (Weather Reporter)

Tôi sẽ sử dụng GWT để tạo một ứng dụng Bản tin dự báo thời tiết đơn giản. GUI của ứng dụng giới thiệu cho người dùng với một hộp đầu vào để nhập mã bưu điện và một lựa chọn độ C hoặc độ F (Fahrenheit) để biểu diễn nhiệt độ. Khi người dùng nhấn vào nút Submit (Gửi đi), ứng dụng sử dụng API Weather miễn phí của Yahoo để nhận được một bản tin với định dạng RSS cho các vị trí đã chọn. Phần HTML của tài liệu này được trích ra và hiển thị cho người dùng.

Các ứng dụng GWT được đóng gói thành các mô đun và phải tuân theo một cấu trúc cụ thể. Một tệp cấu hình -- có tên là module-name gwt.xml -- định nghĩa lớp hoạt động như là lối vào của ứng dụng và chỉ thị xem các nguồn tài nguyên có cần được thừa hưởng từ các mô đun GWT khác không. Bạn phải đặt các tệp cấu hình trong cấu trúc gói nguồn của ứng dụng ở một cùng mức như một gói có tên client, đây là nơi tất cả các mã Java phía máy khách được lưu trữ và một thư mục có tên là public (công cộng), có chứa Tài nguyên Web của dự án như là các hình ảnh, CSS và HTML. Cuối cùng, thư mục public phải bao gồm một tệp HTML có một thẻ meta có chứa tên đủ điều kiện của mô đun đó. Thư viện JavaScript đang chạy của GWT sử dụng tệp này để khởi tạo ứng dụng.

applicationCreator GWT tạo cấu trúc cơ bản này cho bạn, căn cứ vào tên lớp lối vào của bạn. Vì vậy, việc gọi applicationCreator developerworks.gwt.weather.client.Weather tạo ra một phác thảo dự án mà tôi có thể sử dụng như một điểm khởi đầu cho ứng dụng Bản tin dự báo thời tiết. Nguồn tải về cho ứng dụng này bao gồm một tệp xây dựng Ant (Ant Build file) có chứa một số chỉ tiêu có ích để làm việc với một dự án GWT phù hợp với cấu trúc này (xem Tải về).

Phát triển GUI cơ bản

Trước tiên, tôi sẽ phát triển cách bố trí cơ bản về các tiện ích (widget) giao diện người dùng của ứng dụng, mà không cần thêm bất kỳ hành vi nào. Siêu lớp của hầu hết mọi thứ mà bạn có thể đưa ra trong giao diện người dùng GWT là lớp Widget. Các Widget luôn được chứa trong các Panel (bảng), mà chính nó cũng là các Widget và do đó có thể được lồng nhau. Các kiểu khác nhau của các panel cung cấp các hành vi bố trí khác nhau. Vì vậy, một Panel của GWT đóng một vai trò tương tự như Layout (cách bố trí) trong AWT/Swing hoặc một Box trong XUL.

Cuối cùng tất cả các widget và panel phải được gắn vào trang Web chứa chúng. Như bạn đã thấy trong Liệt kê 1, bạn có thể gắn chúng trực tiếp vào RootPanel. Ngoài ra, bạn có thể sử dụng RootPanel để nhận được các tham chiếu đến các phần tử HTML được các mã nhận dạng (ID) hay các tên lớp (classnames) của chúng xác định. Trong trường hợp này, tôi sẽ sử dụng hai phần tử HTML DIV riêng biệt có tên là input-container (thùng chứa-đầu vào) và output-container (thùng chứa-đầu ra). Thùng chứa đầu tiên chứa các điều khiển UI cho Bản tin dự báo thời tiết, thùng chứa thứ hai hiển thị chính bản tin dự báo thời tiết đó.

Liệt kê 2 cho thấy đoạn mã cần thiết để thiết lập cách bố trí cơ bản; nó sẽ tự giải thích. Widget HTML chỉ đơn giản là một thùng chứa để đánh dấu HTML. Đây là nơi kết quả đầu ra HTML từ nguồn cấp dữ liệu thời tiết Yahoo! được hiển thị. Tất cả các mã này hoạt động bên trong phương thức onModuleLoad() của lớp Weather (Thời tiết), được giao diện EntryPoint cung cấp. Phương thức này được gọi khi một trang Web nhúng mô đun Weather được nạp vào trong một trình duyệt Web của máy khách.

Liệt kê 2. Bố trí mã cho bản tin dự báo thời tiết
public void onModuleLoad() {

   HorizontalPanel inputPanel = new HorizontalPanel();

   // Align child widgets along middle of panel
   inputPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);

   Label lbl = new Label("5-digit zipcode: ");
   inputPanel.add(lbl);

   TextBox txBox = new TextBox();
   txBox.setVisibleLength(20);

   inputPanel.add(txBox);

   // Create radio button group to select units in C or F
   Panel radioPanel = new VerticalPanel();

   RadioButton ucRadio = new RadioButton("units", "Celsius");
   RadioButton ufRadio = new RadioButton("units", "Fahrenheit");

   // Default to Celsius
   ucRadio.setChecked(true);

   radioPanel.add(ucRadio);
   radioPanel.add(ufRadio);

   // Add radio buttons panel to inputs
   inputPanel.add(radioPanel);

   // Create Submit button
   Button btn = new Button("Submit");    

   // Add button to inputs, aligned to bottom
   inputPanel.add(btn);
   inputPanel.setCellVerticalAlignment(btn,
      HasVerticalAlignment.ALIGN_BOTTOM);

   RootPanel.get("input-container").add(inputPanel);

   // Create widget for HTML output
   HTML weatherHtml = new HTML();

   RootPanel.get("output-container").add(weatherHtml);
}

Hình 2 cho thấy cách bố trí được thể hiện trong GWT Shell:

Hình 2. Cách bố trí GUI cơ bản
Cách bố trí GUI cơ bản

Thêm kiểu dáng nhờ CSS

Trang Web được thể hiện trông khá buồn tẻ, vì vậy nó lợi dụng một số quy tắc kiểu dáng của CSS (Bảng định kiểu nhiều tầng). Bạn có thể có một vài cách tiếp cận để định kiểu một ứng dụng GWT. Trước tiên, theo mặc định, mỗi widget có một tên lớp CSS theo dạng project-widget. Ví dụ, gwt-Buttongwt-RadioButton là hai trong số các tên lớp của widget GWT cốt lõi. Các panel thường được triển khai thực hiện như một búi các bảng lồng nhau và không có các tên lớp mặc định.

Cách tiếp cận kiểu widget cho mỗi tên lớp mặc định cho phép nó dễ dàng định kiểu các widget một cách thống nhất trên toàn ứng dụng của bạn. Tất nhiên, áp dụng các quy tắc bộ chọn CSS thông thường và bạn có thể khai thác chúng để áp dụng các kiểu dáng khác nhau cho kiểu widget giống nhau tùy thuộc vào ngữ cảnh của nó. Để luôn linh hoạt hơn, bạn có thể thay thế hoặc tăng thêm các tên lớp mặc định của widgets trên một cơ sở đặc biệt bằng cách gọi các phương thức setStyleName()addStyleName() của chúng.

Liệt kê 3 kết hợp các cách tiếp cận này để áp dụng các kiểu dáng cho bảng nhập vào của ứng dụng Bản tin dự báo thời tiết. Tên lớp weather-input-panel được tạo trong Weather.java với một cuộc gọi đến inputPanel.setStyleName("weather-input-panel");.

Liệt kê 3. Áp dụng các kiểu dáng của bảng định khiểu (CSS) cho bảng nhập vào của ứng dụng Bản tin dự báo thời tiết
/* Style the input panel itself */
.weather-input-panel {
   background-color: #AACCFF;
   border: 2px solid #3366CC;
   font-weight: bold;
}

/* Apply padding to every element within the input panel */
.weather-input-panel * {
   padding: 3px;
}

/* Override the default button style */
.gwt-Button {
   background-color: #3366CC;
   color: white;
   font-weight: bold;
   border: 1px solid #AACCFF;
}

/* Apply a hover effect to the button */
.gwt-Button:hover {
   background-color: #FF0084;
}

Hình 3 hiển thị lại ứng dụng, với các kiểu dáng này tại chỗ:

Hình 3. Bảng nhập vào với các kiểu dáng được áp dụng
Bảng nhập vào với các kiểu dáng được áp dụng

Thêm hành vi phía máy khách

Bây giờ cách bố trí cơ bản và việc định kiểu của ứng dụng được thực hiện, tôi có thể bắt đầu áp dụng một số hành vi phía máy khách. Bạn sử dụng mẫu Listener (Người nghe) quen thuộc để thực hiện xử lý sự kiện trong GWT. GWT cung cấp các giao diện Listener cho các sự kiện chuột, các sự kiện bàn phím, các sự kiện thay đổi và v.v, cũng như một số lớp adapter và lớp helper cho thuận tiện hơn.

Nói chung, bạn thêm những người nghe sự kiện bằng cách thể hiện lớp bên trong ẩn danh quen thuộc với các lập trình viên Swing. Tuy nhiên, tham số đầu tiên của tất cả các phương thức Listener của GWT là người gửi của sự kiện, thường là widget mà người dùng đã tương tác với nó. Điều này có nghĩa là bạn có thể đính kèm cùng cá thể Listener cho nhiều widget khi cần thiết; bạn có thể sử dụng tham số người gửi để xác định widget nào trong số chúng đã thực hiện sự kiện này.

Liệt kê 4 cho thấy việc thực hiện của hai người nghe sự kiện trong ứng dụng bản tin dự báo thời tiết. Một trình xử lý (handler) nhấn nút được thêm vào cho nút Submit (Gửi đi) và một trình điều khiển bàn phím (keyhandler) được thêm vào cho TextBox. Dù nhấn chuột vào nút Submit hoặc ấn phím Enter khi TextBox xuất hiện sẽ làm cho trình xử lý có liên quan gọi phương thức riêng validateAndSubmit(). Ngoài các mã trong Liệt kê 4, txBoxucRadio đã trở thành các thành phần thể hiện của lớp Weather sao cho có thể truy cập chúng từ phương thức xác nhận hợp lệ.

Liệt kê 4. Thêm hành vi phía máy khách
// Create Submit button, with click listener inner class attached
Button btn = new Button("Submit", new ClickListener() {

   public void onClick(Widget sender) {
      validateAndSubmit();
   }
});

// For usability, also submit data when the user hits Enter 
// when the textbox has focus
txBox.addKeyboardListener(new KeyboardListenerAdapter(){

   public void onKeyPress(Widget sender, char keyCode, int modifiers) {

      // Check for Enter key
      if ((keyCode == 13) && (modifiers == 0)) {
         validateAndSubmit();
      }        
   }      
});

Liệt kê 5 cho thấy việc thực hiện phương thức validateAndSubmit(). Nó khá đơn giản, dựa vào một lớp ZipCodeValidator để chứa đựng logic xác nhận. Nếu người dùng không nhập vào mã năm số bưu điện hợp lệ, validateAndSubmit() sẽ hiển thị thông báo lỗi trong hộp cảnh báo, thể hiện về thế giới GWT như một lời gọi Window.alert(). Nếu mã bưu điện hợp lệ, sau đó cả nó lẫn sự lựa chọn của người dùng theo đơn vị độ C hoặc độ F được chuyển tới phương thức fetchWeatherHtml(), như tôi sẽ bắt đầu ngay sau đây.

Liệt kê 5. Logic validateAndSubmit
private void validateAndSubmit() {

   // Trim whitespace from input
   String zip = txBox.getText().trim();

   if (!zipValidator.isValid(zip)) {
     Window.alert("Zip-code must have 5 digits");
     return;
   }

   // Disable the TextBox
   txBox.setEnabled(false);

   // Get choice of celsius/fahrenheit
   boolean celsius = ucRadio.isChecked();
   fetchWeatherHtml(zip, celsius);
}

Gỡ rối phía máy khách với Shell của GWT

Tôi sắp chuyển sang hướng khác một chút ở đây để đề cập rằng GWT Shell có các kết nối JVM cho phép bạn gỡ rối mã phía máy khách của bạn trong một Java IDE. Bạn có thể tương tác với giao diện người dùng Web của bạn và chuyển qua đoạn mã Java thể hiện JavaScript tương ứng được thực thi trên máy khách. Đây là một khả năng quan trọng vì việc gỡ rối JavaScript đã tạo ra ở phía máy khách về cơ bản không phải là phần khởi đầu.

Thật dễ dàng để cấu hình một nhiệm vụ gỡ rối Eclipse để khởi chạy trình bao (shell) qua lớp com.google.gwt.dev.GWTShell. Hình 4 cho thấy Eclipse tạm dừng tại một điểm ngắt (Breakpoint) trong phương thức validateAndSubmit(), tiếp sau việc nhấn chuột lên nút Submit:

Hình 4. Eclipse đang gỡ rối mã GWT phía máy khách
Eclipse đang gỡ rối mã GWT phía máy khách

Giao tiếp với các các thành phần phía máy chủ

Bây giờ ứng dụng Bản tin dự báo thời tiết có thể thu thập và xác minh đầu vào người dùng. Bước tiếp theo là lấy dữ liệu từ máy chủ. Trong việc phát triển Ajax thông thường, việc lấy dữ liệu này sẽ đòi hỏi gọi một nguồn tài nguyên phía máy chủ trực tiếp từ JavaScript và nhận dữ liệu trả về đã mã hóa như là JavaScript Object Notation (JSON-Ký hiệu đối tượng JavaScript) hoặc XML. GWT trừu tượng hóa quá trình giao tiếp này phía sau cơ chế gọi thủ tục riêng của nó từ xa (RPC).

Trong thuật ngữ GWT, mã máy khách giao tiếp với các dịch vụ đang chạy trên máy chủ Web. Cơ chế RPC được sử dụng để lộ ra các dịch vụ này có các điểm tương đồng với cách tiếp cận sử dụng Java RMI. Điều này có nghĩa là bạn chỉ cần viết thực hiện dịch vụ phía máy chủ và một vài giao diện. Việc tạo mã và phản chiếu giải quyết các nhánh rẽ của máy khách và các ủy quyền chính của máy chủ.

Theo đó, bước đầu tiên là để xác định một giao diện cho dịch vụ Bản tin dự báo thời tiết. Giao diện này cần mở rộng giao diện GWT RemoteService và nó chứa các chữ kí của các phương thức dịch vụ, các phương thức này được trưng ra cho mã máy khách GWT. Vì các cuộc gọi RPC trong GWT ở giữa mã JavaScript và mã Java, nên GWT kết hợp một cơ chế tuần tự hóa-đối tượng để dàn xếp các đối số và trả về các giá trị trên đường ranh giới ngôn ngữ (xem phần bên Các kiểu tuần tự hóa để thấy những gì bạn có thể sử dụng).

Các kiểu tuần tự hóa

Một tóm tắt ngắn gọn về các kiểu tuần tự hóa trong GWT như sau:

  • Các lớp gốc (như int) và các lớp trình bao bọc (wrapper) nguyên thủy (như Integer) là tuần tự hóa.
  • StringDate là tuần tự hóa.
  • Mảng của các kiểu tuần tự hóa là sự tuần tự hóa chính nó.
  • Các lớp do người dùng định nghĩa là tuần tự hóa nếu tất cả các thành viên dài hạn của chúng là tuần tự hóa và chúng triển khai thực hiện giao diện IsSerializable của GWT.
  • Các lớp Collection (bộ sưu tập) có thể được sử dụng cùng với các chú thích javadoc có tuyên bố kiểu tuần tự hóa mà chúng có chứa.

Vì mã máy khách bị hạn chế theo tập hợp con nhỏ của các lớp Java được GWT triển khai thực hiện, nên các kiểu tuần tự hóa này tạo ra lớp phủ tương đối toàn diện.

Với một giao diện dịch vụ được định nghĩa, bước tiếp theo là thực hiện nó trong một lớp có mở rộng lớp RemoteServiceServlet của GWT. Như tên cho thấy, đây là một chuyên môn của HttpServlet của ngôn ngữ Java, vì vậy nó có thể được lưu trữ trên bất kỳ thùng chứa servlet nào.

Một nét riêng biệt của GWT đáng nói đến ở đây là giao diện truy cập từ xa của dịch vụ này phải ở ngay trong gói client của ứng dụng của bạn vì nó cần phải được kết hợp trong quá trình tạo JavaScript. Tuy nhiên, vì lớp thực hiện phía máy chủ tham chiếu giao diện truy cập từ xa, nên sự phụ thuộc thời gian biên dịch Java bây giờ tồn tại giữa mã phía máy chủ và mã phía máy khách. Giải pháp của tôi cho vấn đề này là đặt giao diện truy cập từ xa vào gói con common của gói client. Sau đó tôi có common trong xây dựng Java nhưng loại trừ phần còn lại của gói client. Điều này ngăn cản các tệp lớp khỏi được tạo ra từ mã máy khách, mã này chỉ cần được chuyển đổi sang JavaScript. Một giải pháp ngắn gọn hơn có thể là chia nhỏ cấu trúc gói giữa hai thư mục mã nguồn cho mã phía máy khách và phía máy chủ để sao chép các lớp chung vào cả hai thư mục.

Liệt kê 6 hiển thị giao diện dịch vụ từ xa được sử dụng trong ứng dụng Bản tin dự báo thời tiết: WeatherService. Nó có một mã bưu điện và một cờ phân biệt độ C/độ F (Celsius/Fahrenheit) làm đầu vào và trả về một String có chứa một mô tả thời tiết HTML. Liệt kê 6 cũng cho thấy phác thảo của YahooWeatherServiceImpl, sử dụng API thời tiết của Yahoo! để nhận được một nguồn cấp dữ liệu RSS cho mã vùng bưu điện cụ thể và trích ra một mô tả HTML từ nó.

Liệt kê 6. Giao diện WeatherService từ xa và thực hiện một phần
public interface WeatherService extends RemoteService {

   /**
    * Return HTML description of weather
    * @param zip zipcode to fetch weather for
    * @param isCelsius true to fetch temperatures in celsius, 
    * false for fahrenheit
    * @return HTML description of weather for zipcode area
    */
   public String getWeatherHtml(String zip, boolean isCelsius) 
      throws WeatherException;
} 

public class YahooWeatherServiceImpl extends RemoteServiceServlet
   implements WeatherService {

   /**
    * Return HTML description of weather
    * @param zip zipcode to fetch weather for
    * @param isCelsius true to fetch temperatures in celsius, 
    * false for fahrenheit
    * @return HTML description of weather for zipcode area
    */
   public String getWeatherHtml(String zip, boolean isCelsius) 
      throws WeatherException {

     // Clever programming goes here
   }
}

Nhiều thứ bắt đầu được tách ra khỏi cách tiếp cận RMI tiêu chuẩn tại điểm này. Vì các cuộc gọi Ajax từ JavaScript không đồng bộ, nên cần có một số công việc bổ sung thêm để định nghĩa một giao diện không đồng bộ, mã máy khách sử dụng giao diện đó để gọi dịch vụ. Các chữ kí của phương thức của giao diện không đồng bộ khác với các giao diện truy cập từ xa, vì vậy GWT dựa vào việc đặt tên gần như ma thuật (Magical Coincidental Naming). Nói cách khác, mối quan hệ biên dịch-thời gian tĩnh không tồn tại giữa giao diện không đồng bộ và giao diện truy cập từ xa, nhưng GWT tìm hiểu nó qua các quy ước đặt tên. Liệt kê 7 chỉ ra giao diện không đồng bộ cho WeatherService:

Liệt kê 7. Giao diện không đồng bộ cho WeatherService
public interface WeatherServiceAsync {

   /**
    * Fetch HTML description of weather, pass to callback
    * @param zip zipcode to fetch weather for
    * @param isCelsius true to fetch temperatures in celsius,
    * false for fahrenheit
    * @param callback Weather HTML will be passed to this callback handler
    */
   public void getWeatherHtml(String zip, boolean isCelsius, 
      AsyncCallback callback);
}

Như bạn có thể nhận thấy, ý tưởng chung là tạo ra giao diện được gọi là MyServiceAsync và để cung cấp cho chữ kí của phương thức, loại bỏ các kiểu trả về và thêm một tham số phụ của kiểu AsyncCallback. Giao diện không đồng bộ cũng phải ở ngay trong cùng gói như giao diện truy cập từ xa. Lớp AsyncCallback có hai phương thức: onSuccess()onFailure(). Nếu cuộc gọi tới dịch vụ này là thành công, thì onSuccess() được gọi với giá trị trả về của cuộc gọi dịch vụ. Nếu cuộc gọi từ xa không thành công, thì onFailure() được gọi và thông qua Throwable được dịch vụ này tạo ra để thể hiện nguyên nhân gây ra lỗi này.


Gọi dịch vụ từ máy khách

Với WeatherService và giao diện không đồng bộ của nó tại chỗ, tôi bây giờ có thể thay đổi khách hàng Bản tin dự báo thời tiết để gọi dịch vụ này và xử lý trả lời của nó. Bước đầu tiên là mã thiết lập soạn sẵn: nó tạo cá thể của WeatherServiceAsync cho khách hàng Weather sử dụng bằng cách gọi GWT.create(WeatherService.class) và tạo khuôn mẫu xuống đối tượng mà nó trả về. Tiếp theo WeatherServiceAsync phải là khuôn mẫu cho một ServiceDefTarget sao cho setServiceEntryPoint() có thể được gọi nó. setServiceEntryPoint() trỏ vào nhánh rẽ WeatherServiceAsync tại URL ở đó việc thực hiện dịch vụ từ xa tương ứng của nó được triển khai. Lưu ý rằng việc này thực sự được mã hóa cứng trong thời gian biên dịch. Vì mã này trở thành mã JavaScript dùng trong trình duyệt Web, nên không có cách nào để tìm kiếm URL này từ một tệp các đặc tính trong thời gian chạy. Rõ ràng, việc này ràng buộc khả năng di động của một ứng dụng Web GWT đã biên dịch.

Liệt kê 8 cho thấy việc thiết lập đối tượng WeatherServiceAsync và rồi đưa ra thực hiện fetchWeatherHtm(), mà tôi đã đề cập trước đó (xem Thêm hành vi phía máy khách):

Liệt kê 8. Sử dụng RPC để gọi một dịch vụ truy cập từ xa
// Statically configure RPC service
private static WeatherServiceAsync ws = 
   (WeatherServiceAsync) GWT.create(WeatherService.class);
static {
   ((ServiceDefTarget) ws).setServiceEntryPoint("ws");
}

/**
 * Asynchronously call the weather service and display results
 */
private void fetchWeatherHtml(String zip, boolean isCelsius) {

   // Hide existing weather report
   hideHtml();

   // Call remote service and define callback behavior
   ws.getWeatherHtml(zip, isCelsius, new AsyncCallback() {
      public void onSuccess(Object result) {

         String html = (String) result;

         // Show new weather report
         displayHtml(html);
      }

      public void onFailure(Throwable caught) {
         Window.alert("Error: " + caught.getMessage());
         txBox.setEnabled(true);
       }
   });
}

Cuộc gọi thực tế đến getWeatherHtml() của dịch vụ là dễ dàng thực hiện, với một lớp của trình xử lý cuộc gọi lại ẩn danh. Lớp này đơn giản chuyển trả lời của máy chủ vào một phương thức để hiển thị nó.

Hình 5 cho thấy ứng dụng đang hoạt động hiển thị một bản tin dự báo thời tiết đã được lấy từ API thời tiết của Yahoo:

Hình 5. Ứng dụng bản tin dự báo thời tiết hiển thị một bản tin lấy từ Yahoo!
Ứng dụng bản tin dự báo thời tiết hiển thị một bản tin lấy từ Yahoo!

Sự cần thiết của việc xác nhận hợp lệ phía máy chủ

Sự kết hợp GWT của mã phía máy khách và phía máy chủ vốn đã nguy hiểm. Vì bạn lập trình mọi thứ bằng ngôn ngữ Java, với sự trừu tượng của GWT đang che giấu sự phân tách máy khách/máy chủ, nên người ta dễ nghĩ sai rằng mã máy khách của bạn có thể tin cậy được trong thời gian chạy. Đây là một sai lầm. Bất kỳ mã nào thực hiện trong một trình duyệt Web đều có thể bị làm giả hoặc đi vòng hoàn toàn, bởi một người dùng xấu. GWT đưa ra một mức khó hiểu cao hơn để giảm nhẹ vấn đề đi một mức, nhưng một điểm tấn công thứ hai vẫn còn: bất kỳ lưu lượng HTTP nào truyền dẫn giữa máy khách GWT của bạn và các dịch vụ của nó.

Giả sử tôi là một kẻ tấn công muốn khai thác điểm yếu trong ứng dụng bản tin dự báo thời tiết. Hình 6 chỉ ra công cụ của Fiddler của Microsoft chặn một yêu cầu từ máy khách của bản tin dự báo thời tiết tới WeatherService đang chạy trên máy chủ. Khi bị chặn lại, Fiddler cho phép bạn thay đổi bất kỳ một phần nào của yêu cầu. Văn bản được tô sáng cho biết tôi đã tìm thấy mã bưu điện mà tôi đã xác định được mã hóa theo yêu cầu nằm ở đâu. Bây giờ tôi có thể thay đổi mã bưu điện này thành bất cứ mã nào mà tôi thích, có lẽ từ "10001" cho đến "XXXXX."

Hình 6. Sử dụng Fiddler để bỏ qua xác nhận hợp lệ phía máy khách
Sử dụng Fiddler để bỏ qua xác nhận hợp lệ phía máy khách

Bây giờ, giả sử một số mã phía máy chủ chưa bao giờ được sử dụng trong YahooWeatherServiceImpl gọi Integer.parseInt() theo mã bưu điện. Sau đó, mã bưu điện phải chuyển qua việc kiểm tra xác nhận hợp lệ đã được tích hợp vào trong phương thức validateAndSubmit() của Weather có đúng không? Đúng, như bạn đã thấy, việc kiểm tra này đã bị hỏng và bây giờ một NumberFormatException được đưa ra.

Trong trường hợp này, không có điều gì khủng khiếp xảy ra cả và kẻ tấn công chỉ nhìn thấy một thông báo lỗi trong máy khách. Tuy nhiên, khả năng xảy ra toàn bộ một lớp tấn công chống lại các ứng dụng GWT đang quan tâm đến dữ liệu nhạy cảm hơn. Hãy tưởng tượng các mã bưu điện đã thay thế cho một số mã nhận dạng ID của khách hàng trong một ứng dụng theo dõi đơn hàng. Việc ngăn chặn và thay đổi giá trị này có thể để lộ ra các thông tin tài chính nhạy cảm về các khách hàng khác. Bất kỳ ở đâu có một truy vấn cơ sở dữ liệu sử dụng một giá trị, cách tiếp cận như vậy cho phép có thể xảy ra một cuộc tấn công bên trong SQL.

Không có cuộc tấn công nào sẽ là hoạt động thông minh cho bất cứ ai đã làm việc với các ứng dụng Ajax trước đây. Bạn chỉ cần kiểm tra hai lần bất kỳ giá trị đầu vào nào bằng cách xác nhận lại chúng bên phía máy chủ. Điều quan trọng ghi nhớ là một số mã Java mà bạn viết trong ứng dụng GWT của bạn về bản chất là không đáng tin cậy trong thời gian chạy. Tuy nhiên đám mây GWT này không có một lớp lót bảo vệ bằng bạc. Trong ứng dụng Bản tin dự báo thời tiết, tôi đã viết một ZipCodeValidator để sử dụng trên máy khách, vì vậy tôi có thể đơn giản di chuyển nó vào gói client.common của tôi và sử dụng lại việc xác nhận hợp lệ giống như ở phía máy chủ. Liệt kê 9 cho thấy việc kiểm tra này được kết hợp vào YahooWeatherServiceImpl:

Liệt kê 9. ZipCodeValidator kết hợp vào YahooWeatherServiceImpl
public String getWeatherHtml(String zip, boolean isCelsius) 
       throws WeatherException {

   if (!new ZipCodeValidator().isValid(zip)) {
      log.warn("Invalid zipcode: "+zip);
      throw new WeatherException("Zip-code must have 5 digits");
   }

Gọi JavaScript bản địa với JSNI

Các thư viện các hiệu ứng trực quan đang trở nên ngày càng phổ biến trong việc phát triển ứng dụng Web, dù các hiệu ứng của chúng được sử dụng để cung cấp cách xử lý của tương tác người dùng thích hợp hoặc chỉ thêm đẹp đẽ. Tôi muốn thêm một số trang trí (eye-candy) cho ứng dụng Bản tin dự báo thời tiết. GWT không cung cấp kiểu chức năng này, nhưng JavaScript Native Interface (JSNI -Giao diện bản địa JavaScript) đưa ra một giải pháp. JSNI cho phép bạn thực hiện cuộc gọi JavaScript trực tiếp từ mã Java máy khách GWT. Điều này có nghĩa là, ví dụ, tôi có thể tận dụng các hiệu ứng từ các thư viện Scriptaculous (xem Tài nguyên) hoặc từ thư viện giao diện người dùng Yahoo.

JSNI sử dụng một sự kết hợp khéo léo của từ khóa native của ngôn ngữ Java và JavaScript được nhúng trong một khối bình luận đặc biệt. Có thể giải thích rõ ràng bằng ví dụ, vì vậy Liệt kê 10 cho thấy một phương thức gọi một hiệu ứng Scriptaculous cụ thể trên một Element:

Liệt kê 10. Gọi các hiệu ứng Scriptaculous bằng JSNI
/**
 * Publishes HTML to the weather display pane
 */
private void displayHtml(String html) {
   weatherHtml.setHTML(html);
   applyEffect(weatherHtml.getElement(), "Appear");
}

/**
 * Applies a Scriptaculous effect to an element
 * @param element The element to reveal
 */
private native void applyEffect(Element element, String effectName) /*-{

   // Trigger named Scriptaculous effect
   $wnd.Effect[effectName](element);
}-*/;

Đây là mã Java hoàn toàn hợp lệ vì trình biên dịch chỉ thấy private native void applyEffect(Element element, String effectName);. GWT phân tích cú pháp các nội dung của các khối bình luận và đưa ra kết quả JavaScript đúng nguyên văn. GWT cung cấp các biến $wnd$doc để tham khảo các đối tượng cửa sổ và tài liệu. Trong trường hợp này, tôi chỉ cần truy cập vào đối tượng Effect của Scriptaculous mức cao nhất và sử dụng cú pháp đối tượng-người truy cập (accessor) và dùng dấu ngoặc vuông của JavaScript để gọi chức năng có tên do người gọi chỉ định. Kiểu Element là một kiểu "ma thuật" được GWT cung cấp để thể hiện cho phần tử bên dưới của Widget trong cả hai mã Java và JavaScript. Các String là một trong số ít các kiểu khác có thể được chuyển qua trong suốt giữa mã Java và JavaScript thông qua JSNI.

Bây giờ tôi có một bản tin dự báo thời tiết mất dần vẻ đẹp khi dữ liệu được trả về từ máy chủ. Chi tiết cuối cùng là kích hoạt lại mã bưu điện TextBox khi hiệu ứng đó đã hoàn tất. Scriptaculous sử dụng một cơ chế gọi lại không đồng bộ để thông báo cho người nghe về vòng đời của các hiệu ứng. Đây là nơi mọi thứ nhận được ít phức tạp hơn vì tôi cần có JavaScript gọi lại mã Java khách hàng GWT của tôi. Trong JavaScript, bạn có thể gọi bất kỳ hàm nào với một số các đối số tùy ý, do đó, sự quá tải của phương thức kiểu dáng Java không tồn tại. Điều này có nghĩa rằng JSNI cần sử dụng một cú pháp khó sử dụng đề cập đến các phương thức Java để làm rõ các sự quá tải có thể. Tài liệu GWT nói rõ cú pháp này như sau:

[instance-expr.]@class-name::method-name(param-signature)(arguments)

Bộ phận instance-expr. là phần tùy chọn do các phương thức tĩnh được gọi không cần cho một sự tham khảo đối tượng. Hơn nữa, dễ dàng thấy nó qua ví dụ minh họa, trong Liệt kê 11:

Liệt kê 11. Gọi lại vào mã Java bằng JSNI
/**
 * Applies a Scriptaculous effect to an element
 * @param element The element to reveal
 */
private native void applyEffect(Element element, String effectName) /*-{

  // Keep reference to self for use inside closure
  var weather = this;

  // Trigger named Scriptaculous effect
  $wnd.Effect[effectName](element, { 
     afterFinish : function () {

     // Make call back to Weather object
     weather.@developerworks.gwt.weather.client.Weather::effectFinished()();
     } 
  });
}-*/;

/**
 * Callback triggered when a Scriptaculous effect finishes.
 * Re-enables the input textbox.
 */
private void effectFinished() {
  this.txBox.setEnabled(true);
  this.txBox.setFocus(true);
}

Phương thức applyEffect() đã được thay đổi để chuyển đối số phụ afterFinish tới Scriptaculous. Giá trị của afterFinish là một hàm ẩn danh, được gọi khi hiệu ứng này được thực hiện. Điều này hơi giống như cách diễn đạt lớp bên trong ẩn danh được các trình xử lý sự kiện GWT sử dụng. Cuộc gọi lại thực sự vào trong mã Java được thực hiện bằng cách xác định thể hiện của đối tượng Weather để gọi yêu cầu, sau đó là toàn bộ tên đủ điều kiện của lớp Weather, tiếp đến tên của hàm để gọi. Cặp rỗng đầu tiên trong các ngoặc đơn chứng tỏ rằng tôi muốn gọi phương thức có tên là effectFinished() không có đối số. Tập thứ hai trong ngoặc đơn gọi hàm này.

Thủ thuật ở đây là biến cục bộ, weather, giữ một bản sao của tài liệu tham khảo this (này). Do cách JavaScript gọi các ngữ nghĩa thực hiện, biến this trong hàm afterFinish thực sự là một đối tượng Scriptaculous vì Scriptaculous thực hiện cuộc gọi hàm này. Việc tạo bản sao của this ở ngoài tập kín là một cách giải quyết khá đơn giản.

Bây giờ tôi đã giải thích một số khả năng của JSNI, tôi sẽ chỉ ra rằng một cách tốt hơn nhiều về việc tích hợp Scriptaculous vào trong GWT là bằng cách bao bọc chức năng của các hiệu ứng Scriptaculous như là một widget GWT tùy chỉnh. Điều này chính xác với những gì mà Alexei Sokolov đã thực hiện trong Thư viện thành phần GWT (xem Tài nguyên).

Bây giờ tôi đang làm tất cả với ứng dụng bản tin dự báo thời tiết, tôi sẽ xem xét một số lợi ích và nhược điểm của việc phát triển Web với GWT.


Tại sao lại sử dụng GWT?

Trái ngược với những gì bạn có thể mong đợi, các ứng dụng GWT rõ ràng không giống như Web. Về cơ bản GWT khai thác trình duyệt như một môi trường thời gian chạy cho các ứng dụng GUI đơn giản và kết quả là khá gần với những gì bạn có thể phát triển bằng Morfik, OpenLaszlo, Flash hoặc thậm chí, với các ứng dụng Web thông thường. Theo đó, GWT thích hợp tốt nhất cho các ứng dụng Web có thể tồn tại như một GUI của Ajax phong phú trên một trang duy nhất. Nó có lẽ không phải là sự trùng hợp ngẫu nhiên mà điều này tiêu biểu cho một số các bản phát hành beta gần đây của Google, như các ứng dụng Calendar (Lịch biểu) và các Spreadsheet (Bảng tính) của nó. Đây là những ứng dụng đáng kể, nhưng bạn không thể giải quyết tất cả các tình huống nghiệp vụ khi sử dụng cách tiếp cận này. Đa số các ứng dụng Web hoàn toàn phù hợp với mô hình giữa-trang và Ajax cho phép các mô hình mẫu tương tác phong phú hơn được sử dụng ở nơi cần thiết. GWT không chạy tốt với các ứng dụng giữa-trang truyền thống. Mặc dù nó có khả năng kết hợp các widget GWT với đầu vào dạng HTML thông thường, trạng thái của một widget GWT được ngăn chặn khỏi phần còn lại của trang. Ví dụ, không có cách nào đơn giản hơn để gửi các giá trị được lựa chọn từ một widget Tree (Cây) GWT như một phần của một dạng chính quy.

Cấp giấy phép

Các thư viện thời gian chạy của GWT được cấp phép theo Giấy phép Apache 2.0 và bạn có thể sử dụng GWT miễn phí để tạo ra các ứng dụng thương mại. Tuy nhiên, chuỗi công cụ GWT được cung cấp chỉ dưới dạng nhị phân và không được phép sửa đổi. Điều này bao gồm trình biên dịch Java-sang-JavaScript. Điều này có nghĩa rằng bất kỳ lỗi nào trong JavaScript được tạo ra của bạn nằm ngoài tầm kiểm soát của bạn. Một vấn đề cụ thể là sự tin cậy của GWT vào việc phát hiện tác nhân-người dùng: Mỗi bản phát hành của một trình duyệt mới đòi hỏi một sự cập nhật cho bộ công cụ GWT để cung cấp sự hỗ trợ.

Nếu bạn quyết định sử dụng ứng dụng GWT trong một môi trường ứng dụng J2EE, thì thiết kế của GWT sẽ làm cho việc tích hợp tương đối đơn giản. Trong kịch bản này, các dịch vụ GWT nên được nghĩ đến như giống với các Action trong Struts -- một tầng mỏng ở giữa đơn giản ủy quyền cho yêu cầu Web trong các cuộc gọi theo logic nghiệp vụ ở tầng sau. Vì các dịch vụ GWT chỉ là HTTP servlets, nên chúng có thể dễ dàng được tích hợp vào Struts hoặc SpringMVC chẳng hạn và được đặt phía sau các bộ lọc xác thực.

GWT có một vài lỗi đáng kể. Đầu tiên là thiếu dự phòng cho sự xuống cấp từ từ. Cách thực hành tốt nhất trong phát triển ứng dụng Web hiện đại là tạo các trang hoạt động mà không cần JavaScript và sau đó sử dụng nó ở nơi có sẵn để làm đẹp và bổ sung thêm hành vi. Trong GWT, nếu JavaScript không có sẵn, bạn sẽ không nhận được bất kỳ UI nào hết. Đối với một vài lớp của ứng dụng Web, đây là trình ngắt thỏa thuận trực tiếp (deal-breaker). Tính quốc tế hóa cũng là một vấn đề lớn đối với GWT. Vì các lớp Java của khách hàng GWT chạy trong trình duyệt, chúng không có quyền truy cập vào các đặc tính hoặc các bó tài nguyên để lấy các chuỗi bản địa hoá trong thời gian chạy. Một cách giải quyết khác phức tạp có sẵn đòi hỏi một lớp con của mỗi lớp phía máy khách được tạo ra cho mỗi vị trí (xem Tài nguyên), nhưng các kỹ sư của GWT đang tiếp tục thực hiện một giải pháp khả thi hơn.

Trường hợp cho việc tạo mã

Có lẽ vấn đề gây tranh cãi nhất trong kiến trúc của GWT là chuyển đổi sang ngôn ngữ Java cho mã phía máy chủ. Một số người ủng hộ GWT đề nghị rằng việc viết mã phía máy khách trong ngôn ngữ Java về bản chất là thích hợp hơn việc viết JavaScript. Đây không phải là một khung nhìn chung cho tất cả và nhiều người viết mã JavaScript đã miễn cưỡng hy sinh tính linh hoạt và tính diễn cảm của ngôn ngữ của họ cho sự cố gắng phát triển Java nặng nề. Một tình huống ở đó sự thay thế của mã Java cho JavaScript có thể đang hấp dẫn trong một nhóm phát triển thiếu các nhà phát triển Web có kinh nghiệm. Tuy nhiên, nếu nhóm đó đang chuyển sang phát triển Ajax, thì cách làm tốt hơn là thuê các lập trình viên JavaScript có kỹ năng hơn là dựa vào những người viết mã Java để làm ra mã JavaScript khó hiểu với một công cụ độc quyền không quen thuộc. Các lỗi không tránh được do các sự rò rỉ thông tin gây ra theo sự trừu tượng mà GWT trải rộng qua JavaScript, HTTP và HTML và các lập trình viên Web thiếu kinh nghiệm mất nhiều thời gian theo dõi chúng. Theo nhà phát triển và blogger Dimitri Glazkov đưa ra nó, "Nếu bạn không thể xử lý JavaScript, bạn không nên viết mã cho các ứng dụng Web. HTML, CSS và JavaScript là ba điều kiện tiên quyết cho công việc này". (Xem Tài nguyên).

Một số người biện luận rằng việc viết mã Java ít bị lỗi hơn lập trình JavaScript, nhờ định kiểu tĩnh và kiểm tra trong lúc dịch. Đây là một lý lẽ khá sai lầm. Có thể viết mã kém trong bất kỳ ngôn ngữ nào và nhiều ứng dụng Java có lỗi đang có mặt để chứng minh điều đó. Bạn cũng phụ thuộc vào việc tạo mã GWT không có lỗi. Tuy nhiên, việc kiểm tra cú pháp ngoại tuyến và xác nhận hợp lệ mã phía máy khách chắc chắn có thể có ích. Nó có sẵn cho JavaScript dưới dạng JSLint của Douglas Crockford (xem Tài nguyên). Mặc dù GWT trội hơn dưới dạng thử nghiệm đơn vị, cung cấp tích hợp JUnit cho mã phía khách. Sự hỗ trợ thử nghiệm đơn vị là một lĩnh vực ở đó JavaScript vẫn còn thiếu nhiều.

Trong việc phát triển ứng dụng Bản tin dự báo thời tiết, các trường hợp hấp dẫn nhất mà tôi đã tìm thấy cho mã Java phía máy khách là khả năng chia sẻ cùng một lớp xác nhận hợp lệ giữa hai tầng. Điều này rõ ràng sẽ tiết kiệm các nỗ lực phát triển. Điều giống như vậy cũng được áp dụng cho bất kỳ các lớp nào được chuyển qua RPC; bạn chỉ cần mã hóa chúng một lần và bạn có thể sử dụng cả mã phía máy khách và phía máy chủ cho chúng. Thật không may, sự trừu tượng có khe hở: trong trình xác nhận hợp lệ (validator) mã bưu điện của tôi chẳng hạn, tôi có khả năng sử dụng các biểu thức chính quy để thực hiện việc kiểm tra. Tuy nhiên, GWT không triển khai thực hiện phương thức String.match(). Ngay cả khi nó đã thực hiện, các biểu thức chính quy trong GWT có các sự khác biệt về cú pháp khi được triển khai như mã máy khách và máy chủ. Điều này là do sự tin cậy của GWT theo cơ chế biểu thức chính quy (regexp) ở dưới môi trường máy chủ và một ví dụ về sự cố mà các sự trừu tượng hóa chưa hoàn thiện có thể đưa bạn vào sự cố đó.

Một thắng lợi lớn mà GWT giành được là cơ chế RPC của nó và sự tuần tự hóa kèm theo của các đối tượng giữa mã Java và JavaScript. Điều này loại bỏ rất nhiều cố gắng để hoàn thành công việc mà bạn thấy trong ứng dụng Ajax bậc trung. Mặc dù không phải không có tiền lệ. Nếu bạn muốn chức năng này không có phần còn lại của GWT, sau đó DWR, đưa ra RPC với đối tượng sắp xếp theo thứ tự đến và từ mã Java sang JavaScript, rất đáng xem xét (xem Tài nguyên).

GWT cũng làm một công việc tốt về việc trừu tượng hóa ngoài một số khía cạnh-mức thấp của sự phát triển ứng dụng Ajax, chẳng hạn như qua sự không tương thích của trình duyệt chéo nhau, mô hình sự kiện DOM và thực hiện các cuộc gọi Ajax. Nhưng các bộ công cụ JavaScript hiện đại như Yahoo! UI Library, Dojo và MochiKit tất cả cung cấp một mức độ trừu tượng tương tự mà không cần phải sử dụng đến việc tạo mã. Hơn nữa, tất cả các bộ công cụ này là mã nguồn mở, nên bạn có thể tùy biến chúng cho phù hợp với nhu cầu hoặc sửa chữa các lỗi phát sinh. Điều này không thể có với hộp đen của GWT (xem phần bên Việc cấp phép).


Kết luận

GWT là một khung công tác toàn diện cung cấp rất nhiều chức năng có ích. Tuy nhiên, GWT là một cái gì đó của cách tiếp cận tất cả-hoặc-không, tập trung vào một dịch vụ nhỏ tương đối trong thị trường phát triển ứng dụng Web. Tôi hy vọng chuyến đi ngắn này đã cho bạn một cảm giác về các khả năng của GWT và các hạn chế của nó. Mặc dù chắc chắn nó sẽ không phù hợp với nhu cầu của mọi người, nhưng GWT vẫn là một thành tựu kỹ thuật lớn và xứng đáng được xem xét nghiêm chỉnh khi bạn thiết kế ứng dụng Ajax tiếp theo của mình. GWT có bề rộng và sâu hơn những thứ mà tôi đã có thể khám phá ở đây, do đó hãy làm đọc tài liệu của Google để biết nhiều hơn hoặc tham gia cuộc thảo luận trên diễn đàn của nhà phát triển GWT (xem Tài nguyên).


Tải về

Mô tảTênKích thước
GWT Weather Reporter applicationj-ajax4-gwt-weather.zip2.1KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

  • GWT SDK: Tải về bộ công cụ Google Web SDK.
  • script.aculo.us: Một thư viện JavaScript cho các hiệu ứng trực quan.
  • Thư viện thành phần GWT: Một bộ sưu tập của các thành phần của bên thứ ba được tạo với GWT, bao gồm các trình bao cho Scriptaculous.
  • JSLint: Trình kiểm tra JavaScript: JSLint cung cấp việc xác thực hợp lệ cú pháp và cấu trúc cho mã JavaScript truyền thống.

Thảo luận

  • Diễn đàn nhà phát triển GWT: Nhóm thảo luận thường được các nhà phát triển của GWT lui tới, cũng dùng làm một nguồn đặc biệt của tài liệu hướng dẫn GWT.
  • developerWorks blogs: Dành tâm trí cho cộng đồng developerWorks.

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Công nghệ Java, Nguồn mở
ArticleID=452529
ArticleTitle=Ajax cho các nhà phát triển Java: Khảo sát bộ công cụ Web của Google
publish-date=12042009