Содержание


Groovy и Spring

Часть 2. Изменение поведения приложения в процессе выполнения

Добавление динамически обновляемых объектов Groovy в приложение Spring

Comments

Серия контента:

Этот контент является частью # из серии # статей: Groovy и Spring

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Groovy и Spring

Следите за выходом новых статей этой серии.

В первой статье серии рассказывалось о возможностях повышения гибкости приложений Spring при помощи объектов Groovy. Средства для поддержки Groovy в Spring позволяют использовать компилируемые или скриптовые объекты Groovy, а также конфигурировать их различными способами в том числе при помощи XML-схемы lang и компонента Bean Builder в Grails. В процессе интеграции скриптов Groovy в приложение можно реализовывать отдельную логику, управляющую процессом создания объектов, например для выбора конкретной реализации того или иного объекта. Кроме того, отдельные скриптовые объекты Groovy можно использовать для повышения гибкости развертывания и компоновки приложения.

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

  • генерация документов PDF (чеки, счеты, отчеты о продажах, инвестиционные отчеты, планы и т. д.);
  • шаблоны электронных писем;
  • генерация отчетов;
  • вынос бизнес-логики во внешние компоненты, предметно-ориентированные языка (DSL) и процессоры применения правил;
  • задачи системного администрирования;
  • изменение уровней вывода диагностических сообщений и отладка в процессе выполнения.

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

Обновляемые объекты Groovy

В первой статье серии был определен интерфейс PdfGenerator и реализующий его Groovy-класс (GroovyPdfGenerator), находящийся в скрипте GroovyPdfGenerator.groovy в CLASSPATH приложения. Местоположение скрипта указывалось в процессе конфигурирования объекта pdfGenerator. Интерфейс, реализация и конфигурация с использование XML-схемы lang приведены в листинге 1.

Листинг 1. Интерфейс, реализация и конфигурация PdfGenerator
// PdfGenerator.java
public interface PdfGenerator {
    byte[] pdfFor(Invoice invoice);
}

// GroovyPdfGenerator.groovy
class GroovyPdfGenerator implements PdfGenerator {

    String companyName

    public byte[] pdfFor(Invoice invoice) {
        ...
    }

}

// applicationContext.xml
<lang:groovy id="pdfGenerator"
             script-source="classpath:groovierspring/GroovyPdfGenerator.groovy">
    <lang:property name="companyName" value="Groovy Bookstore"/>
</lang:groovy>

Пока все идет по плану. Мы создали объект с именем pdfGenerator, реализация которого на языке Groovy находится в CLASSPATH приложения. При инициализации контекста приложения Spring считывает содержимое скрипта, компилирует его в класс Java и инстанциирует GroovyPdfGenerator, помещая экземпляр в контекст. Все компоненты, которым требуется pdfGenerator просто декларируют эту необходимость, после чего Spring связывает классы обычным образом.

Здесь начинается самое интересное. Допустим вам необходимо часто изменять логику генерации документов PDF и желательно, чтобы эти изменения вступали в силу немедленно и без остановки приложения. Spring делает эту задачу фактически тривиальной. Все что необходимо – это добавить атрибут refresh-check-delay в элемент <lang:groovy>, определяющий нужный объект в контексте. Этот атрибут определяет период в миллисекундах, через который Spring будет проверять вносились ли коррективы в скрипт на Groovy. Если скрипт был изменен (т. е. дата изменения скрипта изменилась с момента последней проверки), то Spring повторно загрузит скрипт, скомпилирует его и заменит старый экземпляр pdfGenerator на новый. Это будет сделано абсолютно незаметно для любых компонентов, использующих pdfGenerator.

В листинге 2 приведен пример конфигурирования объекта pdfGenerator с периодом проверки обновлений в 10 секунд (10000 миллисекунд). Добавление атрибута refresh-check-delay приводит к тому, что Spring будет автоматически обновлять объект при изменении скрипта GroovyPdfGenerator.groovy.

Листинг 2. Добавление атрибута refresh-check-delay к описанию скриптового объекта
<lang:groovy id="pdfGenerator"
             script-source="classpath:groovierspring/GroovyPdfGenerator.groovy"
             refresh-check-delay="10000">
    <lang:property name="companyName" value="Refreshable Groovy Bookstore"/>
</lang:groovy>

Теперь если в процессе работы приложения скрипт GroovyPdfGenerator.groovy будет изменен, то это автоматически обнаружит Spring, после чего объект pdfGenerator будет перезагружен без остановки приложения. Обратите внимание, что обновление произойдет после истечения периода задержки, а также вызова метода обновляемого объекта. Например, допустим, что задержка обновления pdfGenerator составляет 10 секунд, но ни один метод этого объекта не был вызван в течение 50 секунд. В этом случае Spring проверит необходимость обновления через 50 секунд, а не через 10. Другими словами, Spring не занимается активным мониторингом обновлений. Вместо этого инфраструктура определяет время с последнего вызова метода и решает, превышает ли оно задержку обновления. Если превышает, то Spring проверяет были ли изменения и следует ли обновить объект. В качестве другого примера допустим, что объект pdfGenerator используется очень интенсивно, скажем его методы вызываются по несколько раз в секунду. Если период обновления составляет 10 секунд, то объект не может быть обновляться чаще, чем раз в 10 секунд вне зависимости от частоты вызовов его методов. Таким образом, не следует беспокоиться о нагрузке на ресурсы вашей системы из-за мониторинга изменений.

Если вы используете несколько скриптовых объектов Groovy в приложении Spring и хотите установить период проверки изменений по умолчанию для всех, то для этого служит элемент <lang:defaults>. Пример его использования приведен в листинге 3.

Листинг 3. Задание значения по умолчанию для параметра refresh-check-delay
<lang:defaults refresh-check-delay="20000"/>

Элемент <lang:defaults>, приведенный в листинге 3, говорит о том, что все скриптовые объекты, написанные на языках Groovy, JRuby, BeanShell и т. д., будут обновляться с 20-тисекундной задержкой. Это значение по умолчанию может быть перегружено для конкретных объектов путем указания атрибута refresh-check-delay при их определении. Более того, для некоторых объектов можно даже вообще отключить автоматическое обновление, задав отрицательное значение атрибута refresh-check-delay (листинг 4).

Листинг 4. Переопределение значения refresh-check delay по умолчанию для конкретного объекта
<lang:defaults refresh-check-delay="20000"/>

<lang:groovy id="pdfGenerator"
             script-source="classpath:groovierspring/GroovyPdfGenerator.groovy"
             refresh-check-delay="60000">
    <lang:property name="companyName" value="Refreshable Groovy Bookstore"/>
</lang:groovy>

<lang:groovy id="invoiceEmailer"
             script-source="classpath:groovierspring/GroovyInvoiceEmailer.groovy"
             refresh-check-delay="-1"/>

В листинге 4 устанавливается задержка обновления по умолчанию равная 20 секундам. Однако, для объекта pdfGenerator она будет составлять 60 секунд, а для объекта invoiceEmailer автоматическое обновление будет полностью отключено.

Конфигурирование обновляемых объектов Groovy при помощи компонента Bean Builder в Grails

В первой статье рассказывалось об использовании класса Bean Builder в Grails (см. Ресурсы) для программного описания объектов Spring. При этом несложно добавить функцию автоматического обновления, хотя придется больше внимания уделить внутренним деталям Spring, поскольку Bean Builder не предоставляет средств, аналогичных <lang:groovy>. В листинге 5 показан пример конфигурирования проверки обновлений по умолчанию, а также установки индивидуального периода обновления для конкретного скриптового объекта.

Листинг 5. Конфигурирование обновляемых объектов Groovy при помощи Grails Bean Builder
def builder = new grails.spring.BeanBuilder()
builder.beans {
    scriptFactoryPostProcessor(ScriptFactoryPostProcessor) {
        defaultRefreshCheckDelay = 20000
    }
    pdfGenerator(GroovyScriptFactory,
                 'classpath:groovierspring/GroovyPdfGenerator.groovy') { bean ->
        companyName = 'Refreshable Bean Builder Bookstore'
        bean.beanDefinition.setAttribute(
            ScriptFactoryPostProcessor.REFRESH_CHECK_DELAY_ATTRIBUTE, 60000)
    }
}

Конфигурирование при помощи Bean Builder, показанное в листинге 5, логически эквивалентно описанию объекта pdfGenerator в листинге 4. Период проверки обновлений для всех скриптовых объектов устанавливается через свойство defaultRefreshCheckDelay объекта ScriptFactoryPostProcessor. Для задания периода проверки для конкретного объекта при помощи Bean Builder необходимо установить значение атрибута соответствующего объекта Spring. При использовании <lang:groovy> эти детали берет на себя Spring, в то время как при работе с Bean Builder о них приходится заботиться самому. Обратите внимание, что вам также необходимо объявить аргумент bean в замыкании объекта pdfGenerator, чтобы иметь возможность установить значение атрибута в описании объекта.

Настройка объектов Groovy

К этому моменту вы уже познакомились с возможностью автоматического обновления объектов Groovy на этапе выполнения, которая значительно повышает динамичность приложений. Кроме того, средства поддержки Groovy в Spring предоставляют возможность настройки объектов Groovy для придания им дополнительной гибкости. Настройка (customization) заключается во внедрении участка кода, реализующего определенную логику, в процесс создания объекта Groovy. Интерфейс GroovyObjectCustomizer (см. листинг 6) позволяет выполнять нужные действия над только что созданным объектом типа GroovyObject.

Листинг 6. Интерфейс GroovyObjectCustomizer
public interface GroovyObjectCustomizer {
    void customize(GroovyObject goo);
}

Метод интерфейса GroovyObjectCustomizer является функцией обратного вызова, которая вызывается Spring сразу после создания объекта Groovy. Таким образом можно либо реализовать определенные действия над объектом, либо применить какие-нибудь магические приемы мета-программирования, например заменить мета-класс объекта (см. Ресурсы). В листинге 7 показан код, который вычисляет и выводит в стандартный поток вывода время, затраченное на вызов метода объекта Groovy.

Листинг 7. Мониторинг производительности при помощи GroovyObjectCustomizer
public class PerformanceLoggingCustomizer implements GroovyObjectCustomizer {

    public void customize(GroovyObject goo) {
        DelegatingMetaClass metaClass = new DelegatingMetaClass(goo.getMetaClass()) {
            @Override
            public Object invokeMethod(Object object, String method, Object[] args) {
                long start = System.currentTimeMillis();
                Object result = super.invokeMethod(object, method, args);
                long elapsed = System.currentTimeMillis() - start;
                System.out.printf("%s took %d millis on %s\n", method, elapsed, object);
                return result;
            }
        };
        metaClass.initialize();
        goo.setMetaClass(metaClass);
    }
}

Класс PerformanceLoggingCustomizer, показанный в листинге 7, заменяет мета-класс объекта GroovyObject, перегружая метод invokeMethod с целью добавления участка кода для мониторинга производительности. Далее необходимо сконфигурировать настройщик таким образом, чтобы он применялся к одному или нескольким объектам Groovy. Это делается при помощи атрибута customizer-ref элемента <lang:groovy> (листинг 8).

Листинг 8. Конфигурирование объекта-настройщика Groovy
<bean id="performanceLoggingCustomizer"
      class="groovierspring.PerformanceLoggingCustomizer"/>

<lang:groovy id="pdfGenerator"
    refresh-check-delay="60000"
    script-source="classpath:groovierspring/GroovyPdfGenerator.groovy"
    customizer-ref="performanceLoggingCustomizer">
    <lang:property name="companyName" value="Customized Groovy Bookstore"/>
</lang:groovy>

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

pdfFor took 18 millis on groovierspring.GroovyPdfGenerator@f491a6

Добавление кода настройки в объекты Groovy не представляет трудностей. Гораздо сложнее реализация самого кода настройки – т. е. поведения объектов Groovy при их создании. Выше были продемонстрированы примеры конфигурирования при помощи <lang:groovy> и его атрибута customizer-ref. Если вы предпочитаете использовать Bean Builder для создания объектов Spring, то добавление кода настройки также делается достаточно просто. В листинге 9 показан пример добавления объекта-настройщика peformanceLoggingCustomizer.

Листинг 9. Добавление объекта-настройщика при помощи Grails Bean Builder
builder.beans {
    performanceLoggingCustomizer(PerformanceLoggingCustomizer)
    scriptFactoryPostProcessor(ScriptFactoryPostProcessor) {
        defaultRefreshCheckDelay = 20000
    }
    pdfGenerator(GroovyScriptFactory,
                 'classpath:groovierspring/GroovyPdfGenerator.groovy',
                 performanceLoggingCustomizer) { bean ->
        companyName = 'Refreshable Bean Builder Bookstore'
        bean.beanDefinition.setAttribute(
            ScriptFactoryPostProcessor.REFRESH_CHECK_DELAY_ATTRIBUTE, 60000)
    }
}

Groovy и базы данных

Стандартным образом Spring поддерживает вложенные скрипты, а также скрипты, загружаемые через интерфейс Resource в Spring (см. Ресурсы). Оба вида скриптов (в том числе скрипты, представляющие собой ресурсы в CLASSPATH) были представлены в первой статье. В этой статье мы лишь задействовали возможность динамического обновления объектов. Возможности Spring по загрузке, компиляции и обновлению объектов, написанных на динамических языках, базируются на интерфейсе ScriptSource, показанном в листинге 10 (комментарии Javadoc опущены для краткости).

Листинг 10. Интерфейс ScriptSource
public interface ScriptSource {

    String getScriptAsString() throws IOException;

    boolean isModified();

    String suggestedClassName();
}

Интерфейс ScriptSource содержит три метода: один для получения исходного кода скрипта, один для определения того, был ли скрипт изменен и один метод, который возвращает рекомендуемое имя скриптового класса. Spring предоставляет две реализации этого интерфейса: StaticScriptSource и ResourceScriptSource. Первый используется для скриптов, вложенных внутрь конфигурационного файла Spring, а второй – при загрузке скриптов через интерфейс Resource (например, при загрузке их из файлов на диске, в CLASSPATH или по URL).

Несмотря на широкий круг мест хранения, поддерживаемых статическими скриптами и интерфейсом Resource, иногда по ряду причин может потребоваться хранить скрипты в базе данных. Например, во многих организациях ограничен доступ к файловым системам серверов приложений, либо развертывание приложений возможно только в виде файла WAR или EAR. Кроме того, базы данных представляют собой транзакционные ресурсы, уже использующиеся в той или иной компании. Они предоставляют легкие в использовании средства для централизованного безопасного доступа к данным, которые не требуют специфических знаний о файловых системах, серверах и т. д. Наконец, хранение скриптов в базе данных означает, что их можно обновлять программным образом, предоставив пользователям право на редактирование. Разумеется, если вы храните код приложения в базе данных, то крайне важно тщательно рассмотреть все аспекты безопасности и защитить ваше приложение надлежащим образом.

Допустим вы хотите хранить ваши скрипты Groovy в реляционной базе данных. В Spring версии 2.5 и старше вы можете создавать новые типа скриптов путем реализации интерфейса ScriptSource и расширения некоторых классов Spring. В частности вам нужно будет модифицировать класс ScriptFactoryPostProcessor, чтобы он мог работать с новым классом, реализующим ScriptSource.

В листинге 11 показан класс DatabaseScriptSource, использующий Spring JDBC для загрузки скриптов из реляционной базы данных.

Листинг 11. Класс DatabaseScriptSource
public class DatabaseScriptSource implements ScriptSource {

    private final String scriptName;
    private final JdbcTemplate jdbcTemplate;
    private Timestamp lastKnownUpdate;

    private final Object lastModifiedMonitor = new Object();

    public DatabaseScriptSource(String scriptName, DataSource dataSource) {
        this.scriptName = scriptName;
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public String getScriptAsString() throws IOException {
        synchronized (this.lastModifiedMonitor) {
            this.lastKnownUpdate = retrieveLastModifiedTime();
        }
        return (String) jdbcTemplate.queryForObject(
                "select script_source from groovy_scripts where script_name = ?",
                new Object[]{ this.scriptName }, String.class);
    }

    public boolean isModified() {
        synchronized (this.lastModifiedMonitor) {
            Timestamp lastUpdated = retrieveLastModifiedTime();
            return lastUpdated.after(this.lastKnownUpdate);
        }
    }

    public String suggestedClassName() {
        return StringUtils.stripFilenameExtension(this.scriptName);
    }

    private Timestamp retrieveLastModifiedTime() {
        return (Timestamp) this.jdbcTemplate.queryForObject(
                "select last_updated from groovy_scripts where script_name = ?",
                new Object[]{ this.scriptName }, Timestamp.class);
    }
}

Класс DatabaseScriptSource, приведенный в листинге 11 достаточно прост, но границы его применения можно несколько расширить, особенно в том, что касается структур таблиц базы данных. Он ожидает, что в базе данных будет существовать таблица groovy_scripts со столбцами script_name, script_source и last_updated. В этом случае он сможет загружать скрипты из этой таблицы и проверять факт изменений.

Теперь необходимо сделать так, чтобы Spring мог распознавать скрипты типа DatabaseScriptSource. Для этого необходимо расширить класс ScriptFactoryPostProcessor и перегрузить метод convertToScriptSource, который отвечает за преобразование адреса скрипта (например, classpath:groovierspring/GroovyPdfGenerator.groovy) в экземпляр нужного класса, реализующего ScriptSource. Стандартная реализация класса ScriptFactoryPostProcessor показана в листинге 12.

Листинг 12. Метод convertToScriptSource класса ScriptFactoryPostProcessor
protected ScriptSource convertToScriptSource(
        String beanName, String scriptSourceLocator, ResourceLoader resourceLoader) {

    if (scriptSourceLocator.startsWith(INLINE_SCRIPT_PREFIX)) {
        return new StaticScriptSource(
                scriptSourceLocator.substring(INLINE_SCRIPT_PREFIX.length()), beanName);
    }
    else {
        return new ResourceScriptSource(resourceLoader.getResource(scriptSourceLocator));
    }
}

Как видите, стандартный класс умеет работать только со вложенными скриптами и скриптами, загружаемыми в виде ресурсов. При этом можно создать новый дочерний класс ScriptFactoryPostProcessor и перегрузить метод convertToScriptSource для загрузки скриптов из базы данных в виде экземпляров DatabaseScriptSource (листинг 13).

Листинг 13. Класс CustomScriptFactoryPostProcessor
public class CustomScriptFactoryPostProcessor extends ScriptFactoryPostProcessor {

    public static final String DATABASE_SCRIPT_PREFIX = "database:";

    private DataSource dataSource;

    @Required
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    protected ScriptSource convertToScriptSource(String beanName,
                                                 String scriptSourceLocator,
                                                 ResourceLoader resourceLoader) {
        if (scriptSourceLocator.startsWith(INLINE_SCRIPT_PREFIX)) {
            return new StaticScriptSource(
                scriptSourceLocator.substring(INLINE_SCRIPT_PREFIX.length()), beanName);
        }
        else if (scriptSourceLocator.startsWith(DATABASE_SCRIPT_PREFIX)) {
            return new DatabaseScriptSource(
                scriptSourceLocator.substring(DATABASE_SCRIPT_PREFIX.length()),
                dataSource);
        }
        else {
            return new ResourceScriptSource(
                resourceLoader.getResource(scriptSourceLocator));
        }
    }

}

Класс CustomScriptFactoryPostProcessor, показанный выше, аналогичен ScriptFactoryPostProcessor за тем исключением, что он способен загружать скрипт из базы данных в случае если адрес скрипта начинается с префикса database: (например, database:groovierspring/GroovyPdfGenerator.groovy). В идеале этот механизм должен обладать большей гибкостью (см. заметку "О возможности подключаемых локаторов скриптов"). Но тем не менее, теперь у вас есть возможность хранить ваши скрипты Groovy в базе данных.

Все что осталось – это сконфигурировать объект pdfGenerator таким образом, чтобы его исходный код загружался из базы данных. Вначале следует определить объект scriptFactoryPostProcessor типа CustomScriptFactoryPostProcessor, показанного в листинге 13. Затем следует определить объект pdfGenerator, задав адрес скрипта, указывающий на базу данных. Объект pdfGenerator может быть описан либо с помощью элемента <bean/>, либо (что более понятно) с помощью элемента <lang:groovy>. Во втором случае Spring проверит, присутствует ли в контексте приложения объект scriptFactoryPostProcessor типа ScriptFactoryPostProcessor и если нет, то создаст его автоматически. Если же присутствует, то Spring будет его использовать (это является иллюстрацией того, как можно подставить собственную реализацию scriptFactoryPostProcessor). Пример конфигурации приведен в листинге 14.

Листинг 14. Конфигурация объекта pdfGenerator, чей код хранится в базе данных
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/GroovierSpringDataSource"/>

<bean id="scriptFactoryPostProcessor"
      class="groovierspring.CustomScriptFactoryPostProcessor">
    <property name="dataSource" ref="dataSource"/>
</bean>

<lang:groovy id="pdfGenerator"
             refresh-check-delay="60000"
             script-source="database:groovierspring/GroovyPdfGenerator.groovy">
    <lang:property name="companyName" value="Database Groovy Bookstore"/>
</lang:groovy>

Фрагмент кода в листинге 14 не отличается особенной сложностью по сравнению с тем, что было показано выше. Объекту scriptFactoryPostProcessor нужен экземпляр DataSource, поэтому определяется соответствующий объект dataSource. Кроме этого момента единственным отличием от CLASSPATH-скрипта является то, что скрипт загружается из базы данных. Если вы предпочитаете использовать Bean Builder в Grails, то вам не составит труда сконфигурировать источник данных и специализированный ScriptFactoryPostProcessor с его помощью.

К этому моменту вы уже знаете как загружать скрипты Groovy из базы данных и обновлять их после редактирования. Таким образом, поддержка Groovy в Spring становится гибкой и динамической как никогда. Кроме того, вы узнали как можно создавать собственные реализации ScriptSource для загрузки скриптов из любого источника данных.

Отрицательные аспекты использования скриптов Groovy

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

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

  • Насколько критичной является система?
  • Каков эффект от потенциальных сбоев?
  • Насколько быстро могут быть устранены потенциальные проблемы?

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

Ошибки компиляции скриптов

Предположим, что вы изменили скрипт в процессе работы приложения таким образом, что он не компилируется. Когда Spring обнаружит изменение и попробует перезагрузить объект, то будет сгенерировано исключение ScriptCompilationException, являющееся оберткой над исходной ошибкой, например MultipleCompilationErrorsException в Groovy. В этом случае Spring отменит обновление объекта и исходный экземпляр продолжит работу, как ни в чем не бывало. Однако приложение должно отреагировать на исключение типа ScriptCompilationException соответствующим образом. Скорее всего необходимо отобразить сообщение об ошибке и послать уведомление (по e-mail или при помощи системы передачи мгновенных сообщений) разработчикам или операционистам. Разумеется, сотрудники, модифицирующие скрипты, должны убедиться, что они компилируются и старая версия объекта была благополучно заменена новой.

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

Ошибки на этапе выполнения скриптов

Проблемы, связанные с ошибками на стадии выполнения скриптов, аналогичны ошибкам в компилируемом коде: они приводят к тому, что приложение начинает функционировать некорректно, причем это поведение становится заметным для пользователей, поскольку система окажется неспособной выполнить нужные действия. Например, допустим вы внесли такое изменение в GroovyPdfGenerator, что скрипт компилируется, но выбрасывает исключение при попытке сгенерировать документ PDF. В этом случае либо код, вызывающий pdfGenerator, должен обработать это исключение, либо передать его на уровень вверх. Так или иначе, пользователь скорее всего увидит сообщение о том, что PDF не может быть сгенерирован (и что эта проблема будет устранена при первой возможности!)

Как и в случае с ошибками компиляции, ошибки при выполнении скриптов не должны быть критичными. На самом деле, поскольку скрипты можно изменить на лету, проблемы в них устранять легче, чем в компилируемом коде. Все что требуется – это внести коррективы в скрипт и перезагрузить его. Таким образом, возможность корректировки кода без останова приложения повышает не только гибкость изменения кода, но и упрощает процесс устранения проблем. Тем не менее, это не означает, что вам следует делать все объекты Spring обновляемыми. Как и множество других вещей, возможность обновления компонентов должна использоваться осмотрительно.

Вопросы безопасности при использовании скриптов

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

Проблему обеспечения безопасности делает еще более актуальной тот факт, что средства поддержки динамических языков в Spring позволяют изменять не только системные данные, но и поведение приложения. В какой-то степени это возможно и без скриптов, например вспомните такие типы атак, как SQL-внедрение вредоносного кода, межсайтовое скриптование JavaScript или межсайтовые запросы с целью подлога, которые также могут влиять на поведение системы. Однако в случае Groovy вам особенно важно тщательно продумать защиту скриптов, особенно если они являются обновляемыми.

В зависимости от того, как именно вы используете обновляемые объекты, преимущества обновления поведения в процессе работы могут перевешивать дополнительный риск. Представьте себе приложение для автоматизации продаж, в котором необходимо часто обновлять правила предоставления скидок, или приложение для страхования, в котором также могут часто меняться бизнес-правила. В этих случаях можно создать собственный DSL на основе Groovy, который затем может использоваться менеджерами по продажам или страховыми агентами для решения своих задач. В частности, они смогут добавить правило, предоставляющее десятипроцентную скидку при заказах на сумму более $50. Подобного рода изменениями можно управлять, разрешив пользователям редактировать небольшие фрагменты кода на DSL непосредственно в запущенной системе. Или возможно вы захотите создать графический редактор, при помощи которого пользователи смогут изменять систему скидок.

Заключение

В этой статье рассказывалось об использовании компилируемых классов Groovy, а также динамически загружаемых и обновляемых скриптов в приложениях на основе Spring. Вы узнали о таких возможностях, как обновление объектов Groovy, их настройка в момент создания, а также сохранение в реляционной базе данных. Кроме того, было рассказано об эффектах, связанных с ошибками на этапах компиляции и выполнения скриптов, о том, как обновляемые объекты могут упростить процедуру устранения проблем по сравнению с традиционными решениями, которые требуют повторного развертывания или перезапуска приложения. Наконец, были кратко затронуты вопросы, связанные с безопасностью скриптовых и обновляемых объектов, с упором на то, что необходимо внимательно оценивать уровень безопасности, который требуется для конкретного приложения.

Groovy и Spring представляют собой мощную связку. Spring берет на себя вопросы архитектуры и инфраструктуры, а Groovy добавляет динамические возможности. Способность Spring перезагружать скрипты Groovy после их изменения может существенно расширить возможности вашего приложения. Тем не менее, не забывайте, что "кому много дано, с того много и спрашивается". Повышение степени динамизма приложений несомненно делает их более гибкими и мощными, однако также может служить источником проблем, с которыми раньше вы могли не встречаться.


Ресурсы для скачивания


Похожие темы

  • Оригинал статьи: Groovier Spring, Part 2: Change application behavior at run time (Скотт Либернайт, developerWorks, январь 2009 г.). (EN)
  • Посетите домашнюю страницу проекта Spring Framework. (EN)
  • Spring AOP: в этой главе справочного руководства по Spring описываются вопросы аспектно-ориентированного программирования. (EN)
  • Прочитайте главу справочного руководства по Spring, посвященную абстракции Resource. В ней вы узнаете об интерфейсе Resource и различных его реализациях. (EN)
  • Прочитайте книгу Pro Spring (Роб Харроп и Ян Махацек, Rob Harrop и Jan Machacek, Apress, 2005 г.), в которой рассказывается обо всех аспектах Spring. (EN)
  • Grails Bean Builder: программное конструирование объектов Spring. (EN)
  • Сделать механизм локации скриптов подключаемым: прочитайте заявку на новую возможность Spring, заключающуюся в том, чтобы сделать механизм ScriptSource подключаемым. (EN)
  • Сотни статей по всем аспектам программирования на Java можно найти на сайте developerWorks в разделе Технология Java.
  • Spring: загрузите последнюю версию Spring. (EN)

Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Open source
ArticleID=421123
ArticleTitle=Groovy и Spring: Часть 2. Изменение поведения приложения в процессе выполнения
publish-date=08142009