Спустя 4 года и 5 месяцев решил поднять из бэкапа свой блог.Что с ним делать дальше ещё не решил. Пока обновил WordPress и переехал на новый хостинг - VPS-ка от reg.ru. Пока всё устраивает. До этого, когда-то давным давно, пользовался услугами brim.ru, потом кажется DigitalOcean, потом очень долгое время блог жил на виртуалке, которую друзья дали. Это был очень крутой подарок.
Мне первые несколько лет было интересно писать заметки и статьи в блоге. Потом надоело, наверное перегорел. Когда поднял бэкап, обнаружил в черновиках пару недописанных статей - одна про JMX, другая "поток мыслей" про будущее ИТ. Буду думать, что с ними делать. Наверное просто удалю. Смысла нет, поскольку многое поменялось за это время. Персональные блоги почти все переехали на medium, а сайты сейчас делают на тильде (а может уже и нет). Да и в целом мир изменился, я изменился. Java изменилась. WordPress изменился. Его новый интерфейс непривычен, но обещает новые возможности.
На этом всё. Пусть это будет первая пробная статья после длительного перерыва.
Мультикарта (multi-map) может пригодиться в реальной работе или на собеседовании. Почему-то в некоторых компаниях при приёме на работу любят давать алгоритмические задачки на её использование (лично я так не поступаю).
По сути это обычная карта (Map) в которой значением является коллекция (List или Set).
Сейчас в JCF (Java Collections Framework) нет готового класса для Multimap.
До выхода Java 8 приходилось логику работы писать вручную.
Например так.
Допустим, нам поступают такие данные:
Moscow=ru
Omsk=ru
Tula=ru
NY=us
LA=us
London=uk и т.д.
Их нужно представить в виде:
ru=Moscow, Omsk, Tula
us=NY, LA
uk=London
Тогда логику можно реализовать, например, в таком виде:
Какой самый лучший шрифт для программиста?
Каждому своё.
Одним нравятся округлые очертания, другому наоборот тонкие и острые. Поэтому выбор шрифта — дело вкуса, хотя конечно есть базовые требования.
Самое главное и очевидное — шрифт должен быть моноширинным, т.е. все символы должны быть одной ширины. Это очень важно для того, чтобы сохранить структуру кода, т.к. иначе будут плыть все отступы и исходный код будет плохо читаться.
Если человек пришел в программирование с дизайнерским/типографским прошлым, такой шрифт его может раздражать. Моноширинные шрифты не самые приятные для чтения, хотя их используют не только программисты-техногики, но и люди искусства.
Например, если верить википедии, в западной театральной и кинематографической традиции сценаристы используют шрифт Courier-12. Одна страница такого сценария длится примерно одну минуту. Вроде это какой-то даже у них стандарт де-факто в отрасли.
Кроме моноширинности, при выборе следует обратить внимание на следующее:
- Цифра 0 (ноль) должна отличаться от буквы O.
- Цифра 1 (один) должна отличаться от буквы l (маленькая L).
Что касается меня, то сейчас я использую шрифт Consolas.
Специфика Linux.
На момент написания этой статьи у некоторых пользователей Linux (Ubuntu) есть определенное недовольство качеством отображения шрифтов в IDE.
Итак, что можно попробовать сделать.
Во-первых, я ставлю набор микрософтовских шрифтов:
$sudoapt-get install ttf-mscorefonts-installer
$sudo apt-get install ttf-mscorefonts-installer
Во-вторых, особо чувствительные к шрифтам могут поставить Infinality.
В-третьих и самое главное. Это не сильно помогает, т.к. на момент написания этой статьи в Java (точнее в её графической подсистеме AWT/Swing) есть какие-то проблемы с красивым отображением шрифтом в Ubuntu. Другими словами проблема актуальна для Netbeans и Android Studio.
Единственное нормальное решение я нашел здесь askubuntu.com.
Итак чуть более подробный пересказ, что нужно делать.
С json парсерами в Java дела обстоят следующим образом. С одной стороны их существует несколько разновидностей, с другой стороны сложно сделать выбор.
Хорошо когда только один критерий принятия решения, например скорость работы или размер библиотеки. Сложности начинаются тогда, когда нужно выбрать с учетом нескольких характеристик.
В этой небольшой заметке я хотел бы кратко описать случаи, когда уместно использование библиотеки org.json.
Итак, кратко плюсы и минусы.
Минусы:
- Далеко не самый быстрый парсер.
- Нет автоматической сериализации в JavaBean-ы.
Плюсы:
+ По-умолчанию есть в стандартной поставке Android
+ Простое API
Занятные факты:
~ Первые версии этой библиотеки были написаны Дугласом Крокфордом (англ. Douglas Crockford) — создателем формата JSON.
Теперь чуть подробнее.
Наличие в стандартной поставки Android API это действительно плюс, с учетом лимита в Android-е на кол-во методов в 64К (65 536 шт) любая дополнительная библиотека может обернуться лишней головной болью.
Другими словами, если нет необходимости разбирать большие массивы данных в json и структура данных примитивна, то им вполне можно обойтись. Типичный пример - небольшой конфигурационный файл, в котором всего около десятка различных параметров.
В случае если объем данных занимает мегабайты, а ручной разбор и мэппинг в бины - утомителен и однотипен, то естественно надо от него уходить в сторону других решений.
Лично я предпочитаю в таких случаях использовать gson, но это дело вкуса и технической необходимости. Никаких предубеждений против того же jackson-a я не имею.
В стандартной Java появление легковесного API пока не предвидится. В новостях были сообщения, что JEP 198: Light-Weight JSON API не будут включать в Java 9, аргументируя это тем, что и так много опенсорных json-парсеров.
Поэтому если все-таки нужен именно "org.json" в стандартной Java, то его можно подключить через мавен.
При этом надо понимать, что во-первых в нормальной Java (не Android) нет ограничений в 64К методов, во-вторых нет стандартного json парсера, поэтому использование org.json не столь очевидно.
Что касается Java EE, в нем есть стандартный javax.json пакет с классами для работы с JSON-ом.
В этой статья я расскажу про свой опыт, про других, про мировые практики и попробую дать советы в выборе компьютера для программиста.
Мой опыт.
Думаю разумнее начинать с того момента как начал работать на себя. Когда трудишься наемным сотрудником в компании, то проблемы выбора как правило нет — работаешь на том оборудование, которое принято использовать у работодателя. В серьезных софтверных компаниях, как правило, возможны различные варианты по конфигурации железа, в мелких компаниях большой свободы выбора нет. Как правило в любых нормальных компаниях стараются не сильно зажиматься по деньгам на оборудование, отчасти это связано с тем, что труд программиста в софтверных компаниях является основным источником дохода, а также с тем, что компьютерная техника относительно быстро амортизируется.
Кратко, как было у меня. Изначально, кажется в 2009-ом году, когда нужно было много ездить по разным встречам, я предпочитал небольшие ноутбуки. В частности, у меня был 13-дюймовый ноутбук (BenQ T31) с достаточными на тот момент характеристикам для компиляции и запуска IDE. Продержался я на нем не так долго, т.к. на маленьком экране и компактной клавиатуре не очень удобно работать. Выходил из положения подключением внешнего монитора (кажется 21-дюймовый Samsung) и клавиатуры.
Через некоторое время я заменил BenQ на HP Probook 4720s и отправил старый ноутбук на дачу. Новый ноутбук был полноценной боевой машиной весом больше трех килограмм с 17-дюймовым экраном и нормальной полноразмерной клавиатурой. В целом машиной был доволен, но пришлось приобрести специальный большой компьютерный рюкзак.
Работать на выезде у заказчика и в поездках стало почти также комфортно как и за рабочим столом. Приблизительно в это же время я перестал работать дома и переехал в коворкинг (на ДЗ Флакон) с фиксированным рабочим местом. При этом от стационарного монитора и клавиатуры я не отказался. Правда я заменил Samsung на ViewSonic чуть большей диагонали.
Через некоторое время я взял новый ноутбук ASUS G550JK серии ROG (Republic of Gamers). Старый ноутбук я отдал племяннику, он на нём учится программировать. Новый ноутбук хоть и был из игровой серии (Asus ROG) именно эта модель получила много негативных отзывов от геймеров, поскольку в нем не самая мощная видеокарта. Для моих задач, как программиста это было не важно, главное было то, что в нем стоит неплохой процессор (i7) и 16 Gb памяти.
Через некоторое время я сменил внешний монитор ViewSonic на DELL с 24". Замена мне понравилась. Старый ViewSonic показал себя не с лучшей стороны, первые полтора года отработал без проблем, но потом пару раз сломался, даже пришлось обратиться в гарантийным отдел. Я редко обращаюсь в сервис, обычно не чиню, а просто покупаю новое оборудование и перестаю пользоваться изделиями этого бренда, просто у этого монитора оказалась очень длительная гарантия, но тем не менее, думаю в ближайшее время ViewSonic-ами пользоваться не буду.
Что касается периферии - мышек и клавиатур, то я меняю их достаточно часто (раз-два в год). Любимые бренды - A4Tech, Logitech и Microsoft. Кстати кто не знает, у микрософта в линейке клавиатур и мышек всегда имелись неплохие модели. Сейчас работаю на Microsoft Wireless Comfort Desktop 5000, уже больше года. Пока это для меня самая удобная клавиатура.
В такой конфигурации я проработал около года, потом опять всё немного поменялись и я перестал таскать с собой ноутбук на встречи, клиенты стали чаще приезжать сюда в коворкинг. Спустя несколько месяцев у меня оказалась без дела рабочая станция на DELL и я решил переехать с ноутбука на стационарный компьютер. Для того чтобы принять это решение пришлось себя немного "поломать". Сложно было принять рациональное решение, тем не менее из плюсов - еще лучше железо, больше свободного места на столе и самое главное, не нужно вечно таскать как черепашка-ниндзя на своей спине рюкзак с ноутбуком. После этого у меня началась настоящая "Dolce Vita!" для программиста.
Приезжаю утром в коворкинг, налегке, без рюкзака, выпиваю чашку кофе, в холле играет легкий джаз, поднимаюсь в комнату, сажусь в кресло и включаю компьютер. Всё уже настроено для комфортной работы - не нужно подключать клавиатуру, монитор, мышку. В комнате - как в научной библиотеке, никто громко не говорит, а для разговора по телефону или между собой все выходят. Сохраняется рабочая атмосфера, только мерное шуршание клавиатуры и запах утреннего кофе. Если хочется общения, то можно спуститься к барной стойки на первом этаже стартхаба, плюс каждую неделю проходят программистские встречи.
Вот так выглядит мое рабочее место.
Мои друзья.
Безусловно такое непостоянство в смене компьютеров возможно является следствием моего темперамента или типом личности. Например, мой друг купивший где-то в конце 2000-ных(!) Thinkpad T520 остается ему верен до сих пор. Безусловно это отличная модель, с хорошими характеристиками, четким линиям и выразительными чертами в дизайне. Второй его ноутбук, это Asus ROG, стилизованный под спорткар Ламборгини, обладает ещё большей маскулинностью. Это тяжелая брутальная заряженная машина сделанная в черно-матовом дизайне. Он тяжелый, массивный и очень резвый.
Если взять другой пример, например вы хрупкая девушка-программист, которая пишет мобильные приложения под iOS и выбираете ноутбук, то в таком случае нужно брать MacBook (или какой-нибудь другой компьютер от Apple). Это будет самый правильный способ и если не хочется каких-то острых извращений, то для эффективной разработки iOS-приложений компьютеры Apple будут самым нормальным решением. Плюс конечно, макбуки это просто красиво и у них действительно неплохая аппаратная платформа. Хотя ничто не идеально, сломаться может всё что угодно, а ремонт мака может обойтись существенно дороже обычного pc.
Так получилось, что мы пишем приложение для Android TV.
Вообще, обычно я занимаюсь тем, что в программисткой среде принято называть "суровым энтерпрайзом" — веб-службы, высокая нагрузка и прочие Java EE штуки.
Тем не менее, иногда мы делаем проекты и под мобильные платформы. Как правило это заказная разработка, например мобильные приложения для внутренних нужд компании (приложения для сотрудников организации/диспетчеров/водителей, интерактивные учебные пособия, системы учета/бронирования, системы мониторинга и так далее), которые потом обычно и интегрируют с бэкендом на "суровом энтерпрайзе". Этими приложениями пользуется персонал компании, а не простые пользователи. В большинстве случаев они даже не публикуются на маркете.
Естественно из этого есть исключения.
Итак. Да, мы пишем под Android TV. Не знаю, много ли существует мобильных разработчиков в России, которые что-то делают под эту платформу. Понятное дело, есть крупные компании, которые занимаются производством и распространением телевизионного и медиа контента и им сам Бог велел заниматься созданием приложений под эту платформу. Когда-то давно, я тоже делал мобильное приложение для одного популярного в определенных кругах телеканала, но сейчас не об этом.
У нас немного другая история. Мы изначально делали обычное приложение (с кнопочкам и картинками), и по началу думали, что оно будет работать на телефонах, планшетах и китайских свистелках-поделках - "тв приставках". Потом внезапно выяснилось, что платформа Android TV подошла на удивление хорошо для целей проекта.
Про Android TV.
Как обычно, я не буду в своем блоге переписывать/переводить учебник и официальную документацию. Считаю это тяжелой и отчасти неблагодарной работой, поскольку документация обновляется у Google постоянно и во-первых я за ней просто не буду успевать, а во-вторых большинство программистов итак читают документацию у первоисточника.
Поэтому я расскажу про детали, те самые которые затронули и показались лично мне очень важными.
1. Нормального эмулятора нет (как обычно). Мы используем оригинальный, привезенный нам из забугорья Android Nexus Player. Настоящий суровый американский Nexus Player с плоской вилкой на 110 В. Без намёка на РОСТЕСТ. Без такого устройства нормальная разработка под Android TV была бы затруднительна. Как и все Nexus-ы он неплохо подходит для целей отладки и разработки. Нет проблем с драйверами, подключением по adb и т.д. Это наше основное девелоперское устройство в этом проекте.
2. Конечно мы проводим бета-тесты еще и на телевизоре Sony. Как выяснилось, это вполне такой обычный телевизор, просто на нем установлен Андроид.
Естественно кроме телевизора и Nexus-а, перед тем как выложить приложение, нам приходится тестировать приложение на десятках разных телефонах и планшетах (те кто пишет под Андроид знают, что это не гипербола, а суровые будни андроид программиста).
Так вот, на телевизоре что-либо дебажить очень не удобно, поэтому опять таки основным устройство у нас является Nexus Player.
3. В какой-то момент Nexus Player внезапно обновился на Marshmallow (Android M) (что в прочем для нексусов вполне типично).
В итоге у нас в руках появился очень редкий, можно сказать "краснокнижный" зверь. Устройство под Android TV (которых мало) и с 6-ой версией Android-а (которых еще меньше). Это прямо-таки удивительное и непредсказуемое создание. Часто было не понятно, особенности в его поведении были связанны с тем, что это был шестой андроид или потому, что это Android TV.
Выводы.
Стоит ли писать под Android TV? Думаю решать нужно исходя из целей проекта. Приведу свои, субъективные размышления на эту тему (актуально на момент публикации этой статьи).
1. ТВ стали смотреть меньше. Возможно я ошибаюсь, но это общий тренд. Я не уверен, что в будущем вообще будут много смотреть телевизоры.
2. Играть на Android TV в игры - сомнительное удовольствие. Я конечно не геймер, но мне кажется, что уж лучше подключить XBox.
3. Платформа требует отдельного внимания и тщательного изучения API.
Текстовый ввод не удобен (т.к. пульт). Если у вас много элементов управления (текстовых полей, кнопок) или используется тач, то придётся все эти моменты перерабатывать.
4. Аудитория. Может я не прав, но если сравнивать со всей массой пользователей андроид устройств её почти нет.
Вживую телефон с андроидом я видел у многих. Телевизор с Android TV только один.
5. Как операционная система Android для телевизоров удобней чем Smart TV. Это лично мое мнение, я могу сравнить, т.к. дома у меня телевизор LG с webOS, а на работе тестируем на Sony с Android-ом.
Мое личное мнение, использовать Android TV приложения, только в том случае, если это действительно вам подходит.
Сил и времени потратите уйму. Если делать по-уму, нужно оптимизировать пользовательский интерфейс (иначе приложением нельзя будет пользоваться), обрабатывать нажатие с пульта, менять систему навигации и многое другое.
Стоит ли следить за развитием платформы? Да, однозначно стоит. Бросить всё, уволиться с работы, продать машину и квартиру и начать делать ещё один клон "ангри бёрдс" под Android TV, в надежде что он вас озолотит — на мой взгляд не самая здравомыслящая идея.
Делать прогнозы сложно, но вряд ли эта платформа даст такой же взрывной рост как и обычные мобильные приложения. Android TV — очень специфичный продукт.
В целом, ситуация с ним чем-то напоминает бум умных часов. Было очень много маркетинговой шумихи, а в итоге никакого стремительно роста никто пока не наблюдает. Эти умные часы я тоже купил, вроде как на всякий случай, думал буду что-то писать под них, но пока они просто лежат у меня в ящике рабочего стола.
PS: Хотел уточнить, чтобы не возникло искаженное понимание ситуации, т.к. я с одной стороны критикую платформу, а с другой стороны пишу что мы под нее активно разрабатываем. Да я считаю, что нет особого смысла в обычных приложениях под Android TV. У нас совсем другой случай, мы делаем специализированный продукт для B2B-сектора по заказу. Другими словами, пишем приложение специально предназначенное для работе на телевизоре.
Во-первых (и это мое личное мнение), использовать такую нотацию в исходниках на Java действительно нелепо.
Во-вторых, когда-то давно я grep-ом проходил исходники андроида (кажется 16-ый API Level) и там нотация не соблюдалась. Более того она была некорректной (статические поля с m-префиксом).
Коворкинг интересен тем, что в нем можно встретить интересных людей.
Следующая история произошла после краткой беседы с Димой Гарником, арт-директором небольшой дизайн-студии.
У Димы была идея создать простой сайт для автовизиток. Идея несложная, но интересная. Вы заходите на сайт, указываете номер телефона и сообщение (например: "Мешает машина? Звони!"). Затем вы получаете развертку, из которой можно собрать симпатичную машинку. Развертка - это PDF-файл, который можно распечатать на листе бумаги формата А4 и собрать. Самое главное: сборка идет как "оригами", т.е. без ножниц и клея, нужны только "прямые" руки и время.
В итоге получается такая штука:
Собственно, нужно было как-то реализовать генерацию этих разверток.
Так совпало, что Дима подошел и рассказал про свою идею именно в тот момент, когда мы с соседом пили кофе и вели досужие разговоры на тему: "что бы такого сделать интересного и не сложного". Димина идея показалась интересной и не очень трудоемкой.
Генерацией PDF-файлов мне уже приходилось заниматься в прошлом (хотя это и была, в основном, генерация документов и отчетов с использованием различных выборок из БД - принципиальной разницы я не видел).
В общем, я сделал эту штуку.
Про FOP, SVG, PDF
Приблизительный план, что и как нужно делать, в целом, был понятен сразу:
1. Берем рисунок в векторе.
2. Сохраняем его в SVG-формате.
3. Поскольку SVG - это обычный XML, то менять в нем содержимое (номер, сообщение, цвет и т.д.) несложно.
4. Прикручиваем преобразователь SVG → PDF.
5. Проверяем, что все это работает, и делаем к этому "обвес", который обрабатывает HTTP запросы и отдает пользователю PDF-файл.
6. Прикручиваем дизайн или интегрируем с текущим сайтом.
В целом, все прошло согласно плану. За исключением следующих моментов:
1. Проблемы со шрифтами.
Конечно, все зависит от SVG-файла, но, возможно, возникнут сложности с кириллическими шрифтами после развертывания на сервере (т.е. на Linux-е).
Решение: поставить недостающие (зависит от того, какие нужны) шрифты.
Например, как-то так:
apt-get install ttf-mscorefonts-installer
2. Еще раз проблемы со шрифтами.
У того, кто раньше работал с Apache FOP, могут возникнуть сложности в более тонкой настройке конфигурации.
Дело в том, что формат файла для настройки PDFTranscoder-а отличается от привычного fop.xconf!
Если кратко:
//Создает перекодировщик. Кроме PDF, если верить документации есть перевод в PostScript, EPS.
Transcoder transcoder =new PDFTranscoder();// Если нужно "вшивать" свои шрифты, требуется дополнительная настройкаtry{
DefaultConfigurationBuilder cfgBuilder =new DefaultConfigurationBuilder();
Configuration cfg = cfgBuilder.buildFromFile(newFile("pdf-renderer-cfg.xml"));
ContainerUtil.configure(transcoder, cfg);}catch(Exception e){thrownew TranscoderException(e);}
//Создает перекодировщик. Кроме PDF, если верить документации есть перевод в PostScript, EPS.
Transcoder transcoder = new PDFTranscoder();
// Если нужно "вшивать" свои шрифты, требуется дополнительная настройка
try {
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
Configuration cfg = cfgBuilder.buildFromFile(new File("pdf-renderer-cfg.xml"));
ContainerUtil.configure(transcoder, cfg);
} catch (Exception e) {
throw new TranscoderException(e);
}
Сейчас это кажется очевидным. Но тогда, чтобы понять, как избавиться от крякозябр в картинках, пришлось потратить почти столько же времени, сколько на всю оставшуюся разработку.
На этом технические детали хотелось бы оставить в стороне и написать пару слов про дизайн и его значение.
Как правило, чем более "программистская" контора, тем сильнее доминирует принцип - делаем "not pretty but functional", а затем дизайн (если остались деньги).
Все бы хорошо, но бывают заказчики, которым потом жалко тратить деньги на нормальный дизайн: им главное, чтобы работало. Как правило, это касается разработки внутренних систем. В целом, это нормальная бизнес-ситуация: лишних денег на "красивости" часто нет. Правда в том, что в итоге сотрудникам некоторых компаний приходится годами пользоваться унылым корпоративным ПО.
Если приводить аналогию, то в таком случае, сайт для создания автовизитки без дизайна выглядел бы примерно так: Без дизайна
Для сравнения, вот так он выглядит сейчас: С дизайном
С другой стороны, если поставить дизайн во главу угла, может получиться ситуация, когда дизайн есть, а функциональности нет.
Красиво выглядит? Да, бесспорно! Работает? К сожалению, нет. При этом могут пройти месяцы, пока из красивого получится работающее. Ситуация может усугубиться, если дизайнер "понапридумывает" разного, а это потом не будет работать или окажется невозможным реализовать.
Многие скажут: "К чему эти банальности? Это и так понятно!". Понятно, но хотел бы повторить: нормальный дизайн - это не опция, это неотъемлемая часть хорошего программного продукта. Такая же составляющая, как и его функциональность.
PS: Может кто-то знает нормальные курсы дизайна по UI/UX? Не про "сферического коня в вакууме", а про конкретные практики - как пользоваться инструментами. И не про общие принципы: что нужно выравнивать, что какие-то цвета плохо сочетаются и т.д., - про это многое уже известно.
Проблема в том, что иногда по мелочи нужно что-то сделать (например, у меня бывают вопросы, как в фотошопе кнопку перекрасить из серого в красный) и это вызывает определенные сложности. Спасибо соседям по коворкингу, что выручают (Николай, Надежда - это я про вас ;).
В общем, если кто-то из программистов, читающих мой блог, бывал на таких курсах - был бы рад услышать про них отзыв: стоит или нет.
PPS: Если, вдруг, какой-то дизайнер забредет на мой программистский блог - буду рад возможности сотрудничества. Пишите в комментариях ;).
Проекты бывают разные. Некоторые можно сделать за два дня, некоторые за месяц, некоторые длятся годами. Именно про последние хотел написать эту заметку.
Дело в том, что общаясь в среде различных программистов с завидной периодичность наблюдаю следующую сцену. Один разработчик рассказывает и показывает итоги своей работы другим программистам в надежде получить отзыв вроде: "Ну ты крууууут!", в ответ же он получает не возгласы восхищения и аплодисменты, а вопрос: "На что именно было потрачено столько времени? На ЭТО? Что тут такого делать, почему так долго?". После этой фразы начинаются жаркие споры, обиды, непонимание и обвинения в некомпетентности друг друга.
Хорошо когда такие беседы проходят в дружеской программисткой атмосфере. Гораздо сложнее, когда выяснение отношений происходит на другом уровне. Например, когда одна команда пытается подрезать заказ у другой, убеждая клиента в том, что если бы обратились к ним, то они дескать сделали бы все гораздо быстрее и без затягивания сроков. Что принятые решения изначально были нерациональными и т.д.
Более того, сами программисты спустя годы, оборачиваясь назад начинают думать: "Я потратил на это три/пять/семь лет своей жизни? Почему?! Как ЭТО заняло столько времени?"
Одной из причин, на мой взгляд, является не совсем точное представление о том, как оценивается сложность проекта. Для многих это прямая из точки А в точку B.
Пред глазами предстает следующая картинка.
На самом деле, это идеальная картина. В жизни разработка сложного проекта сопровождается решением многих неучтенных проблем. Возникают "развилки" на которых нужно принимать решение куда двигаться дальше. Это могут быть решения высокого уровня, такие как выбор языка (java, c#, python или всё вместе) или способ хранения данных (mysql, oracle, berkley db, mongodb, hazelcast или все вместе), так и более низкого уровня - какой веб-фреймворк выбрать (ext или gwt, или может быть JSF 2 и т.д.), прикрутить velocity или обойтись стандартными средствами для работы со строками, начать использовать более популярный graddle или остаться на maven-е (или даже ant-e), использовать AnglularJS или не стоит.
Таких "точек бифуркации" в большом проекте может быть несколько десятков или даже сотен. При этом важно понимать, что все могут ошибаться и иногда приходится возвращаться на исходную позицию, менять стратегию, платформу (или как говорят программисты "переписать половину кода"). Кроме этого цель из точки B может передвинуться в точку B' и в результате некоторые принятые решения уже могут оказаться неподходящими.
Другими словами лучше описывает ситуацию следующая картина.
В итоге, полученное "дерево решений" получается довольно ветвистым, а его "построение" делается методом "разведки боем" и занимает уйму времени. При этом, когда человек уже находится в конечной точке B и если он не страдает потерей памяти, то скорее всего повторить этот путь ему будет намного проще. Он уже знает как добраться к цели, по каким именно дорогам идти и на каком перекрестке свернуть.
Конечно более сложный случай, когда нет хороших проторенных "дорог". Например, если это принципиально новая разработка. В таком случаях утверждение "Что он тут такого делает уже N-цатый год?", звучит аналогично претензии к Колумбу: "Что тут такого? Здесь и ежу понятно, что нужно просто плыть на запад и попадешь в Америку". Сегодня это кажется очевидным, а тогда найти средства на подобную авантюру было не так уж и просто. Опять-таки в итоге Колумб все-таки ошибся где-то в три с половиной раза (почти на Пи), ведь изначально он хотел попасть в Индию, а она намного дальше...
Возвращаясь в наши дни, к теме этой заметки, хотел подвести небольшое резюме.
Чтобы оценить сложность проекта нужно проделать большую работу (см. картинку с деревом решений). Не стоит прогибаться, когда заказчик требует выдать ему оценку сложности какого-то нового проекта в человеко-часах, суть которого вам изложили только что на словах в трех предложениях за чашечкой кофе. Реальная цена такой оценки наверное будет меньше стоимости выпитой вами чашки кофе.
Не стоит удивляться и сильно корить себя за количество потраченного времени. Во-первых в жизни часто сложности забываются, поэтому возникает кажущееся ощущение легкости пройденного пути, а во-вторых задним умом мы все гении.
Желательно искать хорошего "проводника", у которого был опыт работы если не в аналогичных, то хотя бы в похожих проектах. Естественно, что каждый проект в чем-то уникален, но возможно его интуиция и опыт позволит частично избежать попадания в тупиковые ситуации.
P.S. В качестве иллюстрации нарисовал дерево, на развилках которого выбирается один вариант и подсвечивается красным цветом. При нажатии мышкой на дерево, оно случайным образом перестраивается.
Приятного просмотра!
P.P.S. Дерево прорисовывается с помощью языка программирования processing. Собственно экспериментами с ним, а также arduino и raspberry pi сейчас посвящены воркшопы, которые мы проводим вместо проводимых ранее Scala-тусовок.
Отправка простых HTTP запросов из Java уже хорошо описана в различных источниках, например в официальном туториале, поэтому пересказывать в очередной раз как работать с URL и выкладывать примеры кода не вижу смысла. В случае если приходится работать с защищенным HTTPS соединением, также особых осложнений у большинства программистов возникнуть не должно (возможно кроме случаев с самозаверенными сертификатами, но на эту тему также очень много учебного материала в интернете). Несколько больший интерес представляют собой случай, когда HTTPS подключение требует дополнительной авторизации по клиентскому сертификату. Вот об этом и пойдет речь в этой статье.
Для многих пользователей такой способ авторизации выглядит немного экзотичным, т.к. встречается он не часто, хотя работает "из коробки" в большинстве браузеров и поддерживается многими веб-серверами.
Более распространен такой способ авторизации не в клиент-серверных решениях, а скорее в межсерверном взаимодействии. Например при решения задач в сфере B2B, когда необходимо отправлять запросы к различным серверам контрагентов и внешним системам. Отчасти это связанно с тем, что одним из популярных способов создания таких коммуникаций является использование веб-служб, что в свою очередь означает программную отправку HTTP запросов. Поскольку внутренняя реализации JAX-WS, базируется на использовании стандартных классов (URL, HttpURLConnection и т.д.), решив задачу в общем виде (отправка https запросов с авторизацией по сертификату) автоматически решается задача для клиента веб-службы (речь идет о JAX-WS, в Axis2 задачу решать можно немного другим способом).
Самое простое что можно сделать для решения этой задачи — указать ключи и пароли в системных свойствах:
В данном случае в файл privateKey.jks вы можете положить свой приватный (закрытый) ключ, с помощью которого будет проходить аутентификация на сервере и (при необходимости) в файл truststore.jks положить сертификат сервера, к которому идет обращение.
Хранилище ключей может иметь тип не только JKS (Java Key Store), но например быть в более распространенном формате PKCS12. В таком случае в настройках следует указать тип хранилища:
В итоге, если все правильно и аккуратно настроить, то взаимодействие с сервером будет происходить автоматически и программисту не нужно будет вносить серьезных изменений в исходный код.
Теперь рассмотрим более сложный случай в котором необходимо программно регулировать работу с ключами. Например, когда у вас в файле (или на смарт-карте) находится несколько ключей и нужно явно как-то их дополнительно анализировать, указывать какой ключ использовать.
Общая последовательность шагов будет выглядеть следующим образом.
1. Получить хранилище ключей.
2. Создать объект класса менеджера ключей.
3. Создать объект управляющий доверенными сертификатами.
4. Произвести инициализацию ssl-контекста.
Теперь нужно разобраться, кто это такой "менеджер ключей" и "управляющий доверенными сертификатами", а также с кем еще возможно придется столкнуться. Для этого приведу сильно упрощенную диаграмму классов:
Из диаграммы легко видеть как можно управлять поведением работы программы. Например, чтобы поменять логику выбора алиаса, можно сделать наследника класса менеджера ключей (KeyManager).
Для этого вначале нужно получить хранилище ключей (KeyStore), а дальше сделав своего наследника, который будет работать с ключами, передать его в метод для инициализации SSL контекста. Самый простой способ сделать такого наследника — получить менеджера ключей по-умолчанию, а затем обернуть его методы таким образом, чтобы основные методы "проксировались" и в то же время можно было перекрыть нужный метод со специфической логикой (выбор алиас, отслеживание какой ключ/принципал используется и тд).
Если не стоит такой задачи как подмена стандартной логики работы менеджера ключей, а просто необходимо программно управлять работой с хранилищем, то соответственно можно и не делать никаких наследников и не портить логику работы по-умолчанию.
Далее привожу очень простой пример, естественно это "вырожденный" случай иллюстрирующий общую схему работы.
Код приводится в ознакомительных целях и использовать его "как есть" крайне не рекомендуется.
/**
*
* @author Vit vit@programmisty.com
*/publicclass Demo {publicstaticvoid main(String[] args)throwsException{
SSLContext sc = SSLContext.getInstance("SSL");// Каким-то образом получили хранилище ключей. Это за рамками статьи.KeyStore keystore = ... ;String algorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm);// не забываем инициализировать менеджера ключей
keyManagerFactory.init(keystore, password);// получаем список менеджером (при необходимости делаем наследника и/или оборачиваем keyManager)
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();// если у нас свой список доверенных серверов
TrustManager[] trustManagers = getTrustManagers("/trusted.jks", "trustedPassword".toCharArray());
sc.init(keyManagers, trustManagers, null);// проставляем контекст
SSLContext.setDefault(sc);// для проверки отправляем запросString url ="https://my-secure-server.com";try(InputStream in =newURL(url).openStream()){System.out.println(IOUtils.toString(in));}}/**
* если у нас какой-то свой файлик с доверенными сертификатами
*/privatestatic TrustManager[] getTrustManagers(String path, char[] passwd)throwsException{String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);// Загружаем доверенныхKeyStore keystore =KeyStore.getInstance(KeyStore.getDefaultType());// Из ресурсов, так проще. InputStream keystoreStream = Demo.class.getResourceAsStream(path);
keystore.load(keystoreStream, passwd);
trustManagerFactory.init(keystore);//
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();return trustManagers;}}
/**
*
* @author Vit vit@programmisty.com
*/
public class Demo {
public static void main(String[] args) throws Exception {
SSLContext sc = SSLContext.getInstance("SSL");
// Каким-то образом получили хранилище ключей. Это за рамками статьи.
KeyStore keystore = ... ;
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm);
// не забываем инициализировать менеджера ключей
keyManagerFactory.init(keystore, password);
// получаем список менеджером (при необходимости делаем наследника и/или оборачиваем keyManager)
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
// если у нас свой список доверенных серверов
TrustManager[] trustManagers = getTrustManagers("/trusted.jks", "trustedPassword".toCharArray());
sc.init(keyManagers, trustManagers, null);
// проставляем контекст
SSLContext.setDefault(sc);
// для проверки отправляем запрос
String url = "https://my-secure-server.com";
try (InputStream in = new URL(url).openStream()){
System.out.println(IOUtils.toString(in));
}
}
/**
* если у нас какой-то свой файлик с доверенными сертификатами
*/
private static TrustManager[] getTrustManagers(String path, char[] passwd) throws Exception {
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
// Загружаем доверенных
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
// Из ресурсов, так проще.
InputStream keystoreStream = Demo.class.getResourceAsStream(path);
keystore.load(keystoreStream, passwd);
trustManagerFactory.init(keystore);
//
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
return trustManagers;
}
}
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent.
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
Cookie
Duration
Description
cookielawinfo-checkbox-analytics
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional
11 months
The cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy
11 months
The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.