Как сохранить пары имя-значение в среде Tomcat? - PullRequest
3 голосов
/ 11 февраля 2011

У нас есть сервлет, которому нужны определенные переменные, такие как пароли, соли шифрования и т. Д., Чтобы они не были сохранены в файловой системе навсегда. Это то, что мы делаем в настоящее время (резюме):

Во время инициализации,

  1. Сценарий Perl устанавливает ReadMode в 2 для маскирования эхо-вывода stdout, запрашивает у пользователя переменные, фильтрует известный файл для их помещения и вызывает tomcat / bin / startup.sh

  2. Метод сервлета init () считывает переменные из файла и удаляет его (файл).

Проблема: Когда WAR перекомпилируется, tomcat пытается развернуть его (autodeploy = true), что мы и хотим. Но файла данных больше нет, поэтому выдается исключение FileNotFoundException (правильно).

Вопрос: Есть ли свойство или некоторая HashMap / Table, доступная для сервлетов, где несколько переменных могут быть сохранены во время запуска вручную? Идея состоит в том, что init () может проверить их, если файл данных не существует во время повторного развертывания. Спасибо, - ср.

Ответы [ 5 ]

4 голосов
/ 15 февраля 2011

Как насчет передачи их в качестве системных свойств при вызове startup.sh, т.е.'bin / startup.sh -DencryptionSalt = foobar'?Затем вы можете вызвать System.getProperty ("encryptionSalt") для продолжительности JVM.

2 голосов
/ 12 февраля 2011

Поместите ваши изменчивые данные в JNDI.JNDI не очищается между перераспределениями.Ваш сервлет все еще может делать то же самое во время init, чтобы обеспечить актуальность данных в JNDI.

Я не могу вспомнить, являются ли операции чтения / записи JNDI поточно-ориентированными, но если это не так, вы всегда можете поместить поточно-ориентированный объект (например, ConcurrentHashMap) и использовать его без проблем.

РЕДАКТИРОВАТЬ по MS:

==========
restart.pl:
==========
ReadMode 2; print "\nEnter spice: "; chomp ($spice = <STDIN>); print "\n";
ReadMode 0;
open (FP, ">$spicefile") or die "$0: Cannot write file $spicefile: $!\n";
print FP "$spice\n"; close (FP);
system "bin/shutdown.sh";       sleep 8;
system "bin/startup.sh";        sleep 8;        system "wget $tomcaturl";
system '/bin/rm -rf *spicefile*';               # delete wget output file
foreach $sCount (1..10) {                       # give it 10 more secs
    sleep 1;
    if (-f $spicefile) {
        print "$0: Waiting on servlet to delete spicefile [$sCount]\n"
    } else {
        print "$0: Successful servlet initialization, no spicefile\n";
        exit 0
}}
print "\n$0: deleting file $spicefile ...\n";   # Error condition
system "unlink $spicefile";
exit 1;

===========
context.xml
===========
    <Resource name="MyServlet/upsBean" auth="Container" type="packageName.UPSBean"
                factory="org.apache.naming.factory.BeanFactory" readOnly="false"/>

===================
app/WEB-INF/web.xml
===================
  <listener>
        <listener-class>packageName.DBInterface</listener-class>
  </listener>
  <resource-env-ref>
    <description>Use spice as transferable during redeployment</description>
    <resource-env-ref-name>MyServlet/upsBean</resource-env-ref-name>
    <resource-env-ref-type>packageName.UPSBean</resource-env-ref-type>
  </resource-env-ref>

==============
MyServlet.java:
==============
init() {
        if (new File (spiceFile).exists())      # Same name in restart.pl
                dbi = new DBInterface (spiceFile);
        else
                dbi = new DBInterface ();       # Redeployment
        otherInitializations (dbi.getSpice());
}

================
DBInterface.java:
================
public DBInterface () {
        // Comment out following block if contextInitialized works
        FileInputStream fin = new FileInputStream (safeDepositBox);
        ObjectInputStream ois = new ObjectInputStream (fin);
        UPSBean upsBean = (UPSBean) ois.readObject();
        ois.close();
        spice = upsBean.getSpice();
        dbiIndex = 2;
        // do stuff with spice
}

public DBInterface (String spiceFileName) {
        File file = new File (spiceFileName);
        BufferedReader br = new BufferedReader (new FileReader (file));
        spice = br.readLine();
        br.close();
        file.delete();
        dbiIndex = 1;

        // Delete following block if contextInitialized works
        UPSBean upsBean = new UPSBean();
        upsBean.setSpice (spice);
        FileOutputStream fout = new FileOutputStream (safeDepositBox);
        ObjectOutputStream oos = new ObjectOutputStream (fout);
        oos.writeObject (upsBean);
        oos.flush();
        oos.close();
        // do stuff with spice and if it works, ...
        // contextInitialized (null);
}

// Above is working currently, would like the following to work

public void contextDestroyed(ServletContextEvent sce) {
        System.setProperty ("spice", spice);
        System.out.println ("[DBInterface" + dbiIndex +
                                        "] Spice saved at " +
                        DateFormat.getDateTimeInstance (DateFormat.SHORT,
                                        DateFormat.LONG).format (new Date()));
}

public void contextInitialized(ServletContextEvent sce) {
        if (sce != null) {
                spice = System.getProperty ("spice");
                System.out.println ("[DBInterface" + dbiIndex +
                                        "] Spice retrieved at " +
                        DateFormat.getDateTimeInstance (DateFormat.SHORT,
                                        DateFormat.LONG).format (new Date()));
        }
        // do stuff with spice
}

============
UPSBean.java:
============
public class UPSBean implements Serializable {
        private String  spice = "parsley, sage, rosemary and thyme";
        public UPSBean() { }
        public String getSpice() {      return spice;   }
        public void setSpice (String s) {       spice = s;      }
}

Я пытаюсь проверить, работает ли get / setProperty выше.Пытался использовать JNDI напрямую, но когда я установилSpice с помощью ресурса MyServlet / upsBean в contextDestroyed () и попытался прочитать его в contextInitialized (), я получаю нулевое значение (извините, я уже удалил эту часть кода).Теперь объявление в context.xml, а также resource-env-ref стало избыточным.Текущее решение - сохранить сериализованный экземпляр в файл данных (не очень хорошо).

1 голос
/ 17 февраля 2011

Нет ничего в Tomcat, чтобы делать то, что вы хотите. Если вы перенесете настройки в дерево JNDI для tomcat, вам нужно будет поместить комбо пароль пользователя / имени в файл server.xml или context.xml. Вот несколько возможных решений этой проблемы.

Вариант 1. Использование прослушивателя Tomcat

Если вы посмотрите на верхнюю часть файла tomcat server.xml, вы увидите несколько слушателей. Это классы java, которые выполняются при запуске tomcat. Вы можете создать своего собственного слушателя tomcat, который считывает пароль из файловой системы, удаляет файл и сохраняет имя пользователя / пароль Comobo так, чтобы это было доступно для приложения. Приемник tomcat привязан к жизненному циклу сервера tomcat, поэтому автоматическое повторное развертывание приложения не приведет к перезагрузке класса прослушивателя tomcat. Код для прослушивателя tomcat должен быть помещен в jar-файл и помещен в папку CATALINA_HOME \ lib.

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

Слушатель также может получить доступ к глобальному дереву JNDI tomcat и поместить туда комбо имя пользователя и пароль, и тогда вашему приложению потребуется файл context.xml, использующий элемент ResourceLink, чтобы сделать глобальную запись jndi доступной для приложения. Вам также придется проделать дополнительную работу, чтобы этот подход работал с модульными тестами, так как поиск в JNDI обычно усложняет модульное тестирование.

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

Вариант 2. Использование прослушивателя контекста сервлета

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

Вариант 3: использовать JMX

Создайте JMX MBean, зарегистрируйте его в JVM MBeanServer, а затем используйте его для хранения комбинации имени пользователя и пароля. Если вы инициализируете этот MBean из прослушивателя tomcat, вы можете сделать так, чтобы скрипт perl вызывал MBean удаленно и передавал комбинацию имени пользователя / пароля.

Надеюсь, это поможет.

1 голос
/ 15 февраля 2011

Tomcat не может вам здесь помочь.

Это плохие новости.

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

  • Tomcat предоставляет при запуске контейнера возможность прослушивания через элемент Listener.Используйте их, чтобы отслеживать, когда контейнер запускается, выключается и т. Д.
  • API сервлета предоставляет ServletContextListener для прослушивания запуска веб-приложения, завершения работы
  • Когда запускается tomcat - вы можете перейти в Свойства системысначала установив JAVA_OPTS.Но будьте осторожны - эти значения могут быть обнаружены с помощью команды ps.
  • В зависимости от метода развертывания - вы можете добавить настройки для своей конфигурации через my-app.xml (где my-app - это имя приложениядля развертывания)
  • В CATALINA_BASE / conf / context.xml - вы также можете установить параметры init / env для веб-приложения, которое будет отображаться для ВСЕХ веб-приложений.
  • Если вы НЕ используете защитуменеджер - вы должны иметь возможность устанавливать / очищать некоторые системные свойства.

Учитывая все вышеперечисленное - вы можете прочитать «защищенные» значения из файла и записать их в системное свойство.Затем для дополнительной защиты, если вы не хотите, чтобы значения всегда существовали как системные свойства - вы можете использовать ServletContextListener, чтобы прочитать значения системных свойств и удалить их из системных свойств.Затем при повторном развертывании - слушатель будет писать сброс настроек системы при завершении работы - поэтому, когда веб-приложение перезапускается - они все еще там.

Таким образом, последовательность запуска будет выглядеть следующим образом

  • Администратор вводит pw (существующий процесс), и файл сохраняется
  • При запуске веб-приложения - ServletContextListner ищет файл.Если существует - использует эти значения
  • При закрытии веб-приложения - записывает значения в System.properties
  • При перезапуске веб-приложения - ServletContextListner обнаруживает, что файл отсутствует, и использует System.properties.Затем удаляет system.properties

С помощью вышеизложенного вы можете выполнить повторное развертывание, не записывая пароли на диск и, возможно, свести к минимуму векторы атак, временно сохранив их как system.properties.

Хорошоудачи.

1 голос
/ 11 февраля 2011

Если вы хотите обработать это программно, я думаю, что вам может понадобиться ServletContextListener .Создайте класс, который реализует интерфейс, и закодируйте необходимые функции в contextInitialized (ServletContextEvent sce) -метод, см. здесь для простого примера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...