Поддержка J2ME для вызова сервера довольно проста и низка. Вам не только приходится иметь дело с HTTP-соединением на низком уровне, но и нет поддержки высокого уровня для файлов cookie, аутентификации или удаленного вызова процедур. Поэтому, если вы хотите передать объект на сервер и получить ответ, вам необходимо выяснить, как это сделать. XML поверх HTTP является одним из решений, но представляет свои собственные проблемы, такие как сериализация и десериализация объектов, не говоря уже о более высоком сетевом трафике из-за метаданных, содержащихся в XML. JAX Binding определенно не поддерживается в J2ME-land, поэтому вам придется использовать SAX-парсер.
В предыдущих проектах я играл с простым способом предоставления услуг через JSP, которые принимают и получают текст с разделителями. Идея состоит в том, чтобы реализовать собственную простую сериализацию и десериализацию простых объектов, позволяющую вам совершать простые вызовы на сервер и получать простые ответы. Я целенаправленно использовал слово «простой» в последнем предложении четыре раза, чтобы убедить вас в том, что серверные вызовы должны быть простыми.
Возьмем, к примеру, приложение J2ME, которое отслеживает местоположение GPS. Чтобы отправить местоположение пользователя, он может просто отправить строку текста, например:
006.574438 | 045.453345 | 11022344843373
Что это значит?
долгота | широта | отметка времени
Сериализация и десериализация данных ОЧЕНЬ проста с использованием StringTokenizer (эм, которого нет в J2ME, так что посмотрим позже!). И сервер может ответить просто ОК или он может воспользоваться возможностью обновить клиента с некоторой информацией о трафике:
M4 | 4 | 6 | 20 | Авария,
что означает:
дорога | от перекрестка | соединить | сколько минут задержки | причина
Большинство запросов к серверу действительно могут быть такими простыми, особенно когда они задействованы в приложениях J2ME, которые, как правило, сами по себе относительно просты.
Итак, вышеизложенное представляет несколько вопросов … Насколько безопасны данные и как вы узнаете, от кого приходит обновление? Ну, данные должны быть отправлены через SSL, чтобы обеспечить их безопасность, и если данные отправляются через аутентифицированный сеанс, то сервер знает, кто является вошедшим в систему пользователем. Но чтобы войти в систему и запустить сеанс, вам нужны две вещи, а именно: куки (для передачи идентификатора сеанса между клиентом и сервером) и некоторая форма аутентификации. Файлы cookie в J2ME не слишком просты в обращении, поскольку нет встроенного API для обработки их на высоком уровне. Вы можете установить cookie в заголовке запроса, но сложнее всего сохранить cookie из ответов. Я реализовал элементарную обработку файлов cookie, отправив ответ методу, который проверяет наличие любых заголовков файлов cookie и добавляет их в кэш, а также удаляет записи с истекшим сроком действия.Когда запрос отправляется, я вызываю метод, который добавляет все соответствующие файлы cookie в заголовок запроса. Я не реализовалRfC для файлов cookie и не обрабатывать различия между Cookie и Cookie2. На самом деле, я даже не зашёл так далеко, чтобы проверить путь куки, прежде чем отправить его на сервер, потому что в моей среде это даже не актуально. Надлежащая реализация cookie должна была бы делать эти вещи и даже больше, и, возможно, однажды такая библиотека будет существовать. Мне удалось найти это, которое относится к проекту JCookie sourceforge и J2ME, но проверить его сайт я не смог найти что-нибудь, что будет работать с J2ME.
HTTP-аутентификация. Первоначально я обрабатывал ее, добавляя заголовок запроса «авторизация» и применяя к нему строку базовой аутентификации в кодировке Base64. Это вызвало его собственные проблемы, потому что J2ME не имеет встроенного кодера Base64. К счастью, работает класс org.apache.common.codecs.binary.Base64 (который я взял из библиотеки Apache Codecs 1.3. Это зависит от следующих классов, которые есть в распространяемом JAR, а также J2ME-совместимых: BinaryDecoder, BinaryEncoder, Decoder, DecoderException , Encoder, EncoderException, все они находятся в пакете org.apache.commons.codec.
Я столкнулся с другой проблемой, когда хотел, чтобы мое веб-приложение для веб-сайта совпадало с веб-приложением для моих служб. А именно, для сервисов я хотел использовать базовую аутентификацию, а для веб-сайта я хотел использовать форму входа в систему. Спецификация сервлета не позволяет вам определять более одной конфигурации входа в систему для каждого веб-приложения! Поэтому класс J2ME, который я написал для доступа к сервисам (ServerCaller), должен был быть расширен, но это было несложно. Когда веб-приложение, использующее форму входа в систему, требует от вас аутентификации, оно просто возвращает форму входа в систему вместо ожидаемого ответа. Если вы анализируете ответ и проверяете, является ли это вашей формой для входа, а затем просто отправляете эту форму с вашими именем пользователя и паролем, сервер затем регистрирует вас и возвращает код 302, говорящий о том, чтобы вы снова вызывали исходную страницу. Предполагая, что вы ввели правильные учетные данные,все работает Так что мой класс возвращался в себя, если он обнаружил форму входа в систему, и этого было достаточно, чтобы заставить его работать прозрачно.
Следующая проблема заключалась в разборе ответов для их десериализации. Для этого я создал очень простой StringTokenizer, поскольку ни CLDC, ни MIDP не дают его вам ? Реализация приведена ниже.
Кодирование и декодирование URL также важно, поскольку параметры запроса (для GET это строка запроса, для POST тело запроса). К счастью, есть несколько вариантов. У Catalina есть один в своем пакете утилит, который почти работает для J2ME. Поэтому я быстро исправил его, чтобы удалить зависимость от java.util.BitSet, который также не существует в J2ME. .
последняя проблема , я связан с отрывов от просьбы HttpConnection, которые вы можете прочитать больше о здесь .
Итак, наконец, немного кода! Магический класс — это ServerCaller, который вы можете скачать ниже. Это абстрактно, и чтобы использовать его, вам просто нужно расширить его, например:
package uk.co.maxant.cam.c;
import java.util.Vector;
import uk.co.maxant.cam.m.MasterData;
import uk.co.maxant.j2me.core.CredentialsProvider;
import uk.co.maxant.j2me.core.ServerCaller;
import uk.co.maxant.j2me.core.StringTokenizer;
/**
* calls the server to get master data like roles and the version of the latest available download.
*/
public abstract class MasterDataGetter extends ServerCaller implements ServerCaller.Listener {
private ExceptionHandler eh;
public MasterDataGetter(CredentialsProvider credentialsProvider, ExceptionHandler eh) {
super(credentialsProvider, Constants.getBaseUrl(), "ota/getMasterData.jsp");
this.eh = eh;
setListener(this);
}
public void callServer(){
properties.clear();
properties.put("installUid", String.valueOf(InstallHelper.getInstallationUid(eh)));
doCallAsync();
}
protected void handleSuccess(String str) {
StringTokenizer lines = new StringTokenizer(str, "|");
String version = lines.nextToken().trim();
String sessionId = lines.nextToken().trim();
String screenName = lines.nextToken().trim();
String roles = lines.nextToken().trim();
StringTokenizer rs = new StringTokenizer(roles, ",");
Vector rolesv = new Vector();
while(rs.hasMoreTokens()){
rolesv.addElement(rs.nextToken());
}
String s = lines.nextToken().trim();
boolean isSendDebugLog = s.equalsIgnoreCase("true");
MasterData masterData = new MasterData(version, rolesv, sessionId, screenName, isSendDebugLog);
onSuccess(masterData );
}
}
Пример JSP для сервера:
<%@page import="uk.co.maxant.cam.web.VersionHelper"%>
<%@page import="org.apache.log4j.Logger"%>
<%@page import="java.io.FileInputStream"%>
<%@page import="java.util.Properties"%>
<%@page import="java.io.RandomAccessFile"%>
<%@page import="java.io.File"%>
<%@page import="uk.co.maxant.util.ServiceLocator"%>
<%@page import="uk.co.maxant.cam.services.OrchestrationService"%>
<%@page import="uk.co.maxant.cam.data.Party"%>
<%@page import="java.util.List"%>
<%@page import="uk.co.maxant.cam.services.OrchestrationService.MasterData"%>
<%
Logger log = Logger.getLogger(this.getClass().getCanonicalName());
String user = "";
String roles = "";
//either they are logged in, or we can check their login against the authentication header, since its ALWAYS sent
try{
//we know who they are. if the installUid is known, lets add it to their profile!
String installUid = request.getParameter("installUid");
if(installUid != null && installUid.equals("null")) installUid = null;
String auth = request.getHeader("authorization");
OrchestrationService os = ServiceLocator.getInstance().getOrchestrationService();
MasterData md = os.getMasterData(auth, installUid);
if(md.party != null) user = md.party.getScreenname();
for(String role : md.roles){
roles += role + ",";
}
String version = VersionHelper.getVersionFromJad(request);
boolean sendDebugLog = true; //change to false if we get swamped. but really, we shouldnt ever get this stuff.
%>OK
<%=version%>|
<%=session.getId()%>|
<%=user%>|
<%=roles%>|
<%=sendDebugLog%>|
<%
}catch(Exception e){
log.warn("failed to read master data", e);
%>ERROR
<%=e.getMessage() %>
<%
}
%>
В результате получается библиотека maxant J2ME, которая работает на CLDC 1.1 и MIDP 2.0 и может быть загружена здесь . Он включает в себя классы для всего перечисленного и многое другое и выпускается под лицензией LGPL:
- URLEncoder — от Apache Catalina
- Base64 — от кодека Apache Commons
- CredentialsProvider — интерфейс, который ServerCaller использует для получения учетных данных сервера для входа
- Formatter — форматирует, например, десятичные дроби
- GAUploader — загружает событие в Google Analytics
- Математика — некоторые математические вещи, которых нет в CLDC
- Точка — двухмерная точка на плоскости
- ServerCaller — API высокого уровня для вызова сервера, как описано выше
- StringTokenizer — простой и очень похожий на J2SE
- URL — инкапсуляция протокола, домена, порта и страницы
- ImageHelper — код, взятый из Интернета для масштабирования изображений
- DirectoryChooser — интерфейс для выбора локального каталога на устройстве
- FileChooser — интерфейс для выбора локального файла на устройстве
Чтобы использовать библиотеку, добавьте ее в папку «lib» вашего проекта J2ME, затем убедитесь, что она находится на пути сборки, и в Eclipse Pulsar вам также необходимо установить флажок в пути сборки на последней вкладке, чтобы убедиться, что классы в JAR экспортируются в ваш JAR-файл JAD.