Initialisierungsfehler bei exportierter Java-Anwendung

Online-Skater

Erfahrenes Mitglied
Hallo Tutorianer,

vllt. kann einer von euch dieses Phenomen erklären. Mein Programm läuft in der Entwicklungsumgebung Eclipse fehlerfrei. Nun habe ich es als lauffähige JAR-Datei exportiert. Ich starte die Anwendung und dann passiert es: java.lang.ExceptionInInitializerError. Nach der Untersuchung habe ich das Problem gefunden. Es hat mit statischer Initialiserung zu tun bzw. Objekte werden mit statischen Objekten initialisiert:
Java:
// Data type selections in Constants.java
public static final String[] S_LINESTYLES = {"none","dotted","dashed","solid","double","groove","ridge","inset","outset"};
Java:
JComboBox borderStyle = null;
// Hier tritt der Fehler auf
borderStyle = new JComboBox(Constants.S_LINESTYLES);
// So funktioniert es
borderStyle = new JComboBox(new String[] {"none","dotted","dashed","solid","double","groove","ridge","inset","outset"});
Ich habe mehrere solcher statischer Datentypen in der Constants.java definiert da mehrere Methoden diese nutzen. Die Frage ist nun warum es in der Entwicklungsumgebung funktioniert und bei der exportierten Anwendung nicht.

Mit freundlichen Grüßen
Online-Skater
 
Zuletzt bearbeitet von einem Moderator:

genodeftest

Erfahrenes Mitglied
Gibt es zirkuläre Abhängigkeiten? Also dass sich im statischen Code beider Klassen jeweils eine Referenz auf die andere befindet? oder Constants.java die andere Klasse importiert? Beides auch über Umwege?
 

Online-Skater

Erfahrenes Mitglied
Hi genodeftest,

es gibt keine zirkulären Abhängigkeiten. Die Constants.java importiert nicht eine andere Klasse. Um sicherzugehen das es nicht an statische Objekte liegt habe ich ein Minimalbeispiel gemacht und es funktioniert sowohl in der Entwicklungsumgebung als auch in exportierter Form.
Java:
import javax.swing.JComboBox;

public class TestClass 
{
	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{
		JComboBox cbxTest = new JComboBox(Constant.S_LINESTYLES);
		System.out.println("Anzahl Elemente: "+cbxTest.getModel().getSize());
		JComboBox cbxTest1 = new JComboBox(Constant.S_LINESTYLES);
		System.out.println("Anzahl Elemente: "+cbxTest1.getModel().getSize());
	}
}
Java:
public final class Constant 
{
	public static final String[] S_LINESTYLES = {"none","dotted","dashed","solid","double","groove","ridge","inset","outset"};
}
Somit kann es nicht direkt daran liegen aber die Fehlermeldung ist leider auch nicht sehr aussagekräftig.
Code:
java.lang.ExceptionInInitializerError
	at wpw.helper.components.BorderDialog.initializeComponents(BorderDialog.java:118)
	at wpw.helper.components.BorderDialog.<init>(BorderDialog.java:58)
	at wpw.helper.components.BorderPanel.<init>(BorderPanel.java:47)
	at wpw.general.Utilities.getEditableComponent(Utilities.java:92)
	at wpw.operation.config.ConfigView.getGroupPanel(ConfigView.java:110)
	at wpw.operation.config.ConfigView.<init>(ConfigView.java:63)
	at wpw.operation.config.ConfigOperation.<init>(ConfigOperation.java:27)
	at wpw.panels.PatternStyle.loadStyleDataFromXMLFile(PatternStyle.java:102)
	at wpw.panels.PatternStyle.renderingPage(PatternStyle.java:79)
	at org.netbeans.spi.wizard.WizardPage.addNotify(WizardPage.java:363)
	at java.awt.Container.addImpl(Container.java:1101)
	at java.awt.Container.add(Container.java:942)
	at org.netbeans.api.wizard.displayer.WizardDisplayerImpl.setCurrentWizardPanel(WizardDisplayerImpl.java:430)
	at org.netbeans.api.wizard.displayer.WizardDisplayerImpl.navigateTo(WizardDisplayerImpl.java:553)
	at org.netbeans.api.wizard.displayer.NavButtonManager.processNextProceed(NavButtonManager.java:439)
	at org.netbeans.api.wizard.displayer.NavButtonManager.processNext(NavButtonManager.java:422)
	at org.netbeans.api.wizard.displayer.NavButtonManager.actionPerformed(NavButtonManager.java:296)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2012)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2335)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:404)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:253)
	at java.awt.Component.processMouseEvent(Component.java:6203)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
	at java.awt.Component.processEvent(Component.java:5968)
	at java.awt.Container.processEvent(Container.java:2105)
	at java.awt.Component.dispatchEventImpl(Component.java:4564)
	at java.awt.Container.dispatchEventImpl(Container.java:2163)
	at java.awt.Component.dispatchEvent(Component.java:4390)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4461)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4125)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4055)
	at java.awt.Container.dispatchEventImpl(Container.java:2149)
	at java.awt.Window.dispatchEventImpl(Window.java:2478)
	at java.awt.Component.dispatchEvent(Component.java:4390)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:649)
	at java.awt.EventQueue.access$000(EventQueue.java:96)
	at java.awt.EventQueue$1.run(EventQueue.java:608)
	at java.awt.EventQueue$1.run(EventQueue.java:606)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:116)
	at java.awt.EventQueue$2.run(EventQueue.java:622)
	at java.awt.EventQueue$2.run(EventQueue.java:620)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:619)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:275)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:200)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:194)
	at java.awt.Dialog$1.run(Dialog.java:1072)
	at java.awt.Dialog$3.run(Dialog.java:1126)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.awt.Dialog.show(Dialog.java:1124)
	at java.awt.Component.show(Component.java:1496)
	at java.awt.Component.setVisible(Component.java:1448)
	at java.awt.Window.setVisible(Window.java:842)
	at java.awt.Dialog.setVisible(Dialog.java:1011)
	at org.netbeans.api.wizard.displayer.WizardDisplayerImpl.showInDialog(WizardDisplayerImpl.java:368)
	at org.netbeans.api.wizard.displayer.WizardDisplayerImpl.show(WizardDisplayerImpl.java:275)
	at org.netbeans.api.wizard.WizardDisplayer.showWizard(WizardDisplayer.java:107)
	at org.netbeans.api.wizard.WizardDisplayer.showWizard(WizardDisplayer.java:154)
	at wpw.main.WebPatternWizard.actionPerformed(WebPatternWizard.java:83)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2012)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2335)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:404)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:253)
	at java.awt.Component.processMouseEvent(Component.java:6203)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
	at java.awt.Component.processEvent(Component.java:5968)
	at java.awt.Container.processEvent(Container.java:2105)
	at java.awt.Component.dispatchEventImpl(Component.java:4564)
	at java.awt.Container.dispatchEventImpl(Container.java:2163)
	at java.awt.Component.dispatchEvent(Component.java:4390)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4461)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4125)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4055)
	at java.awt.Container.dispatchEventImpl(Container.java:2149)
	at java.awt.Window.dispatchEventImpl(Window.java:2478)
	at java.awt.Component.dispatchEvent(Component.java:4390)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:649)
	at java.awt.EventQueue.access$000(EventQueue.java:96)
	at java.awt.EventQueue$1.run(EventQueue.java:608)
	at java.awt.EventQueue$1.run(EventQueue.java:606)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:116)
	at java.awt.EventQueue$2.run(EventQueue.java:622)
	at java.awt.EventQueue$2.run(EventQueue.java:620)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:619)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:275)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:200)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:185)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:177)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:138)
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -1
	at java.lang.String.substring(String.java:1949)
	at wpw.general.Utilities.getJarExecutionDirectory(Utilities.java:50)
	at wpw.general.Constants.<clinit>(Constants.java:18)
	... 99 more
Die BorderDialog.java:118 ist nunmal:
Java:
borderStyle = new JComboBox(Constants.S_LINESTYLES);
in der Borderdialog.java
Java:
/**
 * 
 */
package wpw.helper.components;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import wpw.general.Constants;

/**
 * @author Ronald Hennig
 * @date Jun 30, 2011
 * @version 1.0  
 *
 */
public class BorderDialog extends JDialog implements ActionListener 
{	
	public static final int BORDER_MIN_WIDTH = 0;
	public static final int BORDER_MAX_WIDTH = 10;
	public static final int BORDER_STEPS = 1;
	
	// Format is: style width color => solid 1px #C1C1C1
	private String cssBorder;
	private JSpinner borderWidth;
	private JComboBox borderStyle;
	private ColorButton borderColor;
	private JButton btnOk;
	private JButton btnCancel;
	private BorderPreview preview;
	private boolean saveClicked = false;
	
	/**
	 * Standard constructor
	 * @param initialBorder Initial border settings
	 */
	public BorderDialog(final String initialBorder) 
	{
		cssBorder = initialBorder;
		initializeComponents();
		layoutComponents();
	}

	private void layoutComponents() 
	{
		JLabel lblBorderWidth = new JLabel("Border width:");
		JLabel lblBorderStyle = new JLabel("Border style:");
		JLabel lblBorderColor = new JLabel("Border color:");
		JLabel lblBorderPreview = new JLabel("Border preview:");
		JPanel pnlBorderSettings = new JPanel(new GridLayout(3, 2, 8, 5));
		pnlBorderSettings.add(lblBorderWidth);
		pnlBorderSettings.add(borderWidth);
		pnlBorderSettings.add(lblBorderStyle);
		pnlBorderSettings.add(borderStyle);
		pnlBorderSettings.add(lblBorderColor);
		pnlBorderSettings.add(borderColor);
		
		JPanel pnlPreview = new JPanel(new GridLayout(2,2,8,5));
		pnlPreview.add(lblBorderPreview);
		pnlPreview.add(preview);
		pnlPreview.add(btnOk);
		pnlPreview.add(btnCancel);
		
		setLayout(new BorderLayout());
		add(pnlBorderSettings, BorderLayout.NORTH);
		add(new JSeparator(), BorderLayout.CENTER);
		add(pnlPreview, BorderLayout.SOUTH);
		
		setTitle("Border settings");
		setModalityType(ModalityType.APPLICATION_MODAL);
		setResizable(false);
		setLocationRelativeTo(null);
		pack();
	}

	private void initializeComponents() 
	{
		borderColor = new ColorButton(extractBorderColor());
		borderColor.addPropertyChangeListener("background", new PropertyChangeListener() {
			
			@Override
			public void propertyChange(PropertyChangeEvent evt) 
			{
				preview.setColor(getBorderColor().getOrigColor());
				preview.repaint();
			}
		});
		borderWidth = new JSpinner(new SpinnerNumberModel(extractBorderWidth(), BORDER_MIN_WIDTH, BORDER_MAX_WIDTH, BORDER_STEPS));
		borderWidth.addChangeListener(new ChangeListener() {
			
			@Override
			public void stateChanged(ChangeEvent e) 
			{
				preview.setWidth(new Float((Integer)getBorderWidth().getValue()));
				preview.repaint();				
			}
		});
//		borderStyle = new JComboBox(Constants.S_LINESTYLES);
		borderStyle = new JComboBox(new String[] {"none","dotted","dashed","solid","double","groove","ridge","inset","outset"});
//		borderStyle = new JComboBox(new DefaultComboBoxModel(Constants.S_LINESTYLES));
		borderStyle.addItemListener(new ItemListener() {
			
			@Override
			public void itemStateChanged(ItemEvent e) 
			{
				if (e.getStateChange() == ItemEvent.SELECTED)
				{
					preview.setStyle(getBorderStyle().getSelectedItem().toString());
					preview.repaint();
				}
			}
		});
		preview = new BorderPreview(getBorderColor().getOrigColor(), getBorderStyle().getSelectedItem().toString(), new Float((Integer)getBorderWidth().getValue()));
		
		btnOk = new JButton("Save settings");
		btnOk.addActionListener(this);
		btnCancel = new JButton("Cancel");
		btnCancel.addActionListener(this);
		
		borderStyle.setSelectedItem(extractBorderStyle());
	}
	
	/**
	 * @return the borderWidth
	 */
	public final JSpinner getBorderWidth() {
		return borderWidth;
	}
	
	/**
	 * @return the preview
	 */
	public final BorderPreview getPreview() {
		return preview;
	}

	/**
	 * @return the borderStyle
	 */
	public final JComboBox getBorderStyle() {
		return borderStyle;
	}

	/**
	 * @return the borderColor
	 */
	public final ColorButton getBorderColor() {
		return borderColor;
	}

	/**
	 * @return the cssBorder
	 */
	public String getCSSBorder() {
		return cssBorder;
	}
	
	/**
	 * Extract the border color from the css border format
	 * @return border color
	 */
	private String extractBorderColor()
	{
		String borderColor = cssBorder.substring(cssBorder.indexOf("#"));
		return borderColor;
	}
	
	/**
	 * Extract the border width from the css border format
	 * @return border width
	 */
	private int extractBorderWidth()
	{
		String borderWidth = cssBorder.substring(cssBorder.indexOf(" ")+1, cssBorder.indexOf("px"));
		return Integer.parseInt(borderWidth);
	}
	
	/**
	 * Extract the border style from the css border format
	 * @return border style
	 */
	private String extractBorderStyle()
	{
		String borderStyle = cssBorder.substring(0, cssBorder.indexOf(" "));
		return borderStyle;
	}

	@Override
	public void actionPerformed(ActionEvent evt) 
	{
		// save settings
		if (evt.getSource().equals(btnOk))
		{
			String bStyle = borderStyle.getSelectedItem().toString();
			String bWidth = Integer.toString((Integer)borderWidth.getValue());
			String bColor = borderColor.getColor();
			
			cssBorder = bStyle+" "+bWidth+"px "+bColor;
			saveClicked = true;
		}
		this.setVisible(false);
	}
	
 	/**
 	 * Make the FontDialog visible and return the css border if user clicked the save button
 	 * @return css font or null
 	 */
 	public String showDialog()
	{
		setVisible(true);
		if (saveClicked)
		{
			saveClicked = false;
			return cssBorder;
		}
		return null;
	}
}
Woran kann das bloß liegen ?
 
Zuletzt bearbeitet von einem Moderator:
S

SE

Hast du dir mal den GESAMTEN Stack angesehen ?

CAUSED BY : java.lang.StringIndexOutOfBoundsException ...

Heißt also das du erstmal das Problem lösen musst und der java.lang.ExceptionInInitializerError nur ein Folgefehler ist.
 

Online-Skater

Erfahrenes Mitglied
Ok zur Aufklärung: In der Constants.java steht folgendes:
Java:
public static final String APP_PATH = Utilities.getJarExecutionDirectory();
Diese Funktion verhält sich je nach Ausführungsort der Anwendung unterschiedlich.
Java:
/**
	   * Returns the path where the currently running JAR-file is located.
	   * Example value: C:\MyProject\build\jar\
	   * @return Path of the JAR-file
	   */
	  public static String getJarExecutionDirectory()
	  {
	    String jarFile = null;
	    String jarDirectory = null;
	    int cutFileSeperator = 0;
	    int cutSemicolon = -1;
	 
	    jarFile = System.getProperty("java.class.path");
	    // Cut seperators
	    cutFileSeperator = jarFile.lastIndexOf(System.getProperty("file.separator"));
	    jarDirectory = jarFile.substring(0, cutFileSeperator);
	    // Cut semicolons
	    cutSemicolon = jarDirectory.lastIndexOf(';');
	    jarDirectory = jarDirectory.substring(cutSemicolon+1, jarDirectory.length());
	 
	    return jarDirectory+System.getProperty("file.separator");
	  }
Aus irgendwelchen Gründen gibt es bei der exportierten Version kein Dateiseparator, was darauf schließen lässt das der Inhalt von jarFile nicht richtig ist. Damit ist cutFileSeperator = -1 und das ist bei substring außerhalb des Wertebereichs. Anscheinend kann der java.class.path nicht ausgelesen werden.
Die Anwendung wird nicht direkt gestartet sondern in der Konsole (java -jar App.jar), was einen Unterschied macht.
 
Zuletzt bearbeitet von einem Moderator:
S

SE

Also um den aktuellen Pfad des Jar-Files zubestimmen habe ich eine bessere Methode welche schon einiges an Verbesserungen hinter sich hat :
Java:
private String getPath()
{
	StringBuilder sb=new StringBuilder();
	String urlPATH=this.getClass().getResource(this.getClass().getSimpleName()+".class").toString();
	int numChars=urlPATH.length(), i=0;
	char c;
	while(i<numChars)
	{
		c=urlPATH.charAt(i);
		switch (c)
		{
			case '%':
				sb.append(new String(new byte[] { (byte)Integer.parseInt(urlPATH.substring(i+1, i+3), 16) }));
				i+=3;
				break;
			default:
				sb.append(c);
				i++;
				break;
		}
	}
	String PATH=sb.toString();
	if(PATH.contains("!"))
		PATH=PATH.substring(0, PATH.indexOf("!"));
	PATH=(new File(PATH.substring(PATH.indexOf("/"), PATH.lastIndexOf("/")))).getAbsolutePath();
	return PATH;
}

Zur groben Arbeitsweise :

1) Bestimmung des absoluten Pfades zur Klasse als URL
2) Nachbau von URLDecoder ... simples Copy&Paste ... wichtig z.B. unter XP da "C:\Dokumente und Einstellungen\" als URL zu "file:///C:/Dokumente%20und%20Einstellungen/" wird
3) Abtrennen des Jar-Pfades ...
4) mit File.getAbsolutePath() einen OS-abhängigen , absoluten Pfad erzeugen ...

Ich verwende diese Methode selbst unter Win2k bis Win7 sowie unter OpenSuSE 11.x und Mac OS X ... und erhalte bei allen den korrekten System-Pfad ... auch mit Whitespaces und Sonderzeichen *unter Win z.B. Pfade mit deutschen Umlauten*.
Ich weis das diese Methode noch immer nicht perfekt ist ... und sie hat auch schon sehr viele Verbesserungen hinter sich *z.B. ist der URLDecoder-Nachbau erst drin seit ich es unter XP getestet und festgestellt habe das File aus der URL mit %20 *oder anderen* keinen Pfad mit Leerzeichen erzeugen kann ... was zu einer FileNotFoundException führt* ... aber aktuell ist es eine auf allen gängigen Systemen lauffähige Methode um den Pfad zur aktuell ausgeführten Jar zuverlässig zu bestimmen ... ganz ohne die Arbeit mit Arrays , OS-spezifischen Separatoren oder gar System-Properties ...


Ich hoffe das dir diese Methode in Zukunft genau so hilft wie sie mir bis jetzt geholfen hat.