Author Archives: elwood
Хочу поделиться маленьким велосипедом. Частенько нам нужно загрузить динамически кусок html-разметки (например, какой-нибудь div) и вставить его в DOM страницы. Иногда этот кусок разметки содержит javascript в виде некоторых обработчиков событий. Но если загружать его в ajax запросе, то javascript выполнен не будет (и функции, которые вы определили, тоже не будут зарегистрированы). Для того, чтобы заставить это работать, нужно либо вынести весь js-код в отдельный js-файл и указать его на главной странице заблаговременно, либо ограничивать себя использованием только inline-javascript (это когда мы пишем <input type=”button” onclick=”someJsCode()”/>. В моем случае оба варианта были неудобны, а самым удобным был именно обычный javascript в загружаемых кусочках разметки (потому что IDE при редактировании этих частей лучше понимает контекст – не возникает варнингов о “неизвестных” функциях, айдишниках элементов, переменных итд”. Поэтому я сочинил функцию, которая принимает в качестве аргумента загруженную разметку, выдирает оттуда скрипты (содержимое тегов <script>), склеивает их в один элемент <script> и добавляет в document.body. После этого javascript-функции, определенные в загруженных скриптах регистрируются в пространстве имен, и нормально работают. Причем, если при следующем ajax-запросе необходимо будет поменять реализацию некоторых функций, ничего дополнительно делать не нужно – js-интерпретатор затрет старые функции (по имени) новыми.
Плюс к этому, так как код скриптов нам больше не нужен, и будет только мешаться при отладке, другая функция убирает все script-теги и возвращает разметку уже без скриптов. Её можно вставить в innerHTML элемента, куда мы хотели запихнуть динамический контент.
Код функции:
/** * Concatenates all javascript code in html into one big script and * creates appends script element with this code to document body. * @param html Markup with embedded scripts retrieved using ajax. * * TODO : mb additional work required for scripts with specified src */ function processEmbeddedScripts(html) { var tempDiv = document.createElement('div'); tempDiv.innerHTML = html; var scriptNodesList = tempDiv.getElementsByTagName('script'); if (scriptNodesList.length > 0) { var wholeScript = ''; for (var i = 0; i < scriptNodesList.length; i++) { var scriptCode = scriptNodesList.item(i); wholeScript = wholeScript + scriptCode.innerHTML + ' '; } var scriptElement = document.createElement('script'); scriptElement.type = 'text/javascript'; scriptElement.text = wholeScript; document.body.appendChild(scriptElement); } } /** * Removes all embedded scripts from specified piece of html markup * and returns the shrinked markup. */ function excludeEmbeddedScripts(html) { return html.replace(/<script(.|\s)*?\/script>/g, ''); } |
Использование:
// получаем html, очищенный от скриптов var htmlWithoutScripts = excludeEmbeddedScripts(rawHtml); // добавляем этот html в DOM document.getElementById('mydiv').innerHTML = htmlWithoutScripts; // и после того, как мы имеем уже полностью собранный DOM, достаем скрипты и регистрируем их // в документе. при этом скрипты будут выполнены браузером. processEmbeddedScripts(rawHtml); |
Возможно (см TODO), еще придется доработать этот велосипед для скриптов с указанным src (их нужно пропихивать в document.body без text, но с указанным src).
PS: При повторных выполнениях этого кода старые функции затираются новым кодом (это корректное поведение).
PS-2: В некоторых браузерах подобных подход тяжело отлаживать, поскольку добавленные таким образом скрипты браузер в инструментах разработчика либо не отображает полностью, либо не отображает до тех пор, пока не случится брейпоинт в этих кусочках кода. Поэтому приходится этот метод использовать редко, предпочитая классически выносить весь js-код в отдельные файлы.
В сервлете будет выброшено исключение org.apache.catalina.connector.ClientAbortException (при вызове ServletOutputStream.write(); )
Чтобы в лог не капали мусорные сообщения об этих исключениях:
} catch (org.apache.catalina.connector.ClientAbortException e) { // do nothing - client has cancelled the download } |
Либа, содержащая класс ClientAbortException – либо томкатовская catalina, либо джейбоссовский jbossweb.jar (в который эмбеддится каталина).
Судя по факу http://www.jboss.org/jbossweb/faq.html, в jbossweb используется tomcat версии 6.0.13+
Так как нам в этом случае совпадение версий некритично, мы можем использовать 6.0.13 (или jbossweb.jar в качестве compile-time-lib, если используется ant).
Мейвеновские координаты каталины:
<!-- JBoss Web is based on Tomcat 6.0.13 --> <!-- this dependency is only to catch org.apache.catalina.connector.ClientAbortException in file download servlets --> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>catalina</artifactId> <version>6.0.13</version> <scope>provided</scope> </dependency> |
За отладкой программы, которая постоянно посылает и принимает сообщения по GPRS с сервера, почувствовал необходимость в имитации плохого соединения, погуглил инструменты и нашел следующее:
1. SoftPerfect Connection Emulator – платная (100 баксов за standard edition), встала на мою Win7 x64 без проблем, позволяет выбрать сетевой интерфейс и задать ему скорость и различные побочные эффекты (% потерь пакетов, дубликаты пакетов, reordering). К сожалению, в триальной версии можно юзать сессии не более 30 секунд, потом сетевой интерфейс начинает работать как обычно и надо заново включать эмуляцию. 30 секунд – это слишком мало, поэтому стал искать дальше.
2. Wipfw – портированный с FreeBSD стандартный инструмент, позволяет делать почти все что и SoftPerfect, но, к сожалению, работает пока только на Windows XP или Windows Server 2003. Ну, мне большего и не надо, взял соседнюю свободную тачку, поставил туда wipfw и запустил серверное приложение. Все сработало наура, сначала подключился в обычном режиме, начал передавать данные и на второй машине включил 100% потери пакетов. Некоторые сообщения успели “отправиться” перед тем, как приложение поняло, что соединение не функционирует. И следующие сообщения уже были сохранены в локальном хранилище для отправки, когда соединение восстановится. Естественно, первые сообщения, “отправленные” на сервер – потерялись.
Минигайд по wipfw. Программа ставится как служба (service) сетевого интерфейса (ее видно, если зайти в свойства соединения), и висит постоянно.
А для манипуляции правилами нужно запускать ipfw.exe.
Вот так можно посмотреть список всех правил: ipfw.exe show
А так удалить все правила (программа спросит подтверждения): ipfw.exe flush
Полный запрет на прием IP-пакетов с хоста 172.28.1.4: ipfw.exe add deny ip from 172.28.1.4 to any
Потеря 30% пакетов: ipfw.exe add prob 0.3 deny ip from 172.28.1.4 to any
0