п'ятниця, 16 травня 2008 р.

про рынок труда


ты помнишь, как все начиналось?
все было впервые и вновь,
как строили лодки,
и лодки завлись "Вера", "Надежда", "Любовь"
(с) Машина Времени

вспоминая самый старт моей програмистской карьеры я каждый раз прихожу к мысли, что тогда все было иначе. небо голубее, трава зеленее, девушки красивее я начинал искать работу в 2000м году, сразу после того, как развалился наш студенческий стартап. мне, сначала студенту, а потом уже выпускнику профильного ВУЗа, пришлось пройти 17 неудачных собеседований за два года, чтобы устроиться на свою первую работу с кабальными условиями. чистой потогонкой была та работа, но и она была ценна. уже тем, что она была и за нее платили хорошие деньги. мы, программисты, ценили факт наличия работы, мы видели снаружи последствия краха доткомов, и 09/11, и краха Телесенса. для меня это была первая работа, и я вдвойне ценил таким трудом добытое место. я учился отвественности, и качеству, и работе по 14 часов в сутки, и быстрому обучению, и долгому саппорту. очень много мне дала первая работа, и в профессиональном и в личном плане.
а после работы, вне стен офиса, мы -- я и мои друзья-коллеги -- мечтали о том, как станем мы экспертами. как будем не мы искать работу, а она нас. как сможем не терпеть прихоти локальной администрации, а ставить условия. как будут считаться с тем, что все-таки мы -- программисты -- создаем реальные продукты. да, роль менедмента и администрации важна, но не следует относитсья к нам, как к крепостным крестьянам. бойтесь своих желаний, они имеют тенденцию сбываться.


шло время, росла моя компетенция, росла моя цена на рынке труда. менялись условия. надувался "пузырь 2.0". и вермя, о котором я мечтал 6 лет назад, пришло. мы построили рынок нашей мечты -- где программист царь и Бог, где его ищет работа и о нем заботиться начальство. и нет сейчас проблем у программиста найти работу. а у хорошего программиста есть проблема отказаться от еще одной работы. но теперь мне приходиться "пасти котов" (с), я стал представителем руководства. и когда невозможно применить сколь-нибудь жесткие меры, такой себе "кнут" в отношении подчиненных, когда руководить приходиться только с помощью "пряников"... это ужасно.
разумеется и в той ситауции, в которой находился я при старте карьеры, ничего хорошего нет. любой перекос, ведущий либо к недооценке, либо к переоценке труда каждого из сотрудников, будь они менеджеры или уборщицы, есть зло. но всегда должен быть баланс. ситуация, когда человека не соотвествующего занимаемой должности можно только "придавить" морально, когда любая непопулярная мера является риском потери ресурса в проекте -- это не нормально. люди перестали быть заинтересованными хорошо делать свою работу, мы возвращаемся в "жизнь по Жванецкому": хорошо собираешь велосипед -- тебе 120, плохо собираешь велосипед -- тебе 120, вообще не собираешь велосипед -- тебе 120 (с).
одной из мер, которая поможет восстановить баланс, я считаю возрождение или старт системы рекомендаций. когда соискатель в резюме или на собеседовании называет пару-тройку своих прошлых руководителей, которые бы могли его рекомендовать. это не обязательно должны быть руководители с текущего места работы, но кто-то, кто может рассказать, как человек ведет себя в боевых условиях. почему он ушел из конторы, как его там оценивали. это тоже субъективное мнение, и презумпция невиновности должна работать здесь. если из двух руководителей, один безмерно хвалит соискателя и расскаызвает, как тот крут и отвественен, а второй -- ругает на чем свет стоит, то скорее всего со вторым руководителем, соискательно просто не сработался. так тоже бывает.

середа, 23 квітня 2008 р.

одной строкой

про слухи:
харьковское сообщество IT специалистов довольно узко. в том плане, что через три рукопожатия ты знаком практически со всеми, а за 5 лет в индустрии -- через два рукопожатия. тем забавнее слышать, каких размеров достигают слухи в устах людей с высшим техническим образованием, которые в большистве своем блещут воспитанием, терпимостью и толерантностью гораздо больше иных своих сограждан. слухи иногда даже превосходят своими размерами и глупостью обычную "бытовуху" о "красных радоактивных осадках" (ТМ). узость комьюнити также обрекает на неудачу любую попытку что-то скрыть. если действительно необходимо скрыть что-то, то об этом не должен знать никто. а такое не всегда возможно.
про команду:
боюсь сглазить и делать прогнозы, но похоже сегодня я присутсвовал при возрождении команды. очень здоровская вещь, господа мои, наблюдать как люди, работающие над одним проектом постепенно начинают прикладывать коллективные усилия, поддерживать друг друга, делить отвественность и работать как единый слаженный механизм организм. особенно отрадно наблюдать это в людях, которые до недавнего времени вели себя как сообщество фрилансеров. причем независимых друг от друга фрилансеров. можно поставить первый плюсик себе в новой должности. и большие плюсы техническому и проектному лидерам проекта. дай Бог, чтобы это стало правилом, а не исключением из правил.
про контору:
случайно вышел, через контекстную рекламу на LinkedIn, на объявление рекламно-рекрутингового характера. впечатление двоякое:


  • с одной стороны идея отличная. сколько раз я пытался выяснить ответ на вопрос: "а что конкретно представляет релокационный пакет" и не слышал на него ответа.

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

JavaScript tips&tricks

для того, чтобы сделать быстрый unescaping специальных символов в строке, достаточно употребить конструкцию:


...
var tmpDiv = document.createElement("div");
tmpDiv.innerHTML = <escaped_text>;
var unescaped = tmpDiv.innerHTML;
...

функция, аналогичная org.apache.commons.StringUtils.split:

function splitString(source, separator) {
    var result = new Array();
    var i = 0;
    var index = source.indexOf(separator);
    while (index > 0) {
        var token = source.substring(0, index);
        source = source.substring(index);
        index = source.indexOf(index);
        result[i++] = token;
    }
    result[i] = source;
    return result;
}

кроссбраузерный (Internet Explorer 6.x,7.x, Opera 9.x, FireFox 2.x) способ динамического добавления обработчика событий:

function eventListener(param) {
    alert(param);
}

function addOnClickListener(elementId) {
    var element = document.getElementById(elementId);
    element.onclick = eval("eventListener'" + "Hello JS world!" + "')");
}

п'ятниця, 11 квітня 2008 р.

весна: играй гормон


И что из того, что разорваны связи?
И что из того, что молчат провода?
И что из того, что все песни похожи
одна на другую, а та - на тебя?
В моей преисподней
Как будто бы людно,
но поздно сшивать уже
рану край в край,
и я разделяю все случаи жизни
на что было до и после тебя!
(с) Светлана Сурганова

в конторе, где я работаю введено честное свободное посещение. т.е. если ты справляешься с производственными задачами, если не подводишь коллег и у тебя не запланировано встреч, то приходить ты можешь когда угодно. действительно когда угодно. разумеется мои сотрудники пользуются таким положением вещей в полной мере. поэтому за полгода работы в конторе, я видел разное сотояние офиса, но полностью пустым я его видел дважды -- когда сам задерживался очень до поздна. каково же было мое удивление, когда уходя вчера в 20:30 я насчитал всего 5 человек, которые остались в офисе. играй гормон, не иначе :)

вівторок, 8 квітня 2008 р.

Аспектно-Ориентированное Программирование: Предисловие

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



действительно, логирование или трассировка программы -- это одна из классических задач АОП. но часто кажется, что одних средств АОП тут недостаточно и все-равно необходимо использовать логи внутри функционала, а раз так, то зачем писать что-то еще? пусть все логи будут внтури, раз нельзя их полностью отделить. на самом деле это не так. рабочую систему логирования я опишу позже, когда создам и оттестирую таковую :) пока поговорим о практических принципах применения АОП в среде Java + Spring. в документации по Spring очень много написано про АОП, фактически есть два способа реализации аспектов --с помощью Spring AOP и через AspectJ. я пока пользуюсь AspectJ аспектами, но возможно посмотрю и реализацию Spring.
итак, первое что необходимо понимать при аспектном программировании -- мы работаем с вызовами функциональности. т.е. елементарная операция для аспекта -- это вызов. мы можем описать для вызова пред- и пост-условия, обернуть выполнение метода какой-то функциональностью, котролировать метод после выброса исключения или после точки возврата. мы не можем обратиться к функционалу метода напрямую, но любой метод нашего класса есть на 90% набор вызовов других методов, а эти методы в свою очредь доступны через АОП. небольшая иллюстрация в коде, чтобы было еще понятнее:
предположим нам необходимо заполнить некторую коллекцию, пусть реализацию java.util.Map внутри класса:

package org.corwin.samples;

public class AopSample {
    private Map container = new HashMap();

    public void fillMap() {
        container.put("sample1", "something");
        container.put("sample2", "another thing");
    }
}

теперь опишем аспект, который будет контролировать AopSample класс:

package org.corwin.samples.aspects;

public class SampleAspect {
    public void beforeFillMap() {
        System.out.println("Call before Map fill with values");
    }

    public void afterFillMap() {
        System.out.println("Call after Map fill with values");
    }
}

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

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
">
    <bean id="sampleClass" class="org.corwin.samples.AopSample"/>
    <bean id="sampleAspect" class="org.corwin.samples.aspects.SampleAspect"/>
    <aop:config>
        <aop:aspect ref="sampleAspect">
            <aop:pointcut id="mapPutCut" expression="execution(* java.util.Map.put(..))"/>
            <aop:before method="beforeFillMap" pointcut-ref="mapPutCut"/>
            <aop:after method="afterFillMap" pointcut-ref="mapPutCut"/>
        </aop:aspect>
    </aop:config>
</beans>

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

вівторок, 1 квітня 2008 р.

Monday hard

сегодняшний, а вернее уже вчерашний понедельник, оказался действительно днем тяжелым. началось все с того, что я решил переселится сюда. несмотря на полярные мнения, я доверяю сервисам Google. и когда мне захотелось сменить площадку, то я выбрал именно эту, насколько хорош был выбор -- покажет время. я долгое время что-то подкручивал, что-то менял в шаблоне, копировал статьи той тематики, которой я бы хотел здесь видеть, из моего старого блога. а потому в офис добрался уже после полудня. как говорится ничто не предвещало беды. привычный офис, привычные лица, набор работ, запланированный заранее...
все пошло кувырком сразу после приема утреней почты, в которой оказалась задача по бизнесс-аналитике. эту задачу я успешно делегировал стразу двоим Java Developerам, с прозьбой посмотреть и определится будет ли им инетерсно этим заниматься. потом случился митинг с моим СТО по поводу деятельности отдела, затем по плану был английский, на деле -- получился минимитинг в курилке с СТО тестров, где довольно полезно и толково обсудили необходимость формализации процессов. если все пойдет хорошо, сможем наладить взаимодействие между отделами. потом таки случился английский, но не надолго -- меня выдернул менеджер проекта, на очередной митинг по статусу одного из Java проектов. весьма проблемного проекта, между прочим. обсудили причины срыва сроков итерации, впервые я увидел заинтересованность людей в признании и исправлении ошибок. понимание необходимости системных изменений в проекте. даст Бог, будет и тут продвижение.
затем я переговорил с заказчиком одного из моих основных и наиболее технологически интересного проекта. как же замечатльно работать с людьми, которые смотрят по стронам, понимают, что Head of Java Department -- это не просто "фуражка", а еще и множество обязанностей. общением с этим заказчиком я пока очень доволен. довольно редко мне получалось видеть людей открытых, понимающих и адекватных с другой стороны. может потому, что люди были другие, а может потому, что я не контактировал с этими людми напрямую, или контактировал не в полном объеме.
остаток дня (вечера, ночера) я занимался морально-технической поддержкой операции по спасению итерации нашего проблемного проекта :)
итоги дня радуют, похоже у нас получает сформировать команду и восптитать у людей отвественность за работу. это то, чего реально не хватает проекту. конечно хотелось бы это делать менее экстремальными методами, но все-таки позивный результат прослеживается. еще одним итогом митингов можно считать понимание необходимости создания технического профайла проектов, я искрене надеюсь, что это останется не просто договоренностями, но и обрете реальную форму.
а еще день поставил вопрос абсолютно неогранизованного процесса доставки сотрудников по домам после таких вот ночных посиделок. нужно эскалировать этот вопрос наверх.
самый главный итог дня для меня наверное заключается в том, что жизнь моя снова стимулирована плотным потоком событий. событий важных, нужных и целевых. ощущение, которое к моему великому сожалению начало уже забываться.

понеділок, 31 березня 2008 р.

JSR-168 Portal + JSF: Работа с настройками портлетов

копия моей статьи на Liveinternet.ru от 9 октября 2007

все порталы, удовлетворяющие спецификации JSR-168, предоставляют стандартный механизм доступа и модификации настроек портлетов, посредством реализации интерфейса javax.portlet.PortletPreferences. в данной заметке рассмотрим способ работы с настройками портлета, реализованного с помощью технологии JavaServer Faces.


быстрое решение
основным интерфейсом связи с внешней средой для JSF технологии является класс javax.faces.context.FacesContext. параметры портлета, сведения о типе портального запроса, настройках режима и вида портлета храняться в классах-реализациях интерфейса портального запроса javax.portlet.PortletRequest. сведенья о пространстве имен портлета храняться в реализациях интерфейса портального ответа javax.portlet.PortletRespose. для связи всех этих классов и получения доступа к специфическим параметраметрам и установкам портлета реализуем утилитарный класс:

public final class PorletFacesUtils {
    private PortletFacesUtils() {
    }

    public static PortletRequest getPortletRequest(FacesContext context) throws Exception {
        Object request = context.getExternalContext().getRequest();
        if (request instanceof PortletRequest) {
            return (PortletRequest) request;
        } else {
            throws new Exception(“Portlet run outside the portal environment or portlet request class is not generic”);
        }
    }

    public static PortletRequest getPortletResponse(FacesContext context) throws Exception {
        Object response = context.getExternalContext().getResponse();
        if (request instanceof PortletResponse) {
            return (PortletResponse) response;
        } else {
            throws new Exception(“Portlet run outside the portal environment or portlet response class is not generic”);
        }
    }

    public static PortletPreferences getPortletRequest(FacesContext context) throws Exception {
        PortletRequest request = getPortletRequest(context);
        return request.getPortletPreferences();
    }
}

таким образом мы реализовали своеобразный мост между JSF-приложением и специфическими классами портальной среды. далее этот класс может быть использован для работы с параметрами и настройками портлета. также класс расширен для получения других классов, специфичных для портальной среды (например javax.portlet.PortletSession, javax.portlet.PortletContext и т.п.).
риски
некоторые фреймворки JSF компонентов (например ICEfaces) использую свои врапперы портальных запросов и ответов. эти врапперы могут не являться наследниками стандартных интерфейсов, а следовательно будут ошибочно обработаны утилитарным классом. в этом случае необходимо реализовать механизм адаптера портальной среды для конкретной реализации враппера. как это сделать, я расскажу ниже.

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

public interface PortletFacesAdapter extends Serializable {
   PortletRequest getPortletRequest(FacesContext context);

   PortletResponse getPortletResponse(FacesContext context);

   PortletPreferences getPortletPreferences(FacesContext context);
}

перенесем функциональность утилитарного класса в реализацию интерфейса адаптера:

public class GenericPortletFacesAdapter {
    public PortletRequest getPortletRequest(FacesContext context) throws Exception {
        Object request = context.getExternalContext().getRequest();
        if (request instanceof PortletRequest) {
            return (PortletRequest) request;
        } else {
            throws new Exception(“Portlet run outside the portal environment or portlet request class is not generic”);
        }
    }

    public PortletRequest getPortletResponse(FacesContext context) throws Exception {
        Object response = context.getExternalContext().getResponse();
        if (request instanceof PortletResponse) {
            return (PortletResponse) response;
        } else {
            throws new Exception(“Portlet run outside the portal environment or portlet response class is not generic”);
        }
    }

    public PortletPreferences getPortletRequest(FacesContext context) throws Exception {
        PortletRequest request = getPortletRequest(context);
            return request.getPortletPreferences();
        }
}

теперь нам необходимо реализовать JSF managed bean, который будет поддерживать механизм адаптеров:

public class AdaptableBean implements Serializable {
   private PortletAdapter adapter;

   public PortletAdapter getAdapter() {
      return adapter;
   }

   public void setPortletAdapter (PortletAdapter adapter) {
      this.adapter = adapter;
   }
}

фактически мы реализовали механизм полноценной работы JSF framework внутри портальной среды. осталось только проиллюстрировать его конфигурацию. итак реализуем простой класс JSF managed bean, который используется в портлете:

public class TestBean extends AdaptableBean {
}

и сконфигурируем портлет, для использования этим классом GenericPortletFacesAdapter адаптера:

<faces-config>
   <managed-bean>
      <managed-bean-name>portletAdapter</managed-bean-name>
      <managed-bean-class>GenericPortletFacesAdapter</managed-bean-class>
      <managed-bean-scope>application</managed-bean-scope>
   </managed-bean>
   <managed-bean>
      <managed-bean-name>testBean</managed-bean-name>
      <managed-bean-class>TestBean</managed-bean-class>
      <managed-bean-scope>request</managed-bean-scope>
      <managed-property>
         <property-name>adapter</property-name>
         <value>portletAdapter</value>
      </managed-property>
   </managed-bean>
</faces-config>

ссылки
Java Portlet API
JavaServer Faces API
статья на английском языке