Java: почему код, упакованный в jar-файл, препятствует доступу внешних классов?

У меня есть система плагинов для моего приложения Java. Я вызываю внешний класс с загрузчиком классов URL. Эта часть работает хорошо, как когда мое приложение запускается как файлы классов, так и когда мое приложение находится в форме JAR. Проблема, с которой я сталкиваюсь, заключается в том, что файлы плагинов могут выполнять собственный независимый код, но они создают JPanel. Когда я пытаюсь добавить этот JPanel к JPanel, который находится в основном классе приложения, я получаю исключение нулевого указателя, ссылающееся на основной класс. (com.cpcookieman.app.Main) Но этого не произойдет, если я запускаю файлы классов для приложения, только когда оно упаковано. Как я могу решить это?

Почему мой код, упакованный в файл jar, не позволяет внешним классам получать доступ к классам внутри jar?

РЕДАКТИРОВАТЬ: По запросу, трассировки стека.

java.lang.NullPointerException
    at TestPlugin2.Main.<init>(Main.java:23)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at java.lang.Class.newInstance0(Unknown Source)
    at java.lang.Class.newInstance(Unknown Source)
    at com.cpcookieman.ph.PluginLoader$2.run(PluginLoader.java:74)
    at java.lang.Thread.run(Unknown Source)

РЕДАКТИРОВАТЬ 2: код загрузки класса

    String pluginsPath;
    if (Main.os.equals("Windows"))
    {
        pluginsPath = "C:\\plugins\\";
    }
    else
    {
        pluginsPath = "~/plugins/";
    }
    File file = new File(pluginsPath);
    if (!file.exists())
    {
        System.out.println("DEBUG: Plugin path could not be found!");
    }
    try
    {
        URL url = file.toURI().toURL();
        final URL[] urls = new URL[]{url};
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    ClassLoader cl = new URLClassLoader(urls);
                    Class plugin = cl.loadClass(text + ".Main");
                    plugin.newInstance();
                    close();
                }
                **snip catch statements**
            }
        }).start();
    }
    **snip catch statements**

Плагин загружается с использованием его конструктора и создает панель для добавления к фрейму, который находится внутри одного из классов в файле JAR. JAR является основным приложением, если кто-то запутался в этом.

 user16639022 июн. 2012 г., 05:57
& Quot; Wut & Quot ;? Возможно, у них нет JAR в СР? (Тем не мение,a NPE is a different issue в большинстве случаев ... так как сбой загрузки класса приводит к исключению Nasty-Non-NPE-Exception, которое часто убивает виртуальную машину. Возможно, посмотрите на это, вместо того, чтобы обвинять загрузчик / загрузку классов :-)
 davidfrancis22 июн. 2012 г., 07:21
Вам нужно будет опубликовать часть своего кода, начните с TestPlugin2.Main.java. Еще лучше было бы опубликовать все это или отдельный небольшой пример, демонстрирующий проблему - HTH
 Crazenezz22 июн. 2012 г., 06:14
Вы можете опубликовать трассировку стека?
 user16639022 июн. 2012 г., 06:07
JAR должен бытьin путь к классам; он не способен поставить себя там. (Также убедитесь, что устаревшая версия, которую класс / классы не использует / не использует ... так как это может раздражать, чтобы отследить.)
 Paulywog22 июн. 2012 г., 06:02
Должен ли JAR находиться в определенной папке, чтобы он находился внутри пути к классам? Если это так, может ли JAR самонастраиваться, чтобы поместить себя в путь к классам?

Ответы на вопрос(1)

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

Учтите, что мой проект размещен наC:\Mine\JAVA\J2SE\src\testingjar>

Внутри этого моя структура каталогов выглядит следующим образом:

                         testingjar
                             |
              ----------------------------------
             |          |           |          |
          classes      src    manifest.text  test.jar(this .jar we be creating shortly)
             |          |
             |       (Almost same as classes folder, just .java files)
      ---------------
      |             |
  actualtest       test
      |             |
   *.class       *.class

Мой класс, который станет частью файла .jar, приведен ниже:

package test;

import java.awt.*;
import javax.swing.*;

public class CustomPanel extends JPanel
{
    public CustomPanel()
    {
        setOpaque(true);
        setBackground(Color.DARK_GRAY);

        JLabel label = new JLabel(
            "I am a JLabel from Java Swing", JLabel.CENTER);
        label.setOpaque(false); 
        label.setForeground(Color.WHITE);
        add(label); 
    }

    @Override
    public Dimension getPreferredSize()
    {
        return (new Dimension(500, 300));
    }
}

I compiled this class with the following command :

C:\Mine\JAVA\J2SE\src\testingjar>javac -d classes src\test\CustomPanel.java

Now Creating Manifest File for the JAR File, the contents of which are as follows :

Main-Class: test.CustomPanel

Do remember the space between colons (:) and the package name i.e. test, and after CustomPanel, Press Enter and save the file.

Теперь для создания файла JAR с именемtest.jarЯ написал эти команды:

C:\Mine\JAVA\J2SE\src\testingjar>cd classes

C:\Mine\JAVA\J2SE\src\testingjar\classes>jar -cfm ..\test.jar ..\manifest.txt test

Now the class that will use this .jar file will be like this :

package actualtest;

import test.CustomPanel;

import java.awt.*;
import javax.swing.*;

public class ActualImplementation
{
    private void createAndDisplayGUI()
    {
        JFrame frame = new JFrame("Testing Jar Implementation");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        CustomPanel panel = new CustomPanel();
        frame.setContentPane(panel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String... args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new ActualImplementation().createAndDisplayGUI();
            }
        });
    }
}

I compiled this by writing these commands :

C:\Mine\JAVA\J2SE\src\testingjar\classes>cd..

C:\Mine\JAVA\J2SE\src\testingjar>javac -classpath test.jar -d classes src\actualtest\ActualImplement
ation.java

Now to run I wrote these commands :

C:\Mine\JAVA\J2SE\src\testingjar>cd classes

C:\Mine\JAVA\J2SE\src\testingjar\classes>java actualtest.ActualImplementation

OUTPUT

JAR IMPLEMENTATION

Сопоставьте шаги, может быть, вы что-то упустили, так как это хорошо работает на моей стороне.

LATEST EDIT : As asked, I did that the other way around, now JFrame is inside the .jar file and JPanel is using it.

Класс, который станет частью файла .jar, выглядит следующим образом:

package test;

import java.awt.*;
import javax.swing.*;

public class CustomFrame extends JFrame
{
    public CustomFrame(String title)
    {
        super(title);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
}

Contents of the manifest.txt file, will change as follows :

Main-Class: test.CustomFrame

И класс, который будет использовать CustomFrame Class из файла .jar, выглядит следующим образом:

package actualtest;

import test.CustomFrame;

import java.awt.*;
import javax.swing.*;


// http://stackoverflow.com/a/11150286/1057230
public class CustomPanel extends JPanel
{
    private CustomFrame frame;

    public CustomPanel()
    {
        setOpaque(true);
        setBackground(Color.DARK_GRAY);

        JLabel label = new JLabel(
            "I am a JLabel from Java Swing", JLabel.CENTER);
        label.setOpaque(false); 
        label.setForeground(Color.WHITE);
        add(label);                 
    }

    private void createAndDisplayGUI()
    {
        frame = new CustomFrame("Testing Jar Implementation");
        frame.setContentPane(this);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    @Override
    public Dimension getPreferredSize()
    {
        return (new Dimension(500, 300));
    }

    public static void main(String... args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new CustomPanel().createAndDisplayGUI();
            }
        });
    }
}

Последовательность компиляции очень похожа на предыдущую:

C:\Mine\JAVA\J2SE\src\testingjar>javac -d classes src\test\CustomFrame.java

C:\Mine\JAVA\J2SE\src\testingjar>cd classes

C:\Mine\JAVA\J2SE\src\testingjar\classes>jar -cfm ..\test.jar ..\manifest.txt test

C:\Mine\JAVA\J2SE\src\testingjar\classes>cd..

C:\Mine\JAVA\J2SE\src\testingjar>javac -classpath test.jar -d classes src\actualtest\CustomPanel.jav
a

C:\Mine\JAVA\J2SE\src\testingjar>cd classes

C:\Mine\JAVA\J2SE\src\testingjar\classes>java actualtest.CustomPanel

Все тот же вывод я получаю.

LATEST EDIT :

Я только что узнал, что иногда эта вещь работает, а не позже, при использовании JAR-файлов, вы должны указать путь к классу, включая оператор точки., лайк

C:\Mine\JAVA\J2SE\src\testingjar>java -classpath test.jar;.; actualtest.CustomPanel

Это когда я принесactualtest package внутриtestingjar Папка, то вышеупомянутая команда работала для этой ситуации.

 22 июн. 2012 г., 07:19
Это все еще работает, если я пойду наоборот, посмотрите это редактирование сейчас.
 Paulywog22 июн. 2012 г., 16:45
Ваш код не использует никакой формы динамической загрузки классов. Так как я создаю функцию плагина, я не знаю, что создают разработчики или как она будет называться. Как таковой, он не может быть импортирован в мое основное приложение или создан так же, как вы его создаете. Я попытаюсь опубликовать небольшой пример кода о том, как загружается мой класс, в основной статье.
 Paulywog22 июн. 2012 г., 06:53
Мне очень трудно обернуть голову вокруг того, что ты делаешь, но я думаю, что у тебя все наоборот. У меня есть кадр в файле jar, и файл jar загружает файл класса с панелью в нем, и в этот момент класс пытается добавить его панель в кадр jar. Кажется, у вас есть панель в jar-файле, а фрейм загружается внешним классом. У меня нет проблем с передачей данных в этом направлении.

Ваш ответ на вопрос