Я пишу приложение GWT / GAE (Java). Пример автоматически созданного слова приветствия, который поставляется с GWT, генерирует код, подобный следующему:
<table align="center">
<tr>
<td colspan="2" style="font-weight:bold;">Please enter your name:</td>
</tr>
<tr>
<td id="nameFieldContainer"></td>
<td id="sendButtonContainer"></td>
</tr>
В клиентской Java (для JavaScript) он обращается к HTML-элементам следующим образом:
final Button sendButton = new Button("Send");
final TextBox nameField = new TextBox();
nameField.setText("GWT User");
Я добавил поле контейнера, чтобы показать, что я могу программно добавить ссылку, используя элемент HTML: someStuffContainer. Я также добавил еще одно поле, которое будет заполнено вызовом службы RPC для URL выхода из системы: logoutUrlContainer. (Да, теперь я знаю, что есть более простой способ сделать это: GWT / GAE (Java): в сочетании с GAE аутентификация пользователя app.yaml / web.xml и вход в систему / выход из системы на самом деле не работают . Я просто хотел посмотреть, смогу ли я заставить его работать.) Обратите внимание, что someStuffContainer был только для того, чтобы сделать этот контролируемый эксперимент: отделить программное добавление URL-адреса от заполнения этого URL-адреса ссылкой для выхода из системы, созданной на стороне сервера. ; это будет актуально ниже.
<tr colspan="2">
<td id="someStuffContainer"></td>
</tr>
<tr colspan="2">
<td id="logoutUrlContainer"></td>
</tr>
В Java на стороне клиента (для JavaScript) я сделал это:
// make it clear that we can add something to the page
final HTML someStuffHTML = new HTML
("<a href=https://www.google.com/search?q=zap>some stuff 3</a>");
// provide the user with a logout URL; FIX: do this by just
// providing a link to a generic logout servlet, rather than by
// proactively creating the logout link here.
final HTML userStatusHTML = new HTML();
GWT.log("userStatusService.getStatus()");
userStatusService.getStatus
("/", new AsyncCallback<UserStatusServiceResponse>() {
public void onFailure(Throwable caught) {
GWT.log("userStatusService.onFailure()");
LogUtil.logThrowable(caught);
}
public void onSuccess(UserStatusServiceResponse result) {
GWT.log("userStatusService.onSuccess()");
if (result.loggedIn) {
userStatusHTML.setHTML("<a href=\""+ result.logOutUrl+
"\">log out</a>");
} else {
// FIX: we should actually redirect to the base page
userStatusHTML.setHTML("<a href=\""+ result.logInUrl+
"\">log in</a>");
}
}
});
RootPanel.get("someStuffContainer").add(someStuffHTML);
RootPanel.get("logoutUrlContainer").add(userStatusHTML);
Это все работает в том смысле, что программный HTML-код появляется, и нажатие на ссылку выхода из системы выводит пользователя из системы. (Чтобы увидеть эту работу, вы должны открыть URL выхода из системы на другой вкладке; плагин devmode GWT выходит из себя, если вы переходите на другую страницу; похоже, он попадает в состояние, в котором он просто продолжает жаловаться, что приложение нужно перекомпилировать и Вы не можете выйти из этого состояния. Кроме того, вы должны нажать перезагрузить для ядра приложения, чтобы потребовать повторного входа в систему, если вы вышли из системы, а клиентская сторона все еще работает, вы можете просто продолжать использовать приложение, если не на стороне сервера, которую вы проверяете, пользователь вошел в систему.)
Мне кажется, что имя поля контейнера "logoutUrlContainer" вообще не должно иметь значения. Поэтому, прежде чем регистрироваться, я решил очистить код и переименовать его в нечто более общее, отражающее тот факт, что я мог бы использовать этот контейнер для более общего виджета «Статус пользователя». Поэтому я переименовал «logoutUrlContainer» в «userStatusContainer»; Я переименовал его в .html и в клиентскую Java (скомпилировано в JavaScript). Теперь вместо работы эта строка получает исключение нулевого указателя:
RootPanel.get("logoutUrlContainer").add(userStatusHTML);
Я довольно опытный программист: я был уверен, что скопировал и вставил, чтобы строки соответствовали. Я проверил, используя функцию поиска в Emacs. Я использовал ant clean для очистки всего проекта. Я использовал grep для grep для любых двоичных файлов (таких как файлы классов) во всем проекте, которые могли быть пропущены ant clean и каким-то образом все еще содержали старое имя. Я перезапустил сервер, убил вкладку браузера, содержащую приложение и т. Д. Я делал все это неоднократно. Неоднократно я получаю исключение нулевого указателя. Я думаю, что делал все это снова и снова, по крайней мере, в течение часа. Обратите внимание, что все время someStuffContainer всегда работал; каким-то образом RootPanel.get () всегда находил его, не находя userStatusContainer. Наконец, в отчаянии я просто снова назвал строку контейнера. Сразу это работает. Я зарегистрировался.
Что, черт возьми, происходит? Муравей чистым что-то упустил? Вот моя мишень для чистой муравьи, которую я расширил сам:
<target name="clean" description="Cleans this project">
<delete dir="gwt-unitCache" failonerror="false" />
<delete dir="war/myapp" failonerror="false" />
<delete dir="war/WEB-INF/classes" failonerror="false" />
<delete dir="war/WEB-INF/lib" failonerror="false" />
<delete dir="war/WEB-INF/deploy" failonerror="false" />
<delete file="war/WEB-INF/appengine-web.xml" failonerror="false" />
<delete file="war/WEB-INF/web.xml" failonerror="false" />
</target>
Единственная причина, по которой я не удаляю все war / WEB-INF, заключается в том, что я использую необязательный app.yaml для настройки Java Google App Engine, а не редактирую файлы .xml самостоятельно.
Неужели в моей цели нет муравьев? Этот опыт действительно заставляет меня не хотеть использовать GWT.
Обновление : Мне кажется, я знаю, что случилось: javascript перекомпилировался, но мне никогда не приходило в голову, что плагин разработки GWT позволит кэшировать страницу. Позже я изменил список файлов приветствия, и старый файл приветствия все еще отображался. Изучая файлы конфигурации, я не мог придумать, как это было бы возможно. Наконец я просто нажал перезагрузить и новый файл отобразился.
В любом случае, кэширование объяснит ошибку, о которой сообщается в этом вопросе. Я не знаю, что делает GWT, позволяя кэшировать страницу, особенно в devmode. Вы думаете, что после 15 лет веб-разработки я мог подумать об этом раньше. Однако я не могу представить, как люди могут быть настолько небрежными с эффектами кэширования, особенно в средах разработки.
Я использую последнюю версию OS X 10.6.8 (x86).
Мой браузер Chrome: 16.0.912.63 (Официальная сборка 113337)
$ java-версия
Java-версия "1.6.0_29"
Java (TM) SE Runtime Environment (сборка 1.6.0_29-b11-402-10M3527)
Java HotSpot (TM) 64-разрядная серверная виртуальная машина (сборка 20.4-b02-402, смешанный режим)
Я использую gwt-2.4.0.