2014-10-12 2 views
2

Я пытаюсь запустить сервер Jetty с веб-контекстом. web.xml, который я передаю, содержит пользовательский класс, который (по разным причинам) относится к пути к классам кода, выполняющего приведенный ниже код. Я ожидаю, что Jetty не сможет найти класс, указанный в web.xml, но это так.Запуск WebAppContext в изоляции

Как бы построить new Server с WebAppContext что будет не загружать классы из текущего контекста?

Код указан в Scala, но он должен дать вам представление о том, что я пробовал. Если вы ответите на Java, это прекрасно.

// A reference to the root class loader, this one should not know 
// about any of my custom classes 
val rootLoader = { 
    // Recursive method to find the loader without parent 
    def parent(loader:ClassLoader):ClassLoader = { 
    val p = loader.getParent 
    if (p == null) loader else parent(p) 
    } 
    parent(getClass.getClassLoader) 
} 

// Check if the custom ClassFile is available from the rootLoader 
val className = classOf[my.custom.servlet.ClassFile].getName 
val classAvailable = 
    try { 
    rootLoader.loadClass(className) 
    true 
    } catch { 
    case e:ClassNotFoundException => false 
    } 
// If I'm not insane no error is thrown 
if (classAvailable) sys.error("Class should not be available") 

// Create a WebAppContext using an empty WebAppClassLoader 
// based on that rootLoader 
val context = new WebAppContext() 
context.setParentLoaderPriority(false) 
//context.setClassLoader(new WebAppClassLoader(rootLoader, context)) 
context.setClassLoader(rootLoader) 
context.setContextPath("/") 
context.setDescriptor(webXmlFile) 
context.setResourceBase(resourceBase) 

// Create an start the server with the context 
val server = new Server(port) 
server.setHandler(context) 
server.start() 

Я ожидал контексте не найти соответствующий класс.

+0

Вопрос не ясен. Похоже, вы хотите, чтобы класс сервера переопределял класс webapp? Это правильно? –

+0

@JoakimErdfelt Конечным результатом является то, что я хочу, чтобы 'WebAppContext' загружал класс, который является (как строка) в' web.xml', используя загрузчик классов. Класс (другая версия), однако, также присутствует в коде, создающем сервер.Вышеприведенный код показывает, что 'WebAppContext', похоже, не уважает загрузчика классов: он находит класс. Это упрощенный пример, показывающий, что «WebAppContext» достигает другого загрузчика классов, пытающегося найти класс. Интересно, что я делаю неправильно, я ожидаю, что он не сможет найти этот класс. – EECOLOR

+0

пример, который у вас есть, слишком надуманный, я вижу, что вы пытаетесь манипулировать загрузчиками классов, но там должен быть лучший способ, но сначала нам нужно понять, какова ваша цель, а не ваши попытки добраться туда. Таким образом, это класс, определенный в web.xml webapp, и вы хотите, чтобы этот класс загружался из загрузчика класса сервера (ваш особый случай нужен), а не загрузчик классов webapp (который имеет тот же класс, по имени, но не функциональность). Это правильное утверждение вашего вопроса? –

ответ

2

OK EEColor, у меня было копаться в коде, и я думаю, что у нас есть 2 вопроса, на которые я открыл эти ошибки:

Теперь ошибка №1 является причиной того, что даже если вы устанавливаете полностью изолированный загрузчик классов, Webapp может магически по-прежнему загружать класс. Ошибка №2 не должна влиять на вас, если вы не начнете играть с предложением Joakim о настройке системных и серверных классов, и даже тогда вы, вероятно, все в порядке, потому что класс существует как в родительской иерархии класса контейнера, так и в загрузчике классов и ошибка возникает только тогда, когда системный класс отсутствует в контейнере-загрузчике классов, но присутствует в загрузчике классов webapp.

Итак, вы можете попробовать использовать предложение Joakim о добавлении класса в качестве системного класса, и это должно работать. Кроме того, вы также можете просто установить WebAppContext.setParentLoaderPriority (true), а также попробовать сначала иерархию загрузчика классов контейнеров. Хотя имейте в виду, что setParentPriority (true) будет применяться ко всей загрузке классов для webapp, в то время как предложение Joakim ограничивает различное поведение загрузки только для рассматриваемого класса.

Jan

+0

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

+0

Это может быть причиной одной из проблем, которые я испытываю на работе. У нас загружен webapp, который использует привязку slf4j, а также за пределами webappcontext у нас есть slf4j-binding. Мы получаем 'LinkageError' относительно' org.slf4j.impl.StaticLoggerBinder.getLoggerFactory() Lorg/slf4j/ILoggerFactory; 'потому что оба загрузчика классов имеют другую версию. Это не должно иметь значения с относительной изоляцией. Спасибо за сообщение об ошибке. – javaj

0

Я могу только предположить, что рассматриваемый класс находится в WEB-INF/lib или WEB-INF/classes. Не знаю, почему вы хотите убедиться, что веб-приложение не может найти этот класс, но попробуйте использовать загрузчик классов, который не является экземпляром WebAppClassLoader, как новый URLClassLoader с пустым списком URL-адресов и rootLoader в качестве родителя.

Jan

+0

Я хочу, чтобы он не нашел класс, потому что класс является частью пакета, на который опирается код, выполняющий 'server.start'. Проблема в том, что Jetty находит класс, который не следует искать с точки зрения безопасности. – EECOLOR

+0

Даже с пустым URLClassLoader ему удается найти класс. Кажется, что «WebAppContext» не использует данный загрузчик классов для загрузки сервлета в дескриптор. – EECOLOR

+1

ОК EEColor, я вырыл и думаю, что у нас есть 2 проблемы, для которых я открыл эти ошибки: https://bugs.eclipse.org/bugs/show_bug.cgi?id=447462 и https://bugs.eclipse.org/bugs/show_bug.cgi?id=447461 В вашем случае, если я правильно понимаю, существуют 2 разных версии одного и того же класса: один по пути класса контейнера и один по пути класса webapp, и вы хотите использовать один из контейнера. Если это так, то просто используйте WebAppContext.setParentLoaderPriority (true), и он должен найти класс, который находится в пути класса контейнера. – Jan

1

Вопрос не очень хорошо сказано, его трудно понять, что ваша цель есть.

Совет: В будущем, спросите, как достичь цели, а затем указать путь к которому вы пытаетесь

Давайте предположим, у вас есть веб-приложение под названием example-webapp.war

Он имеет простой WEB-INF/web.xml с записью сервлета как таковой ...

<servlet> 
    <servlet-name>test</servlet-name> 
    <servlet-class>com.company.foo.TestServlet</servlet-class> 
    </servlet> 

    <servlet-mapping> 
    <servlet-name>test</servlet-name> 
    <url-pattern>/test</url-pattern> 
    </servlet-mapping> 

example-webapp.war приходит со своим собственным com.company.foo.TestServlet (находится в WebApp-х WEB-INF/classes плачевными ctory)

Теперь у вас есть встроенный-Jetty экземпляр, вы хотите запустить example-webapp.war, но вы хотите использовать реализацию com.company.foo.TestServlet, которая существует вне example-webapp.war (с различной функциональностью).

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

Это отверстие должно:

  1. сделать веб-приложение Загрузчик классов способны видеть класс сервера
  2. Принудительный веб-приложение, чтобы использовать версию класса сервера по версии класса WebApp

Это достаточно легко выполнить во встроенном причале.

Давайте начнем с создания пользовательской версии com.company.foo.TestServlet и ее доступности в пути к классам для сервера встроенной пристани.

Наконец, настройте WebAppContext, чтобы сконфигурировать изолирующее отверстие класса загрузчика, упомянутое выше. (Использование системных классов и конфигураций серверных классов)

package demo; 

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.InputStreamReader; 
import java.net.HttpURLConnection; 
import java.net.URI; 

import org.eclipse.jetty.server.Server; 
import org.eclipse.jetty.webapp.WebAppContext; 

public class EmbedWithOverridenServlet 
{ 
    public static void main(String[] args) throws Exception 
    { 
     int port = 8080; 
     Server server = new Server(port); 

     String wardir = "path/to/example-webapp.war"; 

     WebAppContext webapp = new WebAppContext(); 
     webapp.setWar(wardir); 
     webapp.setContextPath("/"); 

     // don't hide server classes from webapps (hole #1 from answer) 
     // (allow webapp to use ones from system classloader) 
     webapp.addServerClass("-com.company.foo."); 

     // webapp cannot change or replace these classes 
     // force use of server classes (hole #2 from answer) 
     webapp.addSystemClass("com.company.foo."); 

     server.setHandler(webapp); 

     try 
     { 
      server.start(); 
      server.dumpStdErr(); 
      makeWebRequest(new URI("http://localhost:8080/test")); 
     } 
     finally 
     { 
      server.stop(); 
     } 
    } 

    private static void makeWebRequest(URI uri) throws IOException 
    { 
     HttpURLConnection conn = (HttpURLConnection)uri.toURL().openConnection(); 
     conn.setAllowUserInteraction(false); 

     int status = conn.getResponseCode(); 
     System.out.printf("Response Status: %d%n", status); 

     if(status != 200) 
     { 
      System.out.printf("ERROR: %s%n", conn.getResponseMessage()); 
      return; 
     } 

     try(InputStream in = conn.getInputStream(); 
      InputStreamReader reader = new InputStreamReader(in); 
      BufferedReader buf = new BufferedReader(reader)) 
     { 
      System.out.printf("Response Content Type: %s%n", conn.getHeaderField("Content-Type")); 
      String line; 
      while ((line = buf.readLine()) != null) 
      { 
       System.out.printf("[resp] %s%n",line); 
      } 
     } 
    } 
} 

Update: 2014-Oct-16

Проект демонстрирует это доступно на github.com/jetty-project/jetty-classloader-manipulation

example-webapp является вполне нормальным WebApp с a single servlet от its own.

example-embedded-servlet-override имеет replacement for that servlet, а также embedded jetty main class для развертывания example-webapp. У этого есть system property check against use.server.class, чтобы продемонстрировать как поведение, нормальное (изолированное), так и переопределенное (дырокол).

Результат выглядит следующим образом:

Нормальный/Изолированный Поведение: (нет системы опоры или с помощью -Duse.server.class=false.)

Response Status: 200 
Response Content Type: text/plain; charset=ISO-8859-1 
[resp] Hello Test Class: com.company.foo.TestServlet 
     from ClassLoader [email protected] 

Переопределенный/Перфорация Поведение: (с использованием -Duse.server.class=true)

Response Status: 200 
Response Content Type: text/html; charset=ISO-8859-1 
[resp] <html><body> 
[resp] <h1>Hello Server Impl</h1> 
[resp] <p>Class: com.company.foo.TestServlet 
     from ClassLoader [email protected]</p> 
[resp] </body></html> 

Надеюсь, это поможет вам понять он характер того, с чем вы работаете.

+0

Joakim, вы также можете сделать шаблон конкретным для класса, так что webapp.addSystemClass ("com.company.foo.TestServlet"). Jan – Jan

+0

Спасибо за подсказку. Возможно, Jetty ведет себя так, как ожидалось, я попытаюсь создать положительный тестовый тест в свете ошибок @Jan. Мое наблюдаемое поведение состояло в том, что он загружал класс с сервера вместо webapp. Это может быть вызвано тем, что он не смог найти класс в webapp. – EECOLOR

+0

@ EECOLOR я пошел вперед и сделал для вас тестовый проект. Когда вы работаете над своим положительным тестом, первое, что вам нужно сделать, - это устранить все усилия 'setClassLoader()' и просто работать со стандартным поведением, не сразу переходить к мысли, что требуется управление иерархиями загрузчиков классов (это редко есть). –

Смежные вопросы