12 марта 2007

Генри Миллер, "Колосс Маруссийский"

<...>
Едва я забрался в машину, припустил дождь, сперва слегка, потом всё сильней и сильней. К тому времени, как мы покинули плодородную равнину, земля представляла собой бурлящее водное пространство; то, что было окаменевшей на солнце глиной, песком, бесплодной почвой, пустошью, теперь представляло собой ряд затопленных террас, пересекаемых коричневыми бурными водопадами, реками, текущими во всех направлениях, мчащимися к огромной впадине, над которой поднимался пар, и заполненной мрачными глыбами земли, сучьями деревьев, камняли, кусками сланца, руды, дикими цветами, мёртвыми насекомыми, ящерицами, тачками, пони, собаками, кошками, будками уборных, жёлтыми кукурузными початками, птичьими гнёздами — всем, что не имело разума, или ног, или корней, чтобы спастись или устоять перед напором стихии. По другую сторону горы, под всё тем же проливным дождём, мы проезжали мимо мужчин и женщин, сидящих, держа зонт над головой, верхом на крошечных животных, которые неторопливо спускались по склону. Молчаливые, печальные фигуры, движущиеся черепашьим шагом, как упорные пилигримы на пути к святыне. Громадные утёсы-часовые, взгромоздившиеся на плечи друг другу, как шаткие пирамиды спичечных коробков на каминной доске у Пикассо, превратились в исполинские шишковатые грибы, сочащиеся чёрным пигментом. Покосившиеся, грозящие рухнуть, они под неистовствущим ливнем казались даже более страшными, чем прежде. Время от времени впереди вставала плоская гора, массив в тонких прожилках руд, несущий на вершине крохотное белое святилище под голубой крышей. Если б я не знал, что это Крит, то вообразил бы, что нахожусь на каком-то таинственном жутком монгольском плато, на запретном перевале, охраняемом злыми духами, которые затаились, поджидая случайного путника, чтобы свести его с ума своими трёхногими мустангами и мертвецами, красными от хны, что стоят, как замёрзшие семафоры, в лунном свете унылой ночи.
<...>


Генри Миллер, "Колосс Маруссийский"

11 марта 2007

Генри Миллер, "Колосс Маруссийский"

<...>
Но переместимся дальше — к Сатурну. Впечатление, какое производит на профана Сатурн, как и наша Луна, когда смотришь на них сквозь увеличительные линзы, должно вызывать в учёном инстинктивный протест и сожаление. Никакие сведения и цифры, касающиеся Сатурна, никакое увеличение не могут объяснить того неоправданного тревожного чувства, какое вид этой планеты рождает в наблюдателя. Сатурн — живой символ всего мрачного, болезненного, бедственного, рокового. Его молочно-белый неизбежно вызывает ассоциации с требухой, гноем, чувствительными органами, укрытыми от чужого глаза, позорными болезнями, пробирками, лабораторными препаратами, воспалёнными миндалинами, мокротой, эктоплазмой, унылым предвечерьем, приступом меланхолии, войной ункуба и суккуба, бесплодием, анемией, мнительностью, пораженческим настроением, запором, антитоксинами, дурацкими романами, грыжей, менингитом, замшелыми законами, кабинетной волокитой, жизнью пролетариев, мерзкими лавчонками, Христианским союзом молодых людей, собраниями "Христианского действия", спиритическими сеансами, поэтами вроде Т.С. Эллиота, фанатиками вроде Александра Доуи, целителями вроде Мэри Бейкер Эдди, политиками вроде Чемберлена, роковой случайностью вроде банановой корки и разбитой башки, мечтами о лучших днях, которые оканчиваются тем, что попадаешь под грузовик, тонешь в собственной ванне, случайно убиваешь лучшего друга, умираешь от икоты вместо того, чтобы пасть на поле брани, и так далее ad infinitum1. Сатурн воздействует пагубной магией инертности. Его кольцо, согласно утверждениям учёных мужей, плоское, как бумага, — это обручальное кольцо, связывающее его вечными узами с естественной смертью или бессмысленным концом. Чем бы ни был Сатурн для астронома, для человека на улице — это знак жестокого рока. Человек несёт его в сердце, потому что его жизнь, некоторым образом обессмысленная, заключена в этом абсолютном знаке, и он может быть уверен, что, если ничто другое не прикончит его, рок не промахнётся. Сатурн — это жизнь в напряжённом ожидании, нет, не смерти, а чего-то вроде бессмертия, то есть неспособности умереть. Сатурн как некий атавизм — двойной сосцевидный отросток души. Сатурн как рулон обоев, намазанный на лицевой стороне клейкой харкотиной, которая, как считают отделочники, незаменима в их metier2. Сатурн — это та зловещего вида дрянь, что отхаркиваешь наутро после того, как накануне выкуришь несколько пачек сухих, мягких, приятных сигарет. Сатурн — это отсрочка, оказывающаяся бессрочной. Сатурн — это сомнение, недоверие, скептицизм, факты ради фактов, и чтобы никаких выдумок, никакой мистики, ясно? Сатурн — это кровавый пот , которым добыты знания ради знания, сгустившийся туман бесконечных поисков маньяком того, что всегда находится у него под носом. Сатурн безумно меланхоличен, потому что не знает и не признаёт ничего, кроме меланхолии; он как медведь в спячке, живущий собственным жиром. Сатурн — это символ всех знамений и суеверий, липовое доказательство божественной энтропии, липовое, потому что, окажись правдой, что Вселенная останавливается, Сатурн бы давным-давно расплавился. Сатурн вечен, как страх и нерешительность; он становится всё бледнее, всё туманней с каждым компромиссом, каждой капитуляцией. Робкие души плачут по Сатурну, совсем как дети, которые, считается, плачут по Кастории. Сатурн даёт нам ровно столько, сколько мы просим, не каплей больше. Сатурн — это белая надежда белой расы, которая бесконечно лепечет о чудесах природы и занимается тем, что уничтожает величайшее чудо — ЧЕЛОВЕКА. Сатурн — это звёздный самозванец, претендующий на роль великого Вершителя судеб, Мсье де Пари, автоматического выключателя мира, поражённого атарксией. Пусть небеса поют свою осанну — этот лимфатический шар никогда не перестанет слать свои молочно-белые лучи смертной тоски.

Это эмоциональный снимок планеты, чьё необычное влияние продолжает угнетать почти погасшее сознание человека. Она представляет собой самое безрадостное зрелице на небесах. Она отвечает всем вызывающим малодушный страх образам, поселившимся в человеческой душе; она — единственное вместилище всего отчаяния и безнадёжности, которым человечество поддалось со времён незапамятных. Она станет невидимой только тогда, когда человек исторгнет её из своего сознания.
<...>


1 До бесконечности (лат.).

2 Ремесло (фр.).

Генри Миллер, "Колосс Маруссийский"

06 марта 2007

Долго идти не хотелось, и мальчик остановился и сел, сначала на корточки, потом, оперевшись на руки, по-турецки.

Вокруг простиралась степь в своём самом ярком наряде — было время цветения всего, что может здесь цвести. Это бескрайнее поле трудилось всё лето, разделяя между миллионами своих жителей все свои запасы, чтобы они подарили затем ему семена своих детей, укрыли его от солнца своей тенью. Мальчик выбрал место повыше, чтобы было интересней смотреть по сторонам и для лучшего обзора примял перед собой траву. Солнце светило ему в спину, уже не такое жаркое в этот вечер, и длинная тень далеко уходила от него сначала по его пригорку вниз, потом дальше по равнине ещё на добрый десяток метров.

Вокруг кипела работа по подготовке жителей ко сну. Цветы набирали последнего перед ночью солнечного тепла, все крылатые насекомые перелетали от цветка к цветку, но уже не так дотошно, как днём, когда ни один цветок не оставался непосещённым. У кого крыльев не было, ползал по стеблям вверх и вниз и тоже не оставался без добычи. Насытившиеся за день мошкарой птички играли друг с другом в траве, изредка взлетая выше неё с тем, чтобы напасть на соперника сверху.

Мальчик любил сидеть так долго, но не мог сидеть неподвижно, у него то затекала нога, то уставали руки, на которые он упирался. Ложась на живот, он переключал внимание на какой-то участок земли или травы, на котором умудрялись уместиться десятки живых существ, каждый из которых жил своей жизнью и знать ничего не хотел ни про кого другого, поэтому так интересно было наблюдать за их поведением, когда они с кем-то встречались. А когда он садился или даже вставал, он мог видеть мир других размеров, побольше, но такой же густонаселённый и суетливый.

Сегодня мальчик не знал, хочет ли он остаться здесь надолго или скоро вернётся туда, откуда пришёл. Но по крайней мере сейчас он не хотел никуда уходить и сидел прямо, не смотря никуда конкретно, а просто вперёд, и наслаждался тихим трудолюбием всей этой природы.

Потом тело почувствовало лёгкий толчок.

Он был настолько слабо ощущаемый и приятный, что ничем не отличался от остальных физических впечатлений этого заката. С точно таким же усилием из травы могла вылететь маленькая песчаная птичка, или слабый порыв ветра пододвинуть сползшие на лоб волосы ближе к переносице. Мальчик просто отметил про себя это событие, и стал наблюдать дальше.

Но для воображаемого стороннего наблюдателя это спокойствие длилось всего мгновение, а после в сознании мальчика последнее событие стало занимать всё больше места, оттесняя другие, рядовые моменты. Он подумал, что же такое произошло, что могло удивить его в этом тягучей жаркой картине мира. Прислушался к себе — внутренние органы его как бы выбрировали, медленно, с малой амплитудой, и через три-четыре цикла качения замерли, но не в своём обыкновенном положении, а как будто наклонившись к спине, как если бы маятник взяли и остановили рукой у одной из стенок напольных часов. Во рту немного пересохло.

Мир, стоявший перед его глазами, остался прежним, разве что чуть прибавилось красок полевых цветов, да тень его фигуры стала едва заметно резче. Это показалось ему красивым и вместе с тем настораживало. А так как впереди ровным счётом ничего не происходило, он решил обернуться лицом к солнцу.

Там, откуда он пришёл сюда, почему-то вместо одного закатного Солнца было два, хотя поначалу он не мог их разделить, так близко они висели рядом в небе. Одно, наверное настоящее Солнце было в центре белого цвета, по краям проходила плотная ярко-оранжевая полоса, а за ней во все стороны расходилось жёлтое осеннее тепло — всё как обычно. Второй же, Гость, имел неправильную ромбовидную форму с закруглёнными краями, был вполовину меньше первого солнца и потихоньку, но всё же не очень медленно рос в размерах в стороны и протягивая свой нижний конец к земле, стараясь всё большей площадью соприкоснуться с первым. Облака разбегались от Гостя в разные стороны, но не успевали и испарялись в голубизне неба.

Ромб захватывал неба всё больше и больше, сглаживал свои края и в конце концов превратился в большой овал с дырой посередине, внутри которой проглядывала полоска чистейшего цвета неба, а в центре висело всё то же второе Солнце.

Это видение совсем не пугало мальчика, какая-то беззаботность заставляла его продолжать сидеть вполоборота, опираться на правую руку и наблюдать за происходящим. Он изучал все контуры явления, следил за его ростом; второе Солнце грело его также, как и первое.

Затем он заметил, как от Гостя вниз к земле стар расти столб, по своему виду похожий на раскалённый воздух, вырывающийся порой из уличной печки-"буржуйки", если в неё засунуть сразу много сена и оно загорится одновременно — он проделывал это не раз, когда был за городом с родителями. Мальчик на секунду вспомнил об этом, вспомнил, кто он есть, как его зовут, сколько ему лет, но тут же обо всём этом забыл как о чём-то бесполезном и стал смотреть дальше.

Навстречу спускащемуся с небес столбу от земли, ровно под вторым солнцем, стал подниматься вверх точно такой же кружащийся вокруг своей оси поток, такой же неровный, отбрасывающий блики от своей изменяющейся поверхности. Вначале на концах они сходили на нет и не были видны, как будто не стремились друг к другу и лениво покачивали своими сужающимися окончаниям в воздухе, то напрявляя себя друг на друга, то отворачиваясь и отклоняясь в стороны. Но крутящая их сила не позволяла им слишком расходиться и всё сильнее тянула нижний вихрь вверх к центру оранжевого овала. Скорость их сближения всё увеличивалась, и наконец они соединились, сначала остриями своих конусов, а затем уже всеми своими полыми телами. Основание воздушный цилиндр имел толще своего тела и как будто поддерживал собой второе солнце, опираясь на землю.

В момент этого соединения мальчик снова почувствовал толчок, который не подбросил его вверх, как он почему-то был уверен, а подтолкнул его ближе к солнцу, даже не подтолкнул, а подтянул, потому что это был не толчок в спину, а притягивание всего его тела вперёд со стороны груди. Вся трава вокруг него и насколько хватало глаз также качнулась вперёд.

Ему показалось, что всё поле превратилось из сухой степи в море, по которому шла единственная волна высотой со стебли растущих на ней цветов. Волна эта приближалась к нему в полной тишине с очень большой скоростью — ещё две секунды назад она была далеко-далеко, видимая из-за того, что вся растительность вокруг полегла, — и почти сразу она оказалась в сотне метров от него. Мальчик начал подниматься.

Мгновенно волна настигла его и сбила с ног, почва как будто уехала вперёд, в сторону Гостя, а он упал назад на руки и беспомощно сел с вытянутыми ногами. Резко вдохнув, он закашлялся от пыли, поднятой волной. Обернувшись, он увидел, что трава снова встала в полный рост и слегка покачивается, как ни в чём ни бывало. Но это уже не интересовало мальчика, его занимал только Гость, который оказался не только красивым, но и опасным.

А дальше случилось то, что мальчик уже ожидал. Что-то внутри Гостя сначала немного сжалось, затем сверкнуло, он резко вырос в размерах и лопнул, как подсвеченный изнутри шарик с газом. Из его центра во все стороны стал расти прозрачный шар того же вида, что и газовая опора Гостя. Шар искажал голубизну неба, казалось, что через него можно заглянуть сквозь небо в чистый тёмный космос и увидеть звёзды. Это было очень красивое зрелище, так как мальчик ни разу в своей жизни не видел вблизи фейерверков.

Красиво было до тех пор, пока шар, увеличиваясь в размере, не коснулся своей нижней поверхностью степи. Трава там, далеко-далеко, загорелась, а стенки шара продолжали расходиться в стороны, выжигая на своём пути землю. Мальчик понял, что с той же невероятной скоростью, с которой к нему шла волна цветов, к нему приближается теперь и огонь. Бежать он уже не мог, ноги не слушали его, и очень скоро такая красивая стенка шара, на таком близком расстоянии уже не так чётко видимая, приблизилась вплотную. Хотелось осторожно потрогать её рукой, такая она была красивая.

Мальчик инстинктивно вдохнул этот огонный воздух полной грудью, и словно глоток раскалённого металла свободно прошёл сквозь него и безо всяких помех вышел в спине между лопаток. Он уже не понимал, сидит ли он на месте в том положении, как его сшибла с ног волна, или летит прочь параллельно земле, прилипнув лицом и грудью к густой плёнке всё растущего шара. Всё остальноё происходило в какие-то мгновения. Мальчику оставалось только с любопытством следить за тем, что происходило с его собственным телом.

Его рёбра сломались одновременно, грудная клетка как будто раскрыла двухстворчатую входную дверь внутрь тела. Вся кожа спереди и сзади почернела, покрылась сеткой трещин, и каждый её кусочек, оторванный теперь от других, стал уменьшаться, сгорая и загибая наружу края. Плоть отделялась от скелета кусками разного размера и улетала назад с ещё большей скоростью. Голова была сильно закинута назад, и кожа от подбородка до низа горла впеклась внутрь рта. Руки то свободно развевались позади тела, то размахивали во все стороны, лишённые почти всего мяса, сначала потеряли свои кисти, затем кости по локоть, затем оторвались сами по себе.

Полутело-полускелет теперь уже не летел ровно у земли, а кувыркался как брошенная в воздух тряпичная кукла. Потом, потеряв свою опору, стенку всё уходившего вдаль шара, оно упало на землю и ещё прокатилось по ней десяток метров, рассыпаясь на части. Дальше всех закатился череп, чёрный от обуглившегося мяса. Он лежал вниз лицом, если у него ещё было лицо, на пепле, оставшемся от травы, и лежал неподвижно, несмотря на гулявший вокруг горячий ветер.


Сон кончился. Остался привкус страха во рту, и как мальчик не сглатывал слюну, он не проходил.

19 февраля 2007

Выбор имени ребёнка

15 февраля у меня родилась дочка, и все мои планы вырастить парня, не знающего Windows, накрылись медным тазом, но не в этом дело. Дочке нужно имя. Родители советуют назвать Еленой, но судя по какому-то найденному графику, это самое распространённое имя. Жена предлагает Анну или Марию. Мои варианты -- Татьяна и Светлана.

Дабы сделать выбор абсолютно1 честным, и был написан нижеследующий сервлет, при каждом запуске выдащий произвольное сочетание одного из пяти вышеобозначенных имён с моим именем, превращённым в отчество.

package ru.unchqua.namechooser;

import java.io.IOException;
import java.io.Writer;
import java.util.Date;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class NameChooserServlet extends HttpServlet {

private static final long serialVersionUID = 3558553303047697031L;

private static final int SECONDS = 3;

private static final String[] NAMES = {"Татьяна", "Светлана", "Елена", "Анна", "Мария"};

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Сессия.
HttpSession sess = req.getSession();

// Номер имени в массиве имён.
int nameidx = -1;

// Момент данного запуска.
long thisrun = new Date().getTime();

// Момент предыдущего запуска.
long lastrun = -1;
try {
lastrun = ((Long) sess.getAttribute("lastrun")).longValue();
} catch (Exception e) {
lastrun = new Date().getTime();
}

// Если запуск был более заданного числа секунд назад.
if ((thisrun - lastrun) > (SECONDS * 1000)) {
nameidx = 0;
log("Special case");
}
else {
Random rnd = new Random(sess.hashCode() ^ thisrun);
nameidx = rnd.nextInt(NAMES.length);
log("Random case");
}

// Вывод результата.
StringBuffer htmlbuf = new StringBuffer(1000)
.append("<?xml version=\"1.0\" encoding=\"KOI8-R\"?>");
.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
.append("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"ru\" lang=\"ru\">");
.append("<head><title>Выбор имени</title></head>")
.append("<body>");
.append("<p style=\"text-align: center; margin-top: 5em; font-size: larger\">")
.append("<span style=\"font-weight: bold;\">")
.append(NAMES[nameidx])
.append("</span>")
.append("<br/>Евгеньевна</p>")
.append("</body></html>");
String html = htmlbuf.toString();
resp.setContentLength(html.getBytes("UTF-8").length);
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
Writer out = resp.getWriter();
out.write(html);
out.flush();
out.close();

// Сохранить время последнего запуска в сессии.
sess.setAttribute("lastrun", new Long(thisrun));
}

}


1 Если присмотритесь к коду внимательнее, то поймёте, какой страшный подвох я приготовил ничего не подозревающим родственникам.

16 февраля 2007

Retrieve document from server with proper error handling

For one of our intranet systems we implemented a simple search in files attached to official documents. Nothing difficult: search result is a list of files with its names and links to download them. Link is simple: servlet read a file by its id and outputs it with "application/octet-stream" or another MIME-type according to file's extension. But what if error occured at server? How can we tell operators about it? First option was to redirect them to a page with written error, but then they have to go back to list to try again, and this is not very comfortable.

We may request a servlet with AJAX XmlHttpRequest and output possible error with window.alert(), but then how to output document binary data from server in not so rare case of successful document retrieval? JavaScript's AJAX callback functions can't handle binary data well and can't present operator with standard browser's download dialog.

We ended up with this solution. Client requests servlet twice: one time it requests document (using AJAX with all its callback functionality), which is in turn requests real document from server and stores it in session with uniquely generated id, and another time client requests the same servlet, using document session id, which it had received at step 1. In case of failed server interchange (after step 1) operator is shown a message describing error just occured.

For AJAX interaction we use MochiKit v1.3.1, but are looking for lighter library.

Additional description may be found in GetFileAJAXWay servlet code.

GetFileAJAXWay.java:

package ru.unchqua.incubator.getfileajaxway;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
* Abstract servlet to get document from server AJAX way.
*
* Sequence is like this.
*
* Step 1. Client, using AJAX request, ask servlet to get particular document,
* identified by "docid". In response client receives JSON-message about
* successful or failed document retrieval; if job is done well, document itself
* is placed in session with unique identity. In that message client receives
* either "dsid" value (that session identity) or error string, which it can
* display to operator, using window.alert() for example.
*
* Step 2. On receiving "dsid" value client knows that document is ready to be
* requested, and servlet, called with that dsid, sends that document back to
* client, at the same time removing it from session, for it is not needed
* anymore.
*
* Descendants of this class must implement getDocument(long) method, which is
* used to actually receive needed document from somewhere.
*
* Calling parameters: either "docid" (identity of document needed) to retrieve
* it from server and place into session, or "dsid" (document session identity)
* for outputting that document to client.
*
* Examples of JSON messages after step 1:
*
* Successfull document retrieval:
* {"result":"success","dsid":"362547383846347775-ER8GERHGWER9GEH8-36644"}
*
* Retrieve error: {"result":"failure","reason":"No such document!"}
*/
public abstract class GetFileAJAXWay extends HttpServlet {

public GetFileAJAXWay() {
super();
}

/**
* Servlet entry point.
*
* @param req HTTP-request.
* @param resp HTTP-response.
* @throws ServletException
* @throws IOException
*/
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

// There must be a session.
if (req.getSession(false) == null) {
final String err = "No session!";
sendFailureReply(err, resp);
log(err);
return;
}

// Request document from server.
String reqid = req.getParameter("reqid");

// Output document to client.
String dsid = req.getParameter("dsid");

// Request.
if (reqid != null && reqid.length() > 0) {
requestDocument(reqid, req, resp);
}
// Output.
else if (dsid != null && dsid.length() > 0) {
retrieveDocument(dsid, req, resp);
}
// God knows what.
else {
final String err = "Unknown mode!";
log(err);
sendFailureReply(err, resp);
return;
}
}

/**
* Getting document from server and storing it in session.
*
* @param docidstr Document id to get.
* @param req HTTP-request.
* @param resp HTTP-response.
* @throws ServletException
* @throws IOException
*/
private void requestDocument(String docidstr, HttpServletRequest req, HttpServletResponse resp) throws IOException {

long docid;

// Session.
HttpSession session = req.getSession(false);

// Document id.
try {
docid = Long.parseLong(docidstr);
if (docid <= 0) {
final String err = "Wrong document id: \"" + escapeJSON(docidstr) + "\"!";
log(err);
sendFailureReply(err, resp);
return;
}
} catch (Exception e) {
final String err = "Wrong \"docid\" parameter value: \"" + escapeJSON(docidstr) + "\", " + escapeJSON(e.getMessage()) + "!";
log(err);
sendFailureReply(err, resp);
return;
}

// Getting document from server.
GetFileAJAXWayByteContainer cont;
try {
cont = GetFileAJAXWayHelper.getDocument(docid);
} catch (Exception e) {
final String err = "Error while getting document: " + escapeJSON(e.getMessage()) + "!";
log(err);
sendFailureReply(err, resp);
return;
}

// Unique document identity to store it in session.
final String dsid = dsid() + "-" + session.getId() + "-" + docidstr;

// Store document in session.
session.setAttribute(dsid, cont);

// Notifying client on successful document retrieval.
sendSuccessReply(dsid, resp);
}

/**
* Sending document to client.
*
* @param dsid Document id to get it from session.
* @param req HTTP-request.
* @param resp HTTP-response.
* @throws ServletException
* @throws IOException
*/
private void retrieveDocument(String dsid, HttpServletRequest req, HttpServletResponse resp) throws IOException {

// Session.
HttpSession session = req.getSession(false);

// Do we have document requested?
Object sessobj = session.getAttribute(dsid);
if (sessobj == null) {
log("No \"" + dsid + "\" object in session!");
resp.setStatus(HttpServletResponse.SC_OK);
return;
} else if (!(sessobj instanceof GetFileAJAXWayByteContainer)) {
log("Wrong \"dsid\" object in session!");
resp.setStatus(HttpServletResponse.SC_OK);
return;
}

// Remove document from session to not pollute memory.
session.removeAttribute(dsid);

// Document.
GetFileAJAXWayByteContainer document = (GetFileAJAXWayByteContainer) sessobj;

// File output.
resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentLength(document.getContent().length);
resp.setContentType("application/octet-stream");
resp.setHeader("Content-Transfer-Encoding", "binary");
resp.setHeader("Content-Disposition", "attachment; filename=\"" + dsid + "\"");
OutputStream out = resp.getOutputStream();
out.write(document.getContent());
out.flush();
out.close();
}

/**
* Unique document identity to store it in session.
*
* @return Unique id.
*/
private String dsid() {
return Long.toString(System.currentTimeMillis(), 10);
}

/**
* Escaping JSON-trouble symbols in string to append it to JSON response.
*
* @param subject Source string.
* @return Result.
*/
private String escapeJSON(String subject) {
if (subject == null || subject.length() == 0)
return "";
return subject.replaceAll("\"", "\\\\\"");
}

/**
* JSON response about successful job done.
*
* @param dsid Document id for client to get it later.
* @param resp HTTP-response.
* @throws ServletException
* @throws IOException
*/
protected void sendSuccessReply(String dsid, HttpServletResponse resp)
throws IOException {
String dsidJSON = "{\"result\":\"success\",\"dsid\":\"" + dsid + "\"}";

sendAnyReply(dsidJSON, resp);
}

/**
* JSON response about any failure.
*
* @param reason Error string.
* @param resp HTTP-response.
* @throws ServletException
* @throws IOException
*/
protected void sendFailureReply(String reason, HttpServletResponse resp)
throws IOException {
String reasonJSON = "{\"result\":\"failure\",\"reason\":\"" + escapeJSON(reason) + "\"}";

sendAnyReply(reasonJSON, resp);
}

/**
* Sending any JSON response to client.
*
* @param json JSON string to send.
* @param resp HTTP-response.
* @throws IOException
*/
private void sendAnyReply(String json, HttpServletResponse resp) throws IOException {

final byte[] result_bytes = json.getBytes("UTF-8");
final int CHUNK = 1024;
final BufferedOutputStream output = new BufferedOutputStream(resp .getOutputStream(), CHUNK);

resp.setStatus(HttpServletResponse.SC_OK);
resp.setHeader("Content-Encoding", "UTF-8");
resp.setContentType("text/plain; charset=UTF-8");
resp.setContentLength(result_bytes.length);

int bytes_pos = 0, bytes_chunk = 0;
do {
bytes_chunk = bytes_pos + CHUNK <= result_bytes.length ? CHUNK : result_bytes.length - bytes_pos;
output.write(result_bytes, bytes_pos, bytes_chunk);
bytes_pos += bytes_chunk;
} while (bytes_pos < result_bytes.length);
output.flush();
output.close();
}

/**
* Method to implement by subclasses.
*
* @param id Document id.
* @return Document body.
* @throws GetFileAJAXWayApplicationException
*/
protected abstract GetFileAJAXWayByteContainer getDocument(long id)
throws GetFileAJAXWayApplicationException;

}


ConcreteDocumentRetrievalServlet.java:

package ru.unchqua.incubator.getfileajaxway;

/**
* Concrete servlet.
*/
public class ConcreteDocumentRetrievalServlet extends GetFileAJAXWay {

private static final long serialVersionUID = 1L;

public ConcreteDocumentRetrievalServlet() {
super();
}

public GetFileAJAXWayByteContainer getDocument(long id)
throws GetFileAJAXWayApplicationException {
/**
* Code for actual document retrieval.
*/
return null;
}
}


GetFileAJAXWay.jsp:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8"/>
<meta http-equiv="Expires" content="Tue, Feb 07 1978 15:30:00 GMT"/>
<meta http-equiv="Content-Style-Type" content="text/css"/>
<meta http-equiv="Content-Script-Type" content="text/javascript"/>
<title>GetFileAJAXWay example</title>

<!-- MochiKit. -->
<script type="text/javascript" src="/MochiKit-1.3.1/Base.js"></script>
<script type="text/javascript" src="/MochiKit-1.3.1/Iter.js"></script>
<script type="text/javascript" src="/MochiKit-1.3.1/DOM.js"></script>
<script type="text/javascript" src="/MochiKit-1.3.1/Async.js"></script>

<script type="text/javascript">
<!--

// Servlet name.
var SERVLET_PATH = "/servlet/ru.unchqua.incubator.ajaxgetfile.ConcreteDocumentRetrievalServlet";
// URL to request.
var SERVLET_URL = document.location.protocol + '//'
+ document.location.hostname
+ (document.location.port > 0 ? ':' + document.location.port : '')
+ SERVLET_PATH;

/**
* Start AJAX request.
*/
function JS_AJAX_GetElFAFile(reqid) {

// Parameters.
var parameters = {};
parameters["reqid"] = reqid;
parameters["rand"] = new Date().getTime();

// Request.
loadJSONDoc(SERVLET_URL, parameters)
.addCallbacks(
JS_AJAX_GetElFAFile_Success,
JS_AJAX_GetElFAFile_Failure);
}

/**
* On successful AJAX call.
*/
function JS_AJAX_GetElFAFile_Success(jsondata) {

// Is that an error?
if (JS_AJAX_GetElFAFile_Is_response_error(jsondata)) {
JS_AJAX_GetElFAFile_Failure(jsondata);
return;
}
else if (typeof(jsondata.dsid) == "undefined") {
JS_AJAX_GetElFAFile_Failure("Document is not received!");
return;
}

// Request actual document.
window.location.href = SERVLET_URL + "?dsid=" + jsondata.dsid;

}

/**
* On failed AJAX call.
*/
function JS_AJAX_GetElFAFile_Failure(jsondata) {

var error_text =
(typeof(jsondata.result) != "undefined"
&& jsondata.result == "failure"
&& typeof(jsondata.reason) != "undefined"
&& jsondata.reason.length > 0)
? jsondata.reason
: jsondata.message + " (" + jsondata.number + ")";

window.alert(error_text);

}

/**
* Is response error?
*
* jsonadata: JSON object just received.
*
* Returns flag (true/false).
*/
function JS_AJAX_GetElFAFile_Is_response_error(jsondata) {

// Error is artifical (i.e. generated by hand at server).
var artifical_error = typeof(jsondata.result) != "undefined"
&& jsondata.result == "failure";

// Internal server error.
var hard_error = typeof(jsondata.number) != "undefined"
&& typeof(jsondata.message) != "undefined"
&& jsondata.number == 500;

return artifical_error || hard_error;

}

//-->
</script>
</head>
<body>

<a href="javascript:JS_AJAX_GetElFAFile(/*docid=*/123);">Get me that document!</a>

</body>
</html>


Other classes like GetFileAJAXWayApplicationException, GetFileAJAXWayByteContainer and GetFileAJAXWayDocumentRetriever are self-explanatory and are not provided here.

15 февраля 2007

Генри Миллер, "Колосс Маруссийский"

<...>
Здесь я должен сделать отступление и поведать вам о моём друге Майо, художнике, которого я знавал в Париже. Настоящее его имя было Маллиаракис, и, думаю, родом он был с Крита. Как бы то ни было, когда мы входили в патракскую гавань, я понял наконец всё, что он пытался объяснить мне в тот вечер, и очень пожалел, что в этот момент его не было со мной, чтобы разделить моё восхищение. Я вспомнил, как он долго рассказывал мне о своей стране и под конец сказал со спокойной и твёрдой уверенностью: "Миллер, тебе понравится Греция, я в этом убеждён". Почему-то эти последние слова произвели на меня впечатление и больше, нежели весь его рассказ, врезались в память. Она тебе понравится... "Господи, она мне нравится!" — вновь и вновь мысленно повторял я, стоя у поручней и упиваясь плавным движением судна и поднявшейся суетой. Я откинулся назад и поднял голову. Никогда прежде я не видел такого неба. Такого изумительного неба. Я чувствовал себя совершенно оторванным от Европы. Я ступил в новое царство свободного человека — всё объединилось, чтобы произвести на меня впечатление небывалое и оплодотворяющее. Боже мой, я был счастлив! Но впервые в жизни, будучи счастлив, я полностью это осознавал. Замечательно, когда просто испытываешь обыкновенное счастье; ещё лучше, когда знаешь, что счастлив; но понимать, что ты счастлив, и знать, отчего и насколько, в силу какого стечения событий и обстоятельств, и всё же испытывать счастье, испытывать счастье от этого состояния и знания, — это больше, чем счастье, это блаженство, и если у тебя хватает ума, следует, не сходя с места, покончить с собой, остановить, так сказать, мгновение. Именно это со мной произошло — за исключением того, что мне не достало ни сил, ни мужества тут же и расстаться с жизнью. И хорошо, что я этого не сделал, потому что меня ждали мгновения ещё более восхитительные, нечто, чего не выразить даже и словом "блаженство", нечто такое, во что, попытайся бы кто описать мне, я вряд ли бы поверил.
<...>


Генри Миллер, "Колосс Маруссийский"