#java #рефлексия
Задача заключается в использовании классов/объектов в динамическом классе(созданного из строкового представления). см. javaFileContents Проблема заключается в объявлении и дальнейшей инициализации объектов. После прохода строки System.out.println(Connector.class); возникает ошибка: java.lang.NoClassDefFoundError: controller/Connector, аналогично и со строками в комментариях. Понятно, что ошибка связана с тем, что компилятор "не знает" о классе Connector, но пакет, в котором он расположен, подключен ч/з JavaCompiler API(параметр optionList метода getTask) и явно импортирован import controller.*;. Путь к jar'нику валидный(переменная pathToPackage). В случае, если путь указан не верно, после прохода строки import controller.*; и import model.*; компилятором, генерируется ошибка: /CompiledClass.java:4: error: package controller does not exist import controller.*; ^ /CompiledClass.java:5: error: package model does not exist import model.*; ^ Оба пакета располагаются по адресу: C:\Users\leonid\AppData\Roaming\JDeveloper\system12.1.3.0.41.140521.1008\o.j2ee\drs\forum\ControllerEJB.jar, те ./controller и ./model и в этих директориях соответственно откомпилированные классы. Т.е. в итоге компилятор видит пакет и удачно импортирует его в класс, но не видит классов внутри пакета, использование других классов/объектов, из стандартных java-lib, безошибочно компилируются, допустим после строки System.out.println(String.class) ошибки не возникает и результат удачно выводится. Код: public class CompilerJavaManager { public static void compileJavaFileObject(StringBuilder inputString, Writer jspOut, String sessionId, String pathToPackage) throws Exception { String className = "CompiledClass"; String classMethod = "methodOf" + className; Class[] parameters = new Class[] { sessionId.getClass() }; Object[] arguments = new Object[] { sessionId }; Class compiledClass = null; //Class c = Class.forName(className); StringBuilder javaFileContents = new StringBuilder("" + //"package compiled;" + '\n' + "import java.io.*;\n" + "import java.util.*;\n" + "import controller.*;\n" + "import model.*;\n" + '\n' + "public class " + className +"{\n" + " private Writer out = null;\n" + //" private Connector connector = null;\n" + //" private UPContainer container = null;\n" + //" private Controller controller = null;\n" + //" private userHandler handler = null;\n" + '\n' + " public " + className +"(Writer out){\n" + " this.out = out;\n" + " }\n" + '\n' + " public void " + classMethod + "(String sessionId) throws java.io.IOException{\n" + " System.out.println(Connector.class);" + //" Connector connector = Connector.getConnector();\n" + //" UPContainer container = Connector.getConnector().getContainerInstance(sessionId);\n" + //" Controller controller = container.getControllerInstance();\n" + //" userHandler handler = container.getUserHandlerInstance();\n" + //inputString + " }\n" + "}\n"); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector diagnosticsCollector = new DiagnosticCollector(); StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(diagnosticsCollector, null, null); SpecialClassLoader classLoader = new SpecialClassLoader(); SpecialJavaFileManager fileManager = new SpecialJavaFileManager(standardJavaFileManager, classLoader); ListoptionList = new ArrayList (); optionList.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path") + ";" + pathToPackage)); JavaObjectFromString javaObjectFromString = new JavaObjectFromString(className, javaFileContents.toString()); Iterable fileObjects = Arrays.asList(javaObjectFromString); Iterable classes = null; Writer out = new PrintWriter(jspOut); CompilationTask task = compiler.getTask(out, fileManager, diagnosticsCollector, optionList, classes, fileObjects); Boolean result = task.call(); List diagnostics = diagnosticsCollector.getDiagnostics(); if (result) { try{ compiledClass = classLoader.findClass(className); Constructor constructor = compiledClass.getConstructor(Writer.class); Object instance = constructor.newInstance(jspOut); Method instanceMethod = compiledClass.getDeclaredMethod(classMethod, parameters); instanceMethod.invoke(instance, arguments); } catch(java.lang.reflect.InvocationTargetException ex) { System.out.println(ex.getCause()); } } else { // Compilation fails for (Diagnostic d : diagnostics) { System.out.println(d); } } } private static class JavaObjectFromString extends SimpleJavaFileObject { private String sourceCode = null; public JavaObjectFromString(String className, String sourceCode) throws Exception { super(URI.create("file:///" + className + Kind.SOURCE.extension), Kind.SOURCE); this.sourceCode = sourceCode; } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return sourceCode; } @Override public OutputStream openOutputStream() { throw new IllegalStateException(); } @Override public InputStream openInputStream() { return new ByteArrayInputStream(sourceCode.getBytes()); } } private static class JavaObjectFromByteCode extends SimpleJavaFileObject { private ByteArrayOutputStream baos; public JavaObjectFromByteCode(String className) { super(URI.create("byte:///" + className + Kind.CLASS.extension), Kind.CLASS); } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { throw new IllegalStateException(); } @Override public OutputStream openOutputStream() { baos = new ByteArrayOutputStream(); return baos; } @Override public InputStream openInputStream() { throw new IllegalStateException(); } public byte[] getBytes() { return baos.toByteArray(); } } private static class SpecialClassLoader extends ClassLoader { private Map m = new HashMap (); protected Class findClass(String className) throws ClassNotFoundException { JavaObjectFromByteCode jobc = m.get(className); if (jobc==null){ jobc = m.get(className.replace(".","/")); if (jobc==null){ return super.findClass(className); } } return defineClass(className, jobc.getBytes(), 0, jobc.getBytes().length); } public void addClass(String className, JavaObjectFromByteCode jobc) { m.put(className, jobc); } } private static class SpecialJavaFileManager extends ForwardingJavaFileManager { private SpecialClassLoader scl; public SpecialJavaFileManager(StandardJavaFileManager sjfm, SpecialClassLoader scl) { super(sjfm); this.scl = scl; } public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { JavaObjectFromByteCode jobc = new JavaObjectFromByteCode(className); scl.addClass(className, jobc); return jobc; } public ClassLoader getClassLoader(Location location) { return scl; } } } В дополнение от 15.12.2015 14:56(МСК): Код класса connector: package controller; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; public class Connector { private static Connector connector = null; private static Context context = null; private List containers = new ArrayList (); public Connector() throws NamingException { context = getInitialContext(); } public static synchronized Connector getConnector() { try { if (connector == null) { connector = new Connector(); } } catch(Exception ex){ ex.printStackTrace(); } return connector; } public synchronized UPContainer getContainerInstance(String session) { UPContainer container = new UPContainer(); boolean has = false; try { Iterator iterator = containers.iterator(); while (iterator.hasNext()) { UPContainer ch = (UPContainer) iterator.next(); if (session.equalsIgnoreCase(ch.getSession())) { has = true; container = ch; break; } } if (!has) { container.setSession(session); container.setControllerInstance((Controller) context.lookup("forum-Controller-Controller#controller.Controller")); container.setUserHandlerInstance((userHandler) context.lookup("forum-Controller-userHandler#controller.userHandler")); containers.add(container); } } catch (Exception ex) { ex.printStackTrace(); } return container; } public synchronized boolean isActive(String userName) { try { Iterator iterator = containers.iterator(); while (iterator.hasNext()) { UPContainer ch = (UPContainer) iterator.next(); if (ch.getUserHandlerInstance().getUserCookie() != null) if (userName.equalsIgnoreCase(ch.getUserHandlerInstance().getUserCookie().getValue())) return true; } } catch (Exception ex) { ex.printStackTrace(); } return false; } public synchronized void destroyContainerInstance(String session) { try { Iterator iterator = containers.iterator(); while (iterator.hasNext()) { UPContainer ch = (UPContainer) iterator.next(); if (session.equalsIgnoreCase(ch.getSession())) { ch.setControllerInstance(null); ch.setUserHandlerInstance(null); containers.remove(ch); break; } } } catch (Exception ex) { ex.printStackTrace(); } } public synchronized int getAmountOfUsersOnline(boolean sm) { try { if (sm) { int counter = 0; Iterator iterator = containers.iterator(); while (iterator.hasNext()) { UPContainer ch = (UPContainer) iterator.next(); if (ch.getUserHandlerInstance().getUserCookie() != null) counter++; } return counter; } } catch (Exception ex) { ex.printStackTrace(); } return containers.size(); } public synchronized List getOnlineUsers() { List usersList = new ArrayList(); try { Iterator iterator = containers.iterator(); while (iterator.hasNext()) { UPContainer ch = (UPContainer) iterator.next(); if (ch.getUserHandlerInstance().getUserCookie() != null) usersList.add(ch.getUserHandlerInstance().getUserCookie().getValue()); } } catch (Exception ex) { ex.printStackTrace(); } return usersList; } private Context getInitialContext() throws NamingException { Hashtable env = new Hashtable(); // WebLogic Server 10.x/12.x connection details env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL, "t3://127.0.0.1:7101"); return new InitialContext(env); } } Дополнение от 15:49: Код класса pageController: package controller; import model.Categories; import model.Threads; public class pageController { private int topicsPerPage = 15; private int viewsPages = 5; private int currentPage = 0; private int currentAmountPages = 0; private int amountPages = 0; private int tagCloudlinks = 20; public pageController() { } public void setDefaultTopicsPerPage(int perPage) { topicsPerPage = perPage; } public int getDefaultTopicsPerPage() { return topicsPerPage; } public int getPagesLimit() { return viewsPages; } public void setPagesLimit(int viewsLimit) { viewsPages = viewsLimit; } public int getCurrentPage() { return currentPage; } public void setCurrentPage(String page) { currentPage = page != null && page != "" ? Integer.parseInt(page) : 0; } public int getCurrentAmountPages() { return currentAmountPages; } public void setCurrentAmountPages(int size) { setAmountPages(size); currentAmountPages = (int)Math.ceil(new Long(size).doubleValue()/new Long(topicsPerPage).doubleValue()); } public void setAmountPages(int amount) { amountPages = amount; } public int getAmountPages() { return amountPages; } public void setAmountCloudLinks(int amount) { tagCloudlinks = amount; } public int getAmountCloudLinks() { return tagCloudlinks; } } UPDATE Проблема до сих пор не решена, бессмысленная трата времени доводит меня до безумия, натыкал пакет controller,с классами, где только можно указал classpath везде где только можно, но до сих пор возникает ошибка java.lang.NoClassDefFoundError: controller/Connector. Если создать отдельное приложение с приведенными выше классами то компиляция завершается успешно System.out.println(Connector.class); выводит класс коннектор, как и было нужно, но если запускать через JSP, то возникает выше описанная проблема Смысл всей этой затеи: запрос к БД за кодом(Java,PHP,HTML) -> парсинг всего этого дела-> в JSP вызов компиляции -> готовый HTML код в JSP
Ответы
Ответ 1
Причина возникновения ошибки NoClassDefFoundError сильно варьируется, те за первопричиной возникновения ее скрыт ряд проблем. В моем же случае ошибка связана с повторной загрузкой класса загрузчиками классов и областью видимости другими загрузчиками классов(см иерархия загрузчиков классов), те один из загрузчиков пытается найти на своем уровне нужный класс, но не находит его здесь, и делегирует запрос на вышестоящий загрузчик(см делегирование запросов), далее на вышестоящем уровне происходит подгрузка необходимого класса, но с другой стороны необходимый класс подгужается другим загрузчиком(не имеющим отношений с предыдущим) при компилиции класса(из строкового представления см StringBuilder класс CompiledClass) и в происходит генерация ошибки(сигнализация что класс не найден). Конкретно: в jsp(Web App) происходит подгрузка классов, но так как они не известны на этом уровне загрузчиков, то загрузчик на этом уровне делегирует загрузку на выше стоящие уровни загрузчиков и в последствии происходит загрузка на Java EE App(где определены классы) уровне. С др стороны подгрузка происходит на др уровне при компилиции класса CompiledClass и возникает ошибка. Те два загрузчика(несвязные) загружают одни и те же классы. Почему компилируется успешно в отдельном приложении? Как описывал выше компиляция в этом случае успешна. Почему? Ответ: классы подгружаются только одним загрузчиком и никаких исключений соотв при этом не возникает. Просьба не редактировать это сообщение тк проблему решил не до конца(пока не решил) в последствии дам более точный конкретный ответ. Как вариант можно попробовать рефлексию(пока не проверял) в компилируемом классе(из строкового представления), но есть одно но, понятно что при увеличении "сложности" класса придется прописывать прописывать обращения множеством строк кода, по-этому этот вариант не совсем подходит.
Комментариев нет:
Отправить комментарий