2. Môi trường phát triển ứng dụng 1 Tổng quan
2.6 Các biến môi trường
Tất cả các thuộc tính của hệ thống và biến môi trường là riêng cho mỗi ứng dụng của bạn. Thiết lập một thuộc tính hệ thống chỉ ảnh hưởng đến giá trị của thuộc tính đó trong ứng dụng đó mà không ảnh hưởng đến toàn bộ JVM.Bạn có thể thiết lập thuộc tính hệ thống và biến môi trường cho ứng dụng trong mô tả triển khai.
App Engine họ thuộc tính hệ thống mà để xác định môi trường thực thi:
• Com.google.appengine.runtime.environment là "Production" khi chạy trên App Engine, và "Development" khi đang chạy trong máy chủ phát triển.
• Com.google.appengine.runtime.version là ID phiên bản của môi trường thực thi, như "1.3.0".
Ngoài việc sử dụng System.getProperty (), bạn có thể truy cập hệ thống bằng cách sử dụng các type-safe API.
Ví dụ:
if (SystemProperty.environment.value() ==
SystemProperty.Environment.Value.Production) { // The app is running on App Engine...
}
App Engine cũng đặt các thuộc tính hệ thống sau khi nó khởi tạo các JVM trên một máy chủ ứng dụng: • file.separator • path.separator • line.separator • java.version • java.vendor • java.vendor.url • java.class.version
• java.specification.version • java.specification.vendor • java.specification.name • java.vm.vendor • java.vm.name • java.vm.specification.version • java.vm.specification.vendor • java.vm.specification.name • user.dir 2.7 Các dịch vụ đi kèm 2.7.1 DataStore
GAE dùng 2 API để thực hiện việc lưu trữ dữ liêu : JDO (Java data object) và JPA (Java persistence API). JPA rất phổ biến trong các framework EJB (Spring,Struts…)
Một dữ liệu đối tượng trong Datastore App Engine được biết đến như một thực thể. Một thực thể có một hoặc nhiều thuộc tính, giá trị có tên của một trong số các loại dữ liệu, bao gồm các số nguyên, phức, xâu, ngày tháng v.v ..Mỗi thực thể cũng có một khóa để phận biệt các thực thể. Các khóa có kiểu và một số ID do Datastore cung cấp. ID cũng có thể là một chuỗi cung cấp bởi ứng dụng.
Một ứng dụng có thể lấy một thực thể từ Datastore bằng cách sử dụng khóa của nó, hoặc bằng cách thực hiện một truy vấn phù hợp với thuộc tính của thực thể. Một truy vấn có thể trở lại không hay nhiều thực thể, và có thể trả lại kết quả được sắp xếp theo thuộc tính. Một truy vấn cũng có thể hạn chế số lượng kết quả trả về bởi Datastore để bảo tồn bộ nhớ và thời gian chạy.Không giống như cơ sở dữ liệu quan hệ, các App Engine Datastore không đòi hỏi rằng tất cả các thực thể của một loại nhất định có properties tương tự nhau. Các ứng dụng có thể chỉ định và thi hành mô hình dữ liệu của nó bằng cách sử dụng thư viện bao gồm với SDK, hoặc mã riêng của mình.
Một thuộc tính có thể có một hoặc nhiều giá trị. Một thuộc tính với nhiều giá trị có thể có giá trị của các loại hỗn hợp. Một truy vấn trên một thuộc tính có giá trị nhiều kiểm tra xem liệu các giá trị đáp ứng các tiêu chuẩn truy vấn. Điều này làm cho thuộc tính đó hữu ích cho việc thử nghiệm tính thành viên.
Mỗi câu truy vấn sẽ được thực hiện trên mọi thực thể của kiểu đang truy vấn. Nó sẽ xác định một bộ lọc trên các thuộc tính và khóa của các thực thể loại đó. Một thực thể sẽ được trả về nếu nó thỏa mãn bộ lọc.Mỗi truy vấn Datastore sử dụng một chỉ mục, một bảng chứa các kết quả cho truy vấn theo thứ tự mong muốn. App Engine một ứng dụng xác định các chỉ số của nó trong tập tin cấu hình.Các máy chủ web sẽ tự động phát triển thêm các đề xuất đến tập tin như nó gặp các truy vấn mà chưa được chỉnh. Bạn có thể điều chỉnh chỉ số bằng tay bằng cách chỉnh sửa tập tin trước khi tải các ứng dụng. Khi ứng dụng làm thay đổi các thực thể Datastore, các Datastore cập nhật các chỉ số với kết quả chính xác. Khi ứng dụng thực hiện một truy vấn, Datastore
2.7.2 Memcahe
Những ứng dụng web yêu cầu hiệu năng cao luôn sử dụng một bộ nhớ tạm thời có tốc độ nhanh hơn nhiều lần bộ nhớ tĩnh. Memcahe là dịch vụ nhắm đến mục đích này. GEA sử dụng package net.sf.jsr107 để thao tác với memcahe.
Ví dụ : import java.util.Collections; import net.sf.jsr107.Cache; import net.sf.jsr107.CacheException; import net.sf.jsr107.CacheFactory; import net.sf.jsr107.CacheManager; // ... Cache cache;
// Xác định thời gian cache hết hạn Map props = new HashMap();
props.put(GCacheFactory.EXPIRATION_DELTA, 3600); try {
CacheFactory cacheFactory =
CacheManager.getInstance().getCacheFactory(); // Factory design pattern cache = cacheFactory.createCache(props);
String key; byte[] value;
// Lưu dữ liệu vào cache cache.put(key, value); // Lấy dữ liệu từ cache
value = (byte[]) cache.get(key); } catch (CacheException e) {
// ... } }
Các hằng số dùng xác định thời gian hết hạn của cache:
• GCacheFactory.EXPIRATION_DELTA: xác định thời gian hết hạn tương đối tính theo thời điểm khởi tạo, tính theo giây.
• GCacheFactory.EXPIRATION_DELTA_MILLIS: xác định thời gian hết hạn tương đối tính theo thời điểm khởi tạo, tính theo mili giây.
• GCacheFactory.EXPIRATION: xác định thời điểm hết hạn là một thời điểm cố định kiểu java.util.Date.
Ngoài ra ta còn có thể xác định xem nên xử lý thế nào khi thêm cache Map props = new HashMap();
props.put(MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT, true);
• MemcacheService.SetPolicy.SET_ALWAYS: thêm giá trị nếu không có giá trị với khóa đó tồn tại, thay thế một giá trị hiện tại nếu một giá trị với khóa đó đã tồn tại, đây là mặc định.
• MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT: thêm giá trị nếu không có giá trị với khóa đó tồn tại, không phải làm gì nếu khóa đã tồn tại. • MemcacheService.SetPolicy.REPLACE_ONLY_IF_PRESENT: không phải làm
gì nếu không có giá trị với khóa đó tồn tại, thay thế một giá trị hiện tại nếu một giá trị với khóa đã tồn tại.
Ta có thể lấy thông tin về cache như ví dụ sau :
import net.sf.jsr107.CacheStatistics; // ...
CacheStatistics stats = cache.getCacheStatistics(); int hits = stats.getCacheHits();
int misses = stats.getCacheMisses();
• Đặc điểm :
Nhanh hơn lưu trữ
Lưu dữ liệu lên bộ nhớ thay vì lên đĩa Tổ chức key-value pair
Kế thừa Jcahe inteface. • Giới hạn :
Dung lượng lưu trữ không vượt qua 1MB.
Loại tài
nguyên Giới hạn hàngĐịnh mức miễn phí Định mức trả phí ngày Mức tối đa Giới hạn hàngngày Mức tối đa
Gọi API memcahe 8.600.000 lời gọi 48.000 lần/phút 96.000.000 lời gọi 108.000 lần/phút Gửi dữ liệu
đến API 10 gigabyte 50megabyte/phút 60 gigabyte 128megabyte/phút
Lấy dữ liệu từ API 50 gigabyte 284 megabyte/phút 315 gigabyte 640 megabyte/phút Bảng định mức dịch vụ Memcache
2.7.3 URL fetch
Ứng dụng GAE có thể yêu cầu và nhận thông tin từ các dịch vụ khác thông qua dịch vụ URL fecth. Dùng URL fetch, ứng dụng có thể gửi yêu cầu HTTP và HTTPS rồi nhận kết quả trả về. URL fetch xây dựng trên cơ sở hạ tầng của Google đảm bảo khả năng mở rộng hiệu quả.
Ta sử dụng package java.net khi muốn dùng dịch vụ này.
import java.net.MalformedURLException; import java.net.URL; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; // ... try {
URL url = new URL("http://www.example.com/atom.xml"); BufferedReader reader = new BufferedReader(new
InputStreamReader(url.openStream())); String line;
while ((line = reader.readLine()) != null) { // ... } reader.close(); } catch (MalformedURLException e) { // ... } catch (IOException e) { // ... }
Một ứng dụng có thể lấy một URL bằng cách sử dụng HTTP (bình thường) hoặc HTTPS (an toàn). URL quy định cụ thể cách thức sử dụng: http:// ... hoặc https ://...
URL có thể sử dụng bất kỳ cổng trong phạm vi sau đây: 80-90, 440-450, 1.024- 65.535. Nếu cổng không được đề cập trong URL, cổng mặc đinh sẽ được sử dụng : http:// ... là cổng 80, https ://... là cổng 443.
Việc lấy có thể sử dụng bất kỳ một trong các phương thức HTTP sau: GET (phương thức thông thường khi yêu cầu các trang web và dữ liệu), POST (dùng khi truyền các form web), PUT, HEAD, và DELETE.
Các thủ tục của dịch vụ URL Fetch sử dụng của một proxy HTTP/1.1 để lấy kết quả. Để ngăn chặn một ứng dụng có thể tạo ra một vòng đệ quy bất tận các yêu cầu, một yêu cầu không được phép để lấy yêu cầu URL của đến chính nó. Tuy nhiên dịch vụ có thể gây ra một đệ quy bất tận bằng các cách khác khác, do đó, phải thận
trọng nếu cho phép người dùng ứng dụng có thể sử dụng dịch vụ một cách tùy ý. Ví dụ như khi người dùng được phép nhập URL để lấy nội dung, khi đó nếu bản thân địa chỉ URL đó đệ quy, yêu cầu cũng sẽ bị đệ quy.
Bạn có thể đặt một thời hạn thực thịcho một yêu cầu, khoảng thời gian dịch vụ sẽ chờ đợi phản hồi. Theo mặc định, thời hạn đó là 5 giây. Thời hạn tối đa là 10 giây. Khi sử dụng URLConnection, dịch vụ sử dụng thời gian chờ kết nối (setConnectTimeout ()) cộng với thời gian chờ đọc (setReadTimeout ()) là thời hạn cuối cùng.
Các dịch vụ URL Fetch hỗ trợ cả các yêu cầu đồng bộ và yêu cầu không đồng bộ. Với một yêu cầu đồng bộ, khi dùng API để lấy một URL, đoạn mã sẽ đợi cho đến khi có kết quả trả về, sau đó mới đưa kết quả đó vào ứng dụng và thực hiện tiếp. Các ứng dụng có thể xác định thời gian tối đã khi chờ đợi kết quả. Nếu thời gian chờ đợi tối đa là vượt quá, một ngoại lệ sẽ được sinh ra.
Một yêu cầu không đồng bộ với dịch vụ URL Fetch bắt đầu khi được yêu cầu, sau đó trả về chương tình một đối tượng ngay lập tức. Ứng dụng có thể thực hiện các nhiệm vụ khác trong khi nộ dung trả về đang được tải xuống.Khi ứng dụng cần kết quả, nó gọi một phương thức trên đối tượng, sau đó đợi yêu cầu hoàn thành nếu cần, sau đó trả về kết quả. Các ứng dụng có thể có đến 10 đồng thời không đồng bộ URL Fetch. Nếu bất kỳ yêu cầu URL Fetch đang chờ mà phần mã xử lý kết thúc, máy chủ ứng dụng đợi yêu cầu kết thúc hoặc là đợi đến thời hạn tối đa trước khi trả lại kết quả cho người dùng.
Trong Java, giao diện không đồng bộ chỉ có thể sử dụng trực tiếp qua các API cấp thấp. Các phương thức fetchAsync() trả về một <HTTPResponse> java.util.concurrent.Future.
• Đặc điểm:
Truy cập các URL bên ngoài thông qua HTTP và HTTPS
Có thể ta đã sử dụng trong các ứng dụng Java khác, ở đây khả năng của nó được tăng thêm
• Giới hạn:
Dung lượng yêu cầu và thông tin trả về không được quá 1MB.
Loại tài
nguyên Giới hạn hàngĐịnh mức miễn phí Định mức trả phí ngày Mức tối đa Giới hạn hàngngày Mức tối đa
Gọi API 657.000 lời gọi 3.000 lần/phút 46.000.000 lời gọi
32.000 lần/phút
Dữ liệu gửi đi 4 gigabyte 22
megabyte/phút 1046 gigabyte 740megabyte/phút
Dữ liệu nhận
vê 4 gigabyte 22megabyte/phút 1046 gigabyte 740megabyte/phút
2.7.4 Mail
Ứng dụng có thể gửi mail dưới danh nghĩa của admin hoặc của người dùng với một tài khoản Google. Ứng dụng gửi mail bằng dịch vụ Mail và nhận mail dưới dang HTTP request tạo ra bởi GAE và chuyển đến ứng dụng. Admin có thể kiểm tra mail tại địa chỉ “string@appid.appspotmail.com” . import java.util.Properties; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; // ...
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null); String msgBody = "...";
try {
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("admin@example.com", "Example.com Admin"));
msg.addRecipient(Message.RecipientType.TO,
new InternetAddress("user@example.com", "Mr. User"));
msg.setSubject("Your Example.com account has been activated"); msg.setText(msgBody); Transport.send(msg); } catch (AddressException e) { // ... } catch (MessagingException e) { // ... }
Bạn có thể gửi tin nhắn với file đính kèm tập tin, hay với một đoạn HTML, cùng với nội dung, sử dụng tin nhắn đa phần (MimeMultipart). Bạn tạo một đối tượng MimeMultipart để chứa các phần, sau đó tạo ra một đối tượng MimeBodyPart cho mỗi tập tin đính kèm hay các phần phụ rồi thêm nó vào MimeMultipart. Cuối cùng, gắn các MimeMultipart vào nội dung của MimeMessage.
import javax.activation.DataHandler; import javax.mail.Multipart; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; // ... String htmlBody; // ... byte[] attachmentData; // ... Multipart mp = new MimeMultipart();
MimeBodyPart htmlPart = new MimeBodyPart(); htmlPart.setContent(htmlBody, "text/html"); mp.addBodyPart(htmlPart);
MimeBodyPart attachment = new MimeBodyPart(); attachment.setFileName("manual.pdf");
attachment.setContent(attachmentData, "application/pdf"); mp.addBodyPart(attachment);
message.setContent(mp);
• Đặc điểm:
Gửi mail dưới danh nghĩa của admin hoặc người dùng của Google. Ứng dụng không thể nhận mail (mail chuyển thành HTTP request). • Giới hạn:
Tổng dung lượng một mail bao gồm cả phần đính kèm không quá 1MB. Nếu admin là người nhận dung lượng mail không được quá 16KB.
Loại tài nguyên Định mức miễn phí Định mức trả phí
Giới hạn hàng
ngày Mức tối đa Giới hạn hàng ngày Mức tối đa
Gọi API 7.000 lời gọi 32 lần/phút 1.700.000 lời gọi 4.900 lần/phút
Số mail nhận 2.000 mail 8 mail/phút 2.000 mails miễn phí; 7.400.000
5.100 mail/phút
Dữ liệu thân mail gửi đi
60 megabyte 340 kilobyte/phút 29 gigabyte 84 megabyte/phút
Số phần đính kèm có thể gửi
2.000 phần 8 phần/phút 2.900.000 phần 8.100 phần/phút
Dung lượng
phần đính kèm 100 megabyte 560 kylobyte/phút 100 gigabyte 300 megabyte/phút
Bảng định mức dịch vụ Mail
Ứng dụng có thể gửi và nhận thông điệp từ các dịch vụ XMPP (như google talk). Một ứng dụng có thể gửi và nhận thông điệp, gửi yêu cầu chat, yêu cầu thông tin chat. import com.google.appengine.api.xmpp.JID; import com.google.appengine.api.xmpp.Message; import com.google.appengine.api.xmpp.MessageBuilder; import com.google.appengine.api.xmpp.SendResponse; import com.google.appengine.api.xmpp.XMPPService; import com.google.appengine.api.xmpp.XMPPServiceFactory; // ...
JID jid = new JID("example@gmail.com");
String msgBody = "Someone has sent you a gift on Example.com. To view: http://example.com/gifts/";
Message msg = new MessageBuilder() .withRecipientJids(jid)
.withBody(msgBody) .build();
boolean messageSent = false;
XMPPService xmpp = XMPPServiceFactory.getXMPPService(); if (xmpp.getPresence(jid).isAvailable()) {
SendResponse status = xmpp.sendMessage(msg); messageSent = (status.getStatusMap().get(jid) == SendResponse.Status.SUCCESS);
}
if (!messageSent) {
// Send an email message instead... }
Người sử dụng dịch vụ chat XMPP có thể gửi tin nhắn chat đến các ứng dụng App Engine. Đối với một ứng dụng chat để nhận được tin nhắn, dịch vụ tin nhắn XMPP phải được kích hoạt trong cấu hình của ứng dụng.
Khi dịch vụ XMPP được kích hoạt, mỗi lần App Engine nhận được một tin nhắn chat cho ứng dụng này, nó thực hiện một yêu cầu HTTP POST đến URL sau đây:
/_ah/xmpp/message/chat/
Để xử lý các thông điệp đến, bạn chỉ cần tạo một hàm xử lý yêu cầu POST đến URL này. Đường dẫn URL này bị hạn chế chỉ cho admin xử lý. Các dịch vụ XMPP kết nối vào các ứng dụng với "tư cách" admin cho mục đích truy cập vào đường dẫn URL này. Ta có thể cấu hình sự hạn chế đến đường dẫn này một cách rõ ràng nếu muốn, nhưng điều này là không cần thiết. Chỉ có dịch vụ XMPP và người dùng đã <inbound-services>
<service>xmpp_message</service> </inbound-services>
được chứng thực là các admin sử dụng Tài khoản Google là có thể truy cập vào đường dẫn URL này.
Dữ liệu yêu cầu POST thể hiện các tin nhắn như một thông điệp MIME nhiều phần dữ liệu (theo chuẩn RFC 2388).
Mỗi phần là tham số POST có định danh:
• from, địa chỉ của người gửi tin nhắn
• to, địa chỉ của người nhận như mô tả của người gửi • body, nội dung của tin nhắn
• stanze, các XMPP ở dạng XML ban đầu của nó
Một ứng dụng có thể nhận tin nhắn của hai loại XMPP: "chat" và "bình thường". Nếu App Engine nhận được một tin nhắn XMPP của một loại không được hỗ trợ, tin nhắn được bỏ qua và xử lý yêu cầu không được gọi.
Các Java Servlet API không cung cấp một cách để phân tích cú pháp dữ liệu POST trực tiếp. Các API XMPP bao gồm một phương thức helper để phân tích dữ liệu này từ các đối tượng HttpServletRequest. Ví dụ sau minh họa cách sử dụng lớp này helper để phân tích đến XMPP tin nhắn.
import java.io.IOException; import javax.servlet.http.*; import com.google.appengine.api.xmpp.JID; import com.google.appengine.api.xmpp.Message; import com.google.appengine.api.xmpp.XMPPService; import com.google.appengine.api.xmpp.XMPPServiceFactory; @SuppressWarnings("serial")
public class XMPPReceiverServlet extends HttpServlet {
public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
XMPPService xmpp = XMPPServiceFactory.getXMPPService(); Message message = xmpp.parseMessage(req);
JID fromJid = message.getFromJid(); String body = message.getBody(); // ...
}} }
Để chỉ servlet này đến đường dẫn URL XMPP, đặt các phần sau đây trong tập tin web.xml của bạn, bên trong các yếu tố <web-app>:
<servlet>
</servlet>
<servlet-mapping>
<servlet-name>xmppreceiver</servlet-name>
<url-pattern>/_ah/xmpp/message/chat/</url-pattern> </servlet-mapping>
Một ứng dụng có thể gửi và nhận tin nhắn bằng cách sử dụng một số kiểu địa chỉ, còn gọi là "JIDs." Một kiểu địa chỉ sử dụng ID của ứng dụng và tên miền