null

Необычное использование maven profiles или нетипичный способ разделить приложение на 2 части

Дисклеймер. Эта статья просто заметка о моем опыте разделения приложения без сортировки и физического распределения всех классов в сжатые сроки. Этот способ - просто вариант быстрого решения проблемы без притязаний на какую-то правильность или ”best practices”.

Дано: с 2017 года мы ведем разработку некоторого веб-приложения на jsf, за это время кодовая база разрослась на целых 2.5 тысячи файлов. В этом году возникла необходимость физически разделить это приложение на 2 контура - открытый и закрытый: в первом должны быть доступны только некоторые публичные страницы. Сюда пользователи попадают из открытой сети. В другом - доступны все страницы приложения, в том числе приватные страницы администрирования и прочее подобное. Сюда доступ разрешен через определенные компьютеры или ВПН.

Задача: Так как в этом приложении высокий уровень ответственности, подходить к организации открытого контура тоже надо серьезно. По крайней мере, ожидается что-то более серьезное, чем редирект на страницу ”403. Доступ запрещен”. Для того, чтобы злоумышленники точно не смогли добраться до кодовой базы страниц нужно было найти решение, как вообще даже не компилировать jsf бины закрытых страниц для открытого контура приложения.

Первые попытки. Первое, что пришло в голову -- это использовать компайлтаймовый процессор аннотаций. Идея была в том, чтобы бины и остальные классы, которые должны быть доступны только в закрытом контуре помечать соответствующей аннотацией и в момент компиляции удалять тело класса. Что-то похожее делают аннотации lombok - добавляют getter’ы/setter’ы, блоки try/catch и подобное прямо в код. Ломбок же может - смогу и я! Но оказалось, что возможности менять код в момент обработки классов, помеченных компайлаймовыми аннотациями, слишком ограничены. В базовом варианте, в основном, доступно только расширение классов путем создания классов-наследников. Как раз всю нужную информацию о классе получить таким путем можно, а вот внести изменения в код -- нет. Остается только использовать классы-наследники и @Override для какое-либо модификации. Но нам такое решение не подходит: мы хотим, чтобы код был недоступен совсем! 

Почему у lombok’a получилось такое реализовать, а у нас нет? Оказалось, что lombok использует API-вызовы внутренней реализации компилятора для прямого изменения абстрактного синтаксического дерева программы между чтением исходного кода и выводом скомпилированного байт-кода. А это уже сложно и не подходит под требование “сделать задачу в сжатые сроки”. Что ж, думаем дальше, и так потратили время на попытку использовать аннотации.

Решение, которое использовалось в итоге. 
В проекте используется система сборки maven. Было решено использовать 2 разных profile, с разными правилами для сборки. Отдельный profile для открытого, отдельно - для закрытого контура. В конфигурации для сборки можно явно задать какие файлы или директории следует компилировать, или, наоборот, исключить из компиляции в зависимости от того, каким путем будет проще описать правила для компиляции. 
 

В примере ниже в тэге <excludes> (или, наоборот,  <includes>) указываются файлы или директории, которые необходимо пропустить или, наоборот, компилировать. Для указания всех файлов в директории, исключая директории внутри изначальной, используется символ звездочка * ru/edu/portfolio/jsf/beans/admin/*, для всех файлов рекурсивно - две звездочки ** ru/edu/portfolio/jsf/beans/admin/**.

Пример получившегося pom файла:
 

<!-- Profile #1 для открытого контура. В нем запрещен доступ на страницы админа и аналитики  -->

<profiles>
   <profile>
       <id>public</id>
       <build>

           <plugins>
               <plugin>
                   <groupId>org.apache.maven.plugins</groupId>
                   <artifactId>maven-compiler-plugin</artifactId>
                   <version>3.3</version>
                   <configuration>
                       <source>${java.version}</source>
                       <target>${java.version}</target>
<!-- Описание файлов для компиляции. Можно также использовать тег include -->
                       <excludes>
                           <exclude>ru/edu/portfolio/jsf/beans/admin/**</exclude>
                           <exclude>ru/edu/portfolio/jsf/beans/analyst/**</exclude>
                           </excludes>
                   </configuration>
               </plugin>

   </profile>

<!-- Profile #2 для закрытого контура. В нем есть доступ ко всем страницам -->

   <profile>
       <id>private</id>
       <activation>
<!-- Для использования этого profile по дефолту. -->
           <activeByDefault>true</activeByDefault>
       </activation>
       <build>

<!-- Ваши обычные правила для компиляции -->
		
       </build>
   </profile>
</profiles>

При стандартной сборке по команде mvn clean package будет компилироваться и собираться private profile, потому что он указан, как дефолтный. 

Для сборки public profile следует в команде явно указать профайл ключем -P: mvn clean package -P public.