По поводу коллекций — так исторически сложилось, а исправить уже нет возможности. В некоторых API immutable collection заменяет Enumeration, но особо не прижилось.
А теперь по поводу checked exceptions вопрос, вы реально топите за то, чтобы все exceptions были checked и пришлось бы писать подобный код?
Это полный контракт данной операции, разве что отсутствует OutOfMemoryError и StackOverflowError.
Если надо расширить контракт — расширяйте, в смысле берите новый расширенный контракт и реализуйте. Потому что от вашей новой реализации на старом контракте никто не хочет неожиданностей.
Боюсь, с таким ригидным подходом до сих пор бы не существовало бы ни Spring, ни JavaEE, ни других фреймворков и технологий в экосистеме. Более слабая и "оптимистичная" парадигма наиболее продуктивна — контракт никогда не отражает всех нюансов, поэтому нужно быть готовым ко всему, но особо на этом не зацикливаться.
Извините, но ваши рассуждения слишком наивны и идеалистичны. Что делать в случае, если ваш контракт изначально не определял возможного исключения, но при при смене провайдера это стало нужно? Например есть интерфейс с методом, который всю жизнь вызывался локально, но затем его реализовали через RPC? Ни клиента ни контракт уже не поменять.
RuntimeException — это ошибка в программе (например NPE), когда надо просто падать.
В подавляющем большинстве этого более чем достаточно. Как правило в коде абсолютно наплевать где зафейлилась операция и почему — достаточно вывести ее корректно в лог, откатить транзакцию и вернуть ошибку клиенту.
Итак, ошибки, ожидаемые от сервиса, известны, они объявлены.
Ну да ну да. Всевозможные RuntimeException-ы особенно объявлены...
а непроверяемые — это всегда должен быть только результат системного сбоя / ошибки программирования.
NumberFormatException при Integer.parseInt, когда пользователь ввел строку вместо числа — это ошибка программирования или системный сбой? В самой Java API использование checked/unchecked много где неконсистентно. Почему парсинг URL выдает checked MalFormedURLException, а парсинг числа unchecked NumberFormatException?
Когда код что-то вызывает, он определенно имеет отношение к тому, что вызвал. И к ошибкам этого в том числе.
Вызывающий код абсолютно не обязан обрабатывать ошибки вызываемого кода, а может делегировать это обработчику более высокого уровня. Пример из Java:
public class DataOutputStream extends FilterOutputStream implements DataOutput {
public final void writeInt(int v) throws IOException {
out.write((v >>> 24) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write((v >>> 0) & 0xFF);
incCount(4);
}
}
Здесь метод writeInt() вызывает OutputStream.write() аж 4 раза, при этом ни нет одного обработчика! Таким же образом любой код вправе сохранить "оптимистичную" модель программирования, не заботясь о завершении каждой "небезопасной" операции, и не замусоривая код обработчиками и логикой, которая не относится к этому методу. Здесь обязуют декларировать checked IOException, однако если бы тут был RuntimeException декларация отсутствовала бы. И такой код вполне себе имеет право на существование.
Сбой операции — один из возможных исходов операции и он входит в контракт этой операции. И корректное обращение со всеми исходами — это часть ответственности вызывающего.
Это ошибочное мнение.
Во-первых, как я уже сказал, вызывающий метод отнюдь не несет ответственность за обработку возможного исключения, если это въявную не предусмотрено его бизнес-задачей.
Во-вторых, вы никогда не сможете обработать все ошибочные исходы, ибо их множество в общем случае вычислимо, но никогда не определено ни одним контрактом. Я имею ввиду всевозможные RuntimeException, которые могут возникнуть в вызываемом коде, особенно если имеете дело со сторонней библиотекой/фреймворком, где под капотом подмешано большое количество различных технологий. И это множество зависит от многих факторов, многие из которых определяются не кодом, а runtime-конфигурацией системы: для одного и того же интерфейса может быть куча разных подключаемых провайдеров, код может работать в транзакции, вызывать remote-методы, делать RPC, стучаться в базу — и везде есть чему вальнуться. Поскольку конфигурация динамическая, на этапе проектирования нет возможности определить все возможные варианты сбоев.
В-третьих, сильно завязывать бизнес логику на исключения — это заведомо плохой подход. Exception — это именно исключительная ситуация, которая говорит о невозможности выполнения операции и требует раннего завершения всей цепочки вызовов. Если аварийное завершение — это часть вашего контракта и предусмотрена бизнес логикой, то и возвращайте его как обычное значение. Например вместо NotFoundException или NullPonterException возвращайте обычный Optional или на худой конец null.
В вашей библиотеке, насколько я могу судить, отсутствуют следующие важные вещи:
Зачастую конфиги имеют иерархическую структуру, поэтому нужно иметь возможность, чтобы пропертями могли быть другие Config-классы.
А как быть со пропертями-списками?
А что насчет других типов пропертей? Например LocalDate, URL, Duration, etc… Поэтому нужны дефолтные и кастомные конвертеры.
Если уж вы упомянули возможность горячего изменения пропертей, то необходим механизм, позволяющий "слушать" эти изменения. Например singleton-бины должны корректно апдейтнуть конфигурацию, если изменился релевантный параметр, а сервисы — еще и рестартнуться на горячую.
Круто! То что нужно! Собирался в свободное время написать что-то похожее, но вы меня сильно опередили. На проде есть кластер из 3 нодов, пока проходит сертификация и rollout приложения нужно часто копаться в логах. А использовать SSH+консоль контрпродуктивно. А ELK и прочаие сторонние тулзы не предусмотрены контрактом.
Есть пожелание по поводу настройки работы с rolled-файлами. Допустим роллинг файлов сконфигурирован по паттерну: service.${hostname}_%d{yyyy-MM-dd}.%i.log, то есть текущий лог записывается на каждой ноде в файл service.nodeX.log, который потом заворачивается в напр. service-nodeX_2021-01-20.0.log по дате и лимиту в МБ. Насколько я понял, для того, чтобы сработал мёрж нужно указать все файлы на всех нодах вручную, а мы точно не знаем сколько их. Поэтому в шорткатах будет очень удобно иметь возможность использовать URL-параметры и wildcards|regexp для задания фильтра на файлы, а также указывать имя хоста:
Тут вообще поднимается вопрос цифрового права конечного потребителя. До сих пор все законодательное регулирование было сосредоточено на введении различного рода ограничений на различные действия компаний и конечных пользователей в цифровой среде. Однако нет ни одного закона, гарантирующего хоть какое-нибудь право финального пользователя. По сути, принимая лицензию, тебя ставят перед выбором: либо ты принимаешь все наши условия, либо не пользуешься продуктом. Такая практика может иметь смысл в среде с множеством конкурентных провайдеров. Но сейчас по-сути весь цифровой рынок поделен между несколькими коммерческими компаниями-гигантами, и идти тебе вобщем-то некуда. Ты фактически не имеешь право ни на что: твой бизнес аккаунт могут заблокировать в любой момент без объяснения причин, в один момент ты можешь лишиться всех своих данных, доменов, способов оплаты, etc. При необходимости всегда могут заткнуть тебя, выкинув со всех публичных площадок. В недалеком будущем стать цифровым изгоем будет соизмеримо с участью бомжа — ни наняться на работу, ни совершить оплату, ни даже тупо идентифицировать свою личность.
Причем в любой другой сфере есть специальное законодательство и государственные ограны, контролирующие поставщика товаров и услуг на соблюдение прав потребителей. Однако в цифровой сфере до сих пор правительства в судах требуют лишь бабки с глобальных монополистов, тогда как реально должны требовать включения в SLA гарантий соблюдения прав конечных пользователей.
Неправильная оценка времени (основная тема этой статьи).
Есть всего одна причина почему не удается оценить время работы, и она заключается в отсутствии размерностей в правой части следующей формулы:
время выполнения задачи = объем задачи / скорость выполнения
В отличие от копания канавы или кладки кирпичей, для работы креативного плана не существует объективной величины, позволяющей выразить объем и скорость выполнения, как бы ни старались современные менеджеры и маркетологи. Все-равно что работу художника приравнять к работе маляра и оценивать по разрисованным квадратным метрам. Реальный объем проекта не измерить ни в тасках ни в строчках ни в сториз, также как и продуктивность девелопера — это совсем не количество закрытых тикетов в месяц.
Украшательства: слишком много внимания уделяется деталям, не относящимся к сути проекта.
Всегда есть нефункциональные требования, которые не оговариваются, но которые должны присутствовать в той или иной мере в проекте. Играя с "уровнем детализации" этих требований, можно выиграть время или наоборот потратить лишнее на рефакторинг, "полировку" и улучшение продукта. С этой точки зрения проект — это газ, который занимает весь доступный объем.
Недостаточно времени уделено этапу исследований и проектирования архитектуры. Или наоборот — уделено слишком много времени.
Чрезмерное увлечение проектированием и паттернами как правило лишь усложняет архитектуру, и сильно утяжеляет последующую разработку и поддержку. Зачастую топорное решение влоб или копипаста быстрее и легче, нежели хитрожопый паттерн, построенный по всем правилам.
Недооценка потенциальных проблем интеграции со сторонними системами.
Сегодня это чуть ли не основная проблема. Львиная доля функционала приходится на подключаемый код, написанный сторонними людьми, в котором всегда водятся подводные камни. А вечно меняющаяся экосистема постоянно заставляет работать с новыми средствами, параллельно осваивая их. Часто бывает, тратишь в разы больше времени, разбираясь как приспособить такой-то фреймворк для своей задачи, нежели запрогать ее самому с нуля.
Я бы еще добавил:
Фетишизм и фричество (девопс, тдд и т.д.)
Желание освоить новые горизонты, сделать все модно, стильно и молодежно, по последним методологиями и с целым паровозом моднючих средств. Идя на поводу у маркетологов, использование ненужных и излишних малоизученных средств сомнительного назначения, которые лишь добавляют проблем и работы, никак не улучшая конечный продукт.
Формально слабый антропный принцип сразу же сводится к сильной его формулировке, где подразумевается бесконечное пространство возможных вселенных со всеми возможными параметрами и конфигурациями. Тем не менее, даже в сильном антропном принципе если ваша модель кажется слишком "экзотичной" и "искусственной" (как современная теория струн), то скорей всего она является лишь частным ограниченным описанием некоторых явлений этого мира. В хорошей модели не должно быть никаких привилегированных констант, измерений, подпространств, и других сущностей.
(2.1) Почему даже в 11-мерном пространстве время остается выделенным измерением?
(4) Почему именно три-брана? Ведь она может являться частью объектов более высоких размерностей, эффекты наличия которых также должны присутствовать в наблюдаемой вселенной.
Классическое голивудское путешествие во времени эффектно, но парадоксально. По-сути это телепортация, когда с т.з. 4D-пространства-времени мировая линия человека или объекта перестает быть непрерывной, и возникают особые точки там, где их быть не должно. На сегодняшний день такие особые точки существуют только внутри черных дыр, и то лишь в результате релятивистского приближения.
Гораздо интересней случай, когда пространство-время топологически неодносвязно и содержит "кротовые норы", предсказываемые многими физическими теориями. В таких топологиях существуют траектории, где мировая линия объекта может пересекать сама себя, и действительно возможны перемещения во времени, что неминуемо приводит к парадоксу. Пока что хорошего объяснения этому нет. Но большинство ученых априори принимают, что Вселенная непарадоксальна (иначе любая сразу теория теряет свою предсказательную силу), а значит должны существовать механизмы, запрещающие подобные траектории.
Интересна также нолановская концепция реверсивности времени в фильме "Довод". Даром что инвертированный объект должен был бы состоять из антиматерии и эффектно проаннигилировать с окружающей средой. Однако там верно указана причина обратного времени: инвертированная энтропия объекта. Вопрос в том, будет ли объект из антиматерии проявлять свойства убывания энтропии (правильный ответ нет) и что для этого нужно.
Собственно все как я и предполагал. А теперь почему я никогда НЕ выберу JEE для своего проекта.
Каждый девелопер в команде должен пройти дебильную ручную процедуру установки и конфигурации сервера локально на своей тачке. То же самое нужно сделать для каждого environment, сервера CI/CD, etc. Конфиги сервера всегда хранятся отдельно от проекта бог знает где и нередко недоступны ввиде файлов. Поэтому правильно делают те, кто пакуют сервер с приложением в докер.
Вам повезло, что ваш сервер OpenSource. Коммерческие решения как правило не так приветливы к программеру (по секрету скажу, что вендорам насрать на девелоперов: их целевая аудитория — шефы) и как правило не умеют пускаться и деплоиться локально из IDE. Поэтому прощай нормальный дебаг и IDE и здравствуй коммандная строка и System.out.println().
Все JEE решения сильно завязаны на тулинг (коммерческие IDE, плагины, коннекторы). Через пару лет тулзы апдейтнутся, и ваш проект тупо перестанет собираться или запускаться. Так у меня было практически с любым JEE проектом — через пару лет так просто поправить плевый баг или фичу без геморроя уже не удастся.
JEE вообще не предназначен для юнит-тестирования. Когда он задумывался, еще даже не было такого модного слова. Во-первых в общем случае JEE не умеет пускаться из IDE локально в embedded режиме и цеплять приложение из workspace (в случае с Glassfish для вас это делает умный плагин к IDE). Во-вторых, не позволяет управлять сборкой контекста: то есть тупо нельзя динамически указать какие бины для теста нужно задеплоить, а какие нет, ведь весь bean-дискавери завязан на текущий classpath. Евангелисты JEE предлагали иметь на каждый тест свой maven-профайл, где указывается свой classpath с ресурсами, специфичными для теста, и пускать тесты вручную из мавена. В-третьих, нельзя программно установить конфигурацию сервера для конкретного теста, например когда ваш тестовый сервис должен послать месседж в тестовую очередь или законнектиться к другой базе данных. Особо продвинутые умудряются отделить всю логику от ресурсов сервера, обвешаться со всех сторон моками и тестировать локально без контейнера. Что конкретно они при этом тестируют — они не понимают сами. Конечно есть технологии типа TomEE и Arquillian, но это велосипеды, призванные криво обойти одно из важнейших ограничений JEE.
Application-specific parameters вообще отсутствует как таковой. То есть тупо указать вебсервису, что тут на данной машине он должен вызывать этот URL, а на другой тот, будет связано с глубоким копанием JAX-WS и велосипедами ввиде отдельно лежащего файла .properties, достижимого по абсолютному путю, либо environment variables. С другой стороны, конфигурация приложения — это то, что из коробки предоставляет вообще любой фреймворк.
Конкретно что такое дает ваш JEE, чего нет в библиотеках и фреймворках? Правильный ответ — JTA. Это то единственное, ради чего его используют в энтерпрайзе. Однако JTA — это один большой закамуфлированный архитектурный антипаттерн, который вы никогда не поймете до конца и никода не сможете настроить нормально. Все остальное делается проще, лучше и изящнее, нежели в JEE.
Чтобы разделить business и web tier не нужен JEE сервер — для джавы есть 100500 легких технологий ремоутинга. Помнится мы в свое время (лет 15 назад) использовали RMI. Сейчас это модно делать при помощи Rest API. Запускаются локально два процесса: business и web, по мере необходимости рестартится любой из них.
Теперь по поводу вашего проекта. Вангую:
Функционал проекта не выходит за рамки базы данных и веба
Вы единственный разработчик
У вас нет ни одного автоматического теста
Конфигурация сервера и ресурсов лежит отдельно от кода
Вы используете Eclipse и ваш проект сильно завязан на его тулинг, отсутствуют средства автоматического деплоя
Вы используете какой-нибудь opensource сервер, типа Glassfish или TomEE
Отсутствует более чем полностью параметризация приложения
Не используется CI/CD
А по поводу CDI — это во многом повторение EJB 3 версии, без их преимуществ. Попробуйте в CDI организовать транзакцию.
Скорей без их рудиментов. С JEE7 бины умеют транзакцию.
Спринг является монолитным независимо ни от каких слов-вирусов, поскольку разбить его на модули в рантайме нельзя.
Один вопрос — нафига? Модуль — логическая единица, а не физическая. В одном процессе и контексте могут сожительствовать сколь угодно много модулей. Сделать инъекцию бина и локальный вызов метода гораздо проще, чем городить API, усложнять архитектуру и искать себе проблемы там, где их нет. Какие конкретно насущные задачи решит разбивка на разные deployable units?
пытающиеся превратить Спринг то в наносервисы, то наоборот в интегрированное решение наподобие Java EE, уже не помогут
Это все-равно что превращать трамвай в паровоз. Spring стал популярным именно как замена JEE, сначала предоставляя более простой слой интеграции с ней, а затем как независимое решение. И уже JEE в надежде удержать юзеров стал таскать идеи из спринга, например CDI.
Монолит — слово-вирус, придуманное и разрекламированное PR-менеджерами для продвижения клауд решений. Поэтому не стоит бездумно изпользовать его везде как штамп. Не существует ни монолитов ни микросервисов. Существует приложение и функциональные модули, и особняком стоит архитектура решения как способ взаимодействия модулей. И спринг как бы здесь ни при чем.
Я поневоле использую JavaEE с 2005 с открытыми и коммерческими серверами вкупе с кучей проприетарного барахла, которое шло поверх. Причем, сколько использую, столько плююсь — платформа реально мешает выполнять задачу, ограничивает и усложняет многие вещи, которые без нее сделать в разы проще.
Ну, во-первых, меня до сих пор интересует зачем вообще делать деплой, если мы не на продакшне. Это бесполезная операция. Функция public static void main(String… args) до сих пор не deprecated, а весь функционал сервера приложений доступен ввиде библиотек или фреймворков.
Во-вторых, если это хеллоу ворлд, то да, все быстро. Однако обычно бизнес приложение тащит за собой еще стопицот сторонних джарников, поэтому развертывание такого приложения на сервере будет не слишком быстрым, ибо львиная доля тратится на резольвинг классов. И ждать после каждого изменения, когда все классы заново отрезольвятся и передеплоятся — это то, почему в свое время люди свалили из джавы на более легковесные решения. А если ваш суперсервер не может деплоить прямо из workspace, то добавьте сюда еще рудиментарную запаковку-распаковку.
Хорошее решение сделано в Spring Boot Devtools: там разделены контексты класслоадеров на статические — которые грузят из джаров (которые в свою очередь редко меняются) и динамические — которые грузят из .class вокрспейса. При изменении в файле класса спринг перезагружает контекст, при этом оставляя статические класслоадеры, которые уже отрезольвили все необходимые зависимости. В итоге приложение апдейтится за секунду.
Лет 15 назад каждый второй вирус распространялся через JVM Browser Plugin, хотя аплеты вроде как работают в изолированной песочнице. Из-за чего юзеры начали массовый выпил джавы со своих компов.
Итак Ява-машина — это ОС. Даже круче чем ОС местами.
Нет, это именно виртуальная машина, не более. А вот была попытка написания настоящей ОС на 100% джаве: https://www.jnode.org/
Потому что, как я уже сказал, она позволяет создавать множество независимых изолированных контекстов
Она не может создавать изолированные контексты памяти (разные кучи) внутри одной JVM. Поэтому любой тред легко может навернуть всю JVM с OutOfMemory. Плюс все контроли за ресурсами устанавливаются на уровне процесса, а не контекста, поэтому никто не запретит треду открыть миллион файлов или создать еще миллион тредов.
Совершенно верно, докер большинство используют как систему развертывания, чтобы избежать сложностей с установкой и конфигурацией зависимостей. Одна команда — и тебе поднимается весь готовый эластик без лишней головной боли. В случае некоторых сторонних продуктов и зависимостей это имеет смысл. Но тем не менее для развертывания есть свои тулзы: Chef, Puppet, Terraform, Ansible — докер больше по части виртуализации, которая в большинстве случаев не нужна. На одной хостовой тачке можно поднять кучу процессов — заворачивать каждый в свой контейнер вовсе не обязательно. Многие утверждают, что докер позволяет избежать ада с зависимостями и библиотеками, но во-первых, ОС имеет свой репозиторий и уже как правило предоставляет свежую версию необходимой зависимости, а во-вторых тут есть скрытая угроза в безопасности — контроль над версиями ложится на самих разрабов, которые очень не спешат апдейтить зависимости, пока не случаются проблемы. А контейнер на продакшне сильно затрудняет сторонний security-audit.
У нас в докере поднимается "неубиваемый" кластер из Postgresql, ничего страшного в этом нет. Естественно data-volume-ы замеплены на файловую систему. Тоже compose+swarm. Плюс отдельный контейнер/машина под Elastic. Сервисы деплоятся без контейнеров прямо на тачки при помощи git.
Да, конкретно по томкэту мануалы уже попахивают. Хотя для общего развития полезно ознакомиться с легаси веб стеком джавы (контексты, сервлеты, фильтры, etc). В мире Java сейчас в основном форсят Spring Boot, который (внезапно!) пускает тот же томкэт в embedded-режиме, но никак на него не завязан. На хабре каждая вторая статья в джавовском хабе — это как написать очередной Hello World на спринге с кучей плюшек.
И да, когда контейнеры и кубернетис — это на 90% хайп, ибо сегодня, планируя архитектуру, никто уже не задумывается о необходимости данных тулзов применительно к решаемой задаче. Архитектура большинства систем не настолько сложна, чтобы был необходим промежуточный слой виртуализации и оркестрации сервисов. Контенеризация зачастую излишня: как правило вы выделяете весь виртуальный сервер под конкретный процесс/систему — зачем еще одна операционка внутри другой? Большинство задач оркестрации и сервис дискавери решается средствами самой операционки (либо тупо конфигурационным файлом). Для деплоя и управления обычно хватает ssh. Чуть сложнее инфраструктура или сценарий — есть Ansible. А бездумно пихать микросервисы, k8s и весь девопс в каждый проект, тем самым усложняя себе и другим жизнь, и получая себе на задницу массы новых впечатлений — лишь увеличивает градус неадеквата. Думаю, об этом и была данная статья.
Томкэт по сути это тот же сервер приложений, только урезанный до веба, и который разделяет все его идеи: запускается отдельно от приложения, ресурсы конфигурирются также отдельно, а само приложение пакуется и деплоится на сервак. К сожалению в экосистеме Java томкэт долгое время это был фактически единственным вебсервером, благодаря чему люди стали использовать альтернативные технологии. Однако ситуация уже давным давно поменялась: есть embedded-сервера (https://www.eclipse.org/jetty/), куча хеви и лайтовых фреймворков для веб разработки с минимальным порогом вхождения (https://javalin.io/).
В идеале задумка провайдеров была задвинуть клиенту за монету свой сервер, на который тот бы смог деплоить свои приложения, и они бы там работали, а провайдер собирал бы за лицензию и поддержку. Но концепт быстро устарел: вместо одного сервера с кучей приложений уже одному приложению требовалось несколько серверов для работы. К тому же люди быстро поняли, что концепция деплоить что-то куда-то стала излишней, а все то, что предлагал сервер приложений либо нафиг не нужно, либо неплохо решалось с помощью сторонних библиотек. И расцвел век фреймворков типа спринга. Но империя нанесла ответный удар ввиде контейнеров и кубернетиса. И теперь хомячкам снова требуется все паковать и куда-то деплоить, чтобы оно там работало. А чтобы это не выглядело совсем по-идиотски, клиента убедили самому раздробить свое приложение по частям, а уже платформа возьмет на себя задачу собрать все снова воедино. И все это безобразие назвали хайповым словом "микросервисы".
По поводу коллекций — так исторически сложилось, а исправить уже нет возможности. В некоторых API immutable collection заменяет Enumeration, но особо не прижилось.
А теперь по поводу checked exceptions вопрос, вы реально топите за то, чтобы все exceptions были checked и пришлось бы писать подобный код?
Это полный контракт данной операции, разве что отсутствует OutOfMemoryError и StackOverflowError.
Боюсь, с таким ригидным подходом до сих пор бы не существовало бы ни Spring, ни JavaEE, ни других фреймворков и технологий в экосистеме. Более слабая и "оптимистичная" парадигма наиболее продуктивна — контракт никогда не отражает всех нюансов, поэтому нужно быть готовым ко всему, но особо на этом не зацикливаться.
Извините, но ваши рассуждения слишком наивны и идеалистичны. Что делать в случае, если ваш контракт изначально не определял возможного исключения, но при при смене провайдера это стало нужно? Например есть интерфейс с методом, который всю жизнь вызывался локально, но затем его реализовали через RPC? Ни клиента ни контракт уже не поменять.
В подавляющем большинстве этого более чем достаточно. Как правило в коде абсолютно наплевать где зафейлилась операция и почему — достаточно вывести ее корректно в лог, откатить транзакцию и вернуть ошибку клиенту.
Ну да ну да. Всевозможные RuntimeException-ы особенно объявлены...
NumberFormatException при Integer.parseInt, когда пользователь ввел строку вместо числа — это ошибка программирования или системный сбой? В самой Java API использование checked/unchecked много где неконсистентно. Почему парсинг URL выдает checked MalFormedURLException, а парсинг числа unchecked NumberFormatException?
Вызывающий код абсолютно не обязан обрабатывать ошибки вызываемого кода, а может делегировать это обработчику более высокого уровня. Пример из Java:
Здесь метод writeInt() вызывает OutputStream.write() аж 4 раза, при этом ни нет одного обработчика! Таким же образом любой код вправе сохранить "оптимистичную" модель программирования, не заботясь о завершении каждой "небезопасной" операции, и не замусоривая код обработчиками и логикой, которая не относится к этому методу. Здесь обязуют декларировать checked IOException, однако если бы тут был RuntimeException декларация отсутствовала бы. И такой код вполне себе имеет право на существование.
Это ошибочное мнение.
Во-первых, как я уже сказал, вызывающий метод отнюдь не несет ответственность за обработку возможного исключения, если это въявную не предусмотрено его бизнес-задачей.
Во-вторых, вы никогда не сможете обработать все ошибочные исходы, ибо их множество в общем случае вычислимо, но никогда не определено ни одним контрактом. Я имею ввиду всевозможные RuntimeException, которые могут возникнуть в вызываемом коде, особенно если имеете дело со сторонней библиотекой/фреймворком, где под капотом подмешано большое количество различных технологий. И это множество зависит от многих факторов, многие из которых определяются не кодом, а runtime-конфигурацией системы: для одного и того же интерфейса может быть куча разных подключаемых провайдеров, код может работать в транзакции, вызывать remote-методы, делать RPC, стучаться в базу — и везде есть чему вальнуться. Поскольку конфигурация динамическая, на этапе проектирования нет возможности определить все возможные варианты сбоев.
В-третьих, сильно завязывать бизнес логику на исключения — это заведомо плохой подход. Exception — это именно исключительная ситуация, которая говорит о невозможности выполнения операции и требует раннего завершения всей цепочки вызовов. Если аварийное завершение — это часть вашего контракта и предусмотрена бизнес логикой, то и возвращайте его как обычное значение. Например вместо NotFoundException или NullPonterException возвращайте обычный Optional или на худой конец null.
А чем не устраивает например http://www.cfg4j.org/ или http://owner.aeonbits.org/ ??? Тот самый фатальный недостаток?
В вашей библиотеке, насколько я могу судить, отсутствуют следующие важные вещи:
Круто! То что нужно! Собирался в свободное время написать что-то похожее, но вы меня сильно опередили. На проде есть кластер из 3 нодов, пока проходит сертификация и rollout приложения нужно часто копаться в логах. А использовать SSH+консоль контрпродуктивно. А ELK и прочаие сторонние тулзы не предусмотрены контрактом.
Есть пожелание по поводу настройки работы с rolled-файлами. Допустим роллинг файлов сконфигурирован по паттерну:
service.${hostname}_%d{yyyy-MM-dd}.%i.log
, то есть текущий лог записывается на каждой ноде в файлservice.nodeX.log
, который потом заворачивается в напр.service-nodeX_2021-01-20.0.log
по дате и лимиту в МБ. Насколько я понял, для того, чтобы сработал мёрж нужно указать все файлы на всех нодах вручную, а мы точно не знаем сколько их. Поэтому в шорткатах будет очень удобно иметь возможность использовать URL-параметры и wildcards|regexp для задания фильтра на файлы, а также указывать имя хоста:И использовать в URL типа
http://localhost:8111/log?log=daily&date=2020-01-20
.(*) Подожду Вальгаллу, чтобы уж наверняка!
Тут вообще поднимается вопрос цифрового права конечного потребителя. До сих пор все законодательное регулирование было сосредоточено на введении различного рода ограничений на различные действия компаний и конечных пользователей в цифровой среде. Однако нет ни одного закона, гарантирующего хоть какое-нибудь право финального пользователя. По сути, принимая лицензию, тебя ставят перед выбором: либо ты принимаешь все наши условия, либо не пользуешься продуктом. Такая практика может иметь смысл в среде с множеством конкурентных провайдеров. Но сейчас по-сути весь цифровой рынок поделен между несколькими коммерческими компаниями-гигантами, и идти тебе вобщем-то некуда. Ты фактически не имеешь право ни на что: твой бизнес аккаунт могут заблокировать в любой момент без объяснения причин, в один момент ты можешь лишиться всех своих данных, доменов, способов оплаты, etc. При необходимости всегда могут заткнуть тебя, выкинув со всех публичных площадок. В недалеком будущем стать цифровым изгоем будет соизмеримо с участью бомжа — ни наняться на работу, ни совершить оплату, ни даже тупо идентифицировать свою личность.
Причем в любой другой сфере есть специальное законодательство и государственные ограны, контролирующие поставщика товаров и услуг на соблюдение прав потребителей. Однако в цифровой сфере до сих пор правительства в судах требуют лишь бабки с глобальных монополистов, тогда как реально должны требовать включения в SLA гарантий соблюдения прав конечных пользователей.
Есть всего одна причина почему не удается оценить время работы, и она заключается в отсутствии размерностей в правой части следующей формулы:
время выполнения задачи = объем задачи / скорость выполнения
В отличие от копания канавы или кладки кирпичей, для работы креативного плана не существует объективной величины, позволяющей выразить объем и скорость выполнения, как бы ни старались современные менеджеры и маркетологи. Все-равно что работу художника приравнять к работе маляра и оценивать по разрисованным квадратным метрам. Реальный объем проекта не измерить ни в тасках ни в строчках ни в сториз, также как и продуктивность девелопера — это совсем не количество закрытых тикетов в месяц.
Всегда есть нефункциональные требования, которые не оговариваются, но которые должны присутствовать в той или иной мере в проекте. Играя с "уровнем детализации" этих требований, можно выиграть время или наоборот потратить лишнее на рефакторинг, "полировку" и улучшение продукта. С этой точки зрения проект — это газ, который занимает весь доступный объем.
Чрезмерное увлечение проектированием и паттернами как правило лишь усложняет архитектуру, и сильно утяжеляет последующую разработку и поддержку. Зачастую топорное решение влоб или копипаста быстрее и легче, нежели хитрожопый паттерн, построенный по всем правилам.
Сегодня это чуть ли не основная проблема. Львиная доля функционала приходится на подключаемый код, написанный сторонними людьми, в котором всегда водятся подводные камни. А вечно меняющаяся экосистема постоянно заставляет работать с новыми средствами, параллельно осваивая их. Часто бывает, тратишь в разы больше времени, разбираясь как приспособить такой-то фреймворк для своей задачи, нежели запрогать ее самому с нуля.
Я бы еще добавил:
Желание освоить новые горизонты, сделать все модно, стильно и молодежно, по последним методологиями и с целым паровозом моднючих средств. Идя на поводу у маркетологов, использование ненужных и излишних малоизученных средств сомнительного назначения, которые лишь добавляют проблем и работы, никак не улучшая конечный продукт.
Формально слабый антропный принцип сразу же сводится к сильной его формулировке, где подразумевается бесконечное пространство возможных вселенных со всеми возможными параметрами и конфигурациями. Тем не менее, даже в сильном антропном принципе если ваша модель кажется слишком "экзотичной" и "искусственной" (как современная теория струн), то скорей всего она является лишь частным ограниченным описанием некоторых явлений этого мира. В хорошей модели не должно быть никаких привилегированных констант, измерений, подпространств, и других сущностей.
(2.1) Почему даже в 11-мерном пространстве время остается выделенным измерением?
(4) Почему именно три-брана? Ведь она может являться частью объектов более высоких размерностей, эффекты наличия которых также должны присутствовать в наблюдаемой вселенной.
Классическое голивудское путешествие во времени эффектно, но парадоксально. По-сути это телепортация, когда с т.з. 4D-пространства-времени мировая линия человека или объекта перестает быть непрерывной, и возникают особые точки там, где их быть не должно. На сегодняшний день такие особые точки существуют только внутри черных дыр, и то лишь в результате релятивистского приближения.
Гораздо интересней случай, когда пространство-время топологически неодносвязно и содержит "кротовые норы", предсказываемые многими физическими теориями. В таких топологиях существуют траектории, где мировая линия объекта может пересекать сама себя, и действительно возможны перемещения во времени, что неминуемо приводит к парадоксу. Пока что хорошего объяснения этому нет. Но большинство ученых априори принимают, что Вселенная непарадоксальна (иначе любая сразу теория теряет свою предсказательную силу), а значит должны существовать механизмы, запрещающие подобные траектории.
Интересна также нолановская концепция реверсивности времени в фильме "Довод". Даром что инвертированный объект должен был бы состоять из антиматерии и эффектно проаннигилировать с окружающей средой. Однако там верно указана причина обратного времени: инвертированная энтропия объекта. Вопрос в том, будет ли объект из антиматерии проявлять свойства убывания энтропии (правильный ответ нет) и что для этого нужно.
Собственно все как я и предполагал. А теперь почему я никогда НЕ выберу JEE для своего проекта.
Чтобы разделить business и web tier не нужен JEE сервер — для джавы есть 100500 легких технологий ремоутинга. Помнится мы в свое время (лет 15 назад) использовали RMI. Сейчас это модно делать при помощи Rest API. Запускаются локально два процесса: business и web, по мере необходимости рестартится любой из них.
Теперь по поводу вашего проекта. Вангую:
Скорей без их рудиментов. С JEE7 бины умеют транзакцию.
Один вопрос — нафига? Модуль — логическая единица, а не физическая. В одном процессе и контексте могут сожительствовать сколь угодно много модулей. Сделать инъекцию бина и локальный вызов метода гораздо проще, чем городить API, усложнять архитектуру и искать себе проблемы там, где их нет. Какие конкретно насущные задачи решит разбивка на разные deployable units?
Это все-равно что превращать трамвай в паровоз. Spring стал популярным именно как замена JEE, сначала предоставляя более простой слой интеграции с ней, а затем как независимое решение. И уже JEE в надежде удержать юзеров стал таскать идеи из спринга, например CDI.
Монолит — слово-вирус, придуманное и разрекламированное PR-менеджерами для продвижения клауд решений. Поэтому не стоит бездумно изпользовать его везде как штамп. Не существует ни монолитов ни микросервисов. Существует приложение и функциональные модули, и особняком стоит архитектура решения как способ взаимодействия модулей. И спринг как бы здесь ни при чем.
Я поневоле использую JavaEE с 2005 с открытыми и коммерческими серверами вкупе с кучей проприетарного барахла, которое шло поверх. Причем, сколько использую, столько плююсь — платформа реально мешает выполнять задачу, ограничивает и усложняет многие вещи, которые без нее сделать в разы проще.
Ну, во-первых, меня до сих пор интересует зачем вообще делать деплой, если мы не на продакшне. Это бесполезная операция. Функция public static void main(String… args) до сих пор не deprecated, а весь функционал сервера приложений доступен ввиде библиотек или фреймворков.
Во-вторых, если это хеллоу ворлд, то да, все быстро. Однако обычно бизнес приложение тащит за собой еще стопицот сторонних джарников, поэтому развертывание такого приложения на сервере будет не слишком быстрым, ибо львиная доля тратится на резольвинг классов. И ждать после каждого изменения, когда все классы заново отрезольвятся и передеплоятся — это то, почему в свое время люди свалили из джавы на более легковесные решения. А если ваш суперсервер не может деплоить прямо из workspace, то добавьте сюда еще рудиментарную запаковку-распаковку.
Хорошее решение сделано в Spring Boot Devtools: там разделены контексты класслоадеров на статические — которые грузят из джаров (которые в свою очередь редко меняются) и динамические — которые грузят из .class вокрспейса. При изменении в файле класса спринг перезагружает контекст, при этом оставляя статические класслоадеры, которые уже отрезольвили все необходимые зависимости. В итоге приложение апдейтится за секунду.
Лет 15 назад каждый второй вирус распространялся через JVM Browser Plugin, хотя аплеты вроде как работают в изолированной песочнице. Из-за чего юзеры начали массовый выпил джавы со своих компов.
Нет, это именно виртуальная машина, не более. А вот была попытка написания настоящей ОС на 100% джаве: https://www.jnode.org/
Она не может создавать изолированные контексты памяти (разные кучи) внутри одной JVM. Поэтому любой тред легко может навернуть всю JVM с OutOfMemory. Плюс все контроли за ресурсами устанавливаются на уровне процесса, а не контекста, поэтому никто не запретит треду открыть миллион файлов или создать еще миллион тредов.
Совершенно верно, докер большинство используют как систему развертывания, чтобы избежать сложностей с установкой и конфигурацией зависимостей. Одна команда — и тебе поднимается весь готовый эластик без лишней головной боли. В случае некоторых сторонних продуктов и зависимостей это имеет смысл. Но тем не менее для развертывания есть свои тулзы: Chef, Puppet, Terraform, Ansible — докер больше по части виртуализации, которая в большинстве случаев не нужна. На одной хостовой тачке можно поднять кучу процессов — заворачивать каждый в свой контейнер вовсе не обязательно. Многие утверждают, что докер позволяет избежать ада с зависимостями и библиотеками, но во-первых, ОС имеет свой репозиторий и уже как правило предоставляет свежую версию необходимой зависимости, а во-вторых тут есть скрытая угроза в безопасности — контроль над версиями ложится на самих разрабов, которые очень не спешат апдейтить зависимости, пока не случаются проблемы. А контейнер на продакшне сильно затрудняет сторонний security-audit.
У нас в докере поднимается "неубиваемый" кластер из Postgresql, ничего страшного в этом нет. Естественно data-volume-ы замеплены на файловую систему. Тоже compose+swarm. Плюс отдельный контейнер/машина под Elastic. Сервисы деплоятся без контейнеров прямо на тачки при помощи git.
Да, конкретно по томкэту мануалы уже попахивают. Хотя для общего развития полезно ознакомиться с легаси веб стеком джавы (контексты, сервлеты, фильтры, etc). В мире Java сейчас в основном форсят Spring Boot, который (внезапно!) пускает тот же томкэт в embedded-режиме, но никак на него не завязан. На хабре каждая вторая статья в джавовском хабе — это как написать очередной Hello World на спринге с кучей плюшек.
И да, когда контейнеры и кубернетис — это на 90% хайп, ибо сегодня, планируя архитектуру, никто уже не задумывается о необходимости данных тулзов применительно к решаемой задаче. Архитектура большинства систем не настолько сложна, чтобы был необходим промежуточный слой виртуализации и оркестрации сервисов. Контенеризация зачастую излишня: как правило вы выделяете весь виртуальный сервер под конкретный процесс/систему — зачем еще одна операционка внутри другой? Большинство задач оркестрации и сервис дискавери решается средствами самой операционки (либо тупо конфигурационным файлом). Для деплоя и управления обычно хватает ssh. Чуть сложнее инфраструктура или сценарий — есть Ansible. А бездумно пихать микросервисы, k8s и весь девопс в каждый проект, тем самым усложняя себе и другим жизнь, и получая себе на задницу массы новых впечатлений — лишь увеличивает градус неадеквата. Думаю, об этом и была данная статья.
Томкэт по сути это тот же сервер приложений, только урезанный до веба, и который разделяет все его идеи: запускается отдельно от приложения, ресурсы конфигурирются также отдельно, а само приложение пакуется и деплоится на сервак. К сожалению в экосистеме Java томкэт долгое время это был фактически единственным вебсервером, благодаря чему люди стали использовать альтернативные технологии. Однако ситуация уже давным давно поменялась: есть embedded-сервера (https://www.eclipse.org/jetty/), куча хеви и лайтовых фреймворков для веб разработки с минимальным порогом вхождения (https://javalin.io/).
В идеале задумка провайдеров была задвинуть клиенту за монету свой сервер, на который тот бы смог деплоить свои приложения, и они бы там работали, а провайдер собирал бы за лицензию и поддержку. Но концепт быстро устарел: вместо одного сервера с кучей приложений уже одному приложению требовалось несколько серверов для работы. К тому же люди быстро поняли, что концепция деплоить что-то куда-то стала излишней, а все то, что предлагал сервер приложений либо нафиг не нужно, либо неплохо решалось с помощью сторонних библиотек. И расцвел век фреймворков типа спринга. Но империя нанесла ответный удар ввиде контейнеров и кубернетиса. И теперь хомячкам снова требуется все паковать и куда-то деплоить, чтобы оно там работало. А чтобы это не выглядело совсем по-идиотски, клиента убедили самому раздробить свое приложение по частям, а уже платформа возьмет на себя задачу собрать все снова воедино. И все это безобразие назвали хайповым словом "микросервисы".