27. März 2007

Spring und Log4J in einer Web-Applikation

Problem: Spring wird in einer Web-Applikation verwendet. Log4J loggt trotz korrekter Konfiguration nicht in die vorgesehenen LogFiles.

Lösung: In der web.xml muss folgender Eintrag stehen:

<web-app>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
...
</web-app>

NHibernate 1.2 HowTo

Zur Codegenerierung aus Datenbanktabellen wird MyGeneration empfohlen. Dort muss zunächst ein NHibernate Template heruntergeladen werden.

Im VisualStudio-Projekt muss folgender Config-Eintrag in die app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section
name="hibernate-configuration"
type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"
/>
</configSections>

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="default_schema">dbo</property>
<property name="connection.connection_string">Server=myserver;Initial Catalog=mydb;Integrated Security=True</property>
<property name="show_sql">true</property>
<mapping assembly="MyAssembly" />
</session-factory>
</hibernate-configuration>
</configuration>

Wichtig ist, dass die generierten *.obm.xml-Files in VisualStudio als eingebette Ressource über die Properties definiert sind. Die Mapping-Files müssen nicht explizit im Config-File angegeben werden. Durch den Eintrag mapping assembly werden alle .obm.xml-Files in dieser Assembly verwendet.

Beispiel für das Neuanlegen eines Datensatzes:

Configuration cfg = new Configuration();
ISession session = cfg.Configure().BuildSessionFactory().OpenSession();
ITransaction transaction = session.BeginTransaction();

ProfilProjekt pro = new ProfilProjekt();
pro.Kunde = "Kunde";
pro.LfdNr = 1;
session.Save(pro);
transaction.Commit();

session.Close();

22. März 2007

Umlaute beim Posten falsch verschickt mit UTF-8

Problem: Bei UTF-8 werden Umlaute beim Posten aus einem Formular falsch vom Browser verschickt und kommen fehlerhaft beim Server an.

Lösung: Beim Verwenden eines HttpFilters, muss folgendes aufgenommen werden:


public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) {
HttpServletRequest req = (HttpServletRequest)servletRequest;

try {
req.setCharacterEncoding("UTF-8");
}
catch (UnsupportedEncodingException e) {
logger.error(e);
}
...

21. März 2007

Sonderzeichen in XML

&      &amp;
' &apos;
< &lt;
> &gt;
" &quot;

Expression Language not supported in compile time

Problem: Die Ausdruckssprache wird im compile time-Attribut items nicht unterstützt, Expression Language not supported in compile time
Mögliche andere Fehlermeldung: ExpressionEvaluatorManager JasperException

Lösung:

a)
isELIgnored="true" in der Page directive der jsp:
<%@ page contentType="text/html;charset=windows-1252" isELIgnored="true" %>
b)
oder in der web.xml folgenden Eintrage hinzu:

<jsp-config>
<jsp-property-group>
<display-name>Ignore EL</display-name>
<url-pattern>*.jsp</url-pattern>
<el-ignored>true</el-ignored>
</jsp-property-group>
</jsp-config>


Ursache:

Defaulteinstellung:
  • für JSP-Seiten ab Servlet 2.4 : EL evaluieren
  • für JSP-Seiten bis einschl. Servlet 2.3 : EL ignorieren
Die EL wird erst mit JSP 2.0 ausgeliefert, kann aber auch mit JSP 1.2 betrieben werden, dann werden einige JARs benötigt (jstl.jar, …)

Es gibt älter Taglibs, die eine EL-Syntax für ihre Attribute verwenden. Diese wird bis Servlet 2.3 vom JSP-Compiler ignoriert. Wird die JSP, die diese Taglib enthält nun unter einem Servlet-Container >2.4 betrieben, tritt dieser Fehler auf. Die EL-Syntax darf bei diesen älteren Taglibs aber nicht vom JSP-Compiler evaluiert werden. Die Libs stellen eigene Logik dafür bereit.
Daher muss das Evaluieren von EL-Syntax deaktiviert werden.

Links:
http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSPIntro7.html
http://blogs.oracle.com/Didier/JSTL_1_JSP_2

18. März 2007

Globale Datasourcen unter Tomcat

Stichworte:

JNDI, global, Datasource, DB, JDBC


 

Problem:

Eine Globale Resource in einer Webanwendung verwende.


 

Lösung:

Zunächst muss die Resource global definiert werden


 

<GlobalNamingResources>

    <!-- JNDI-Datasource fuer die Testdatenbank -->

<Resource name="jdbc/DB2TGLOBAL"

        auth="Container"

        type="javax.sql.DataSource"/>


 

    <ResourceParams name="jdbc/DB2TGLOBAL">

        <parameter>

            <name>factory</name>

            <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>

        </parameter>

        

        ...

        

    </ResourceParams>        

</GlobalNamingResources>


 

Im Context der Anwendung muss eine Referenz auf die globale Datasource angegeben werden:


 

<ResourceLink name="jdbc/DB2TPooledDS" global="jdbc/DB2TGLOBAL" type="javax.sql.DataSource" />

    


 

14. März 2007

Eine .NET Collection in ein Array eines bestimmten Typsumwandeln

ArrayList al = (DataGridColumnStyle[])al.ToArray(typeof(DataGridColumnStyle)));

Active Directory DN über SAM-Account via LDAP ermitteln

String searchBase = "DC=xxx,DC=xyz,DC=co,DC=za";

String searchString = "SamAccountName=";

String dn = "";

SearchControls constraints = new SearchControls();

constraints.setSearchScope (SearchControls.SUBTREE_SCOPE);

// Perform seach

NamingEnumeration results =

ctx.search(searchBase, searchString + username.trim(), constraints);

// Loop though results

if (results.hasMore())

{

SearchResult sr = (SearchResult) results.next();

dn = sr.getName();

dn=dn + "," + searchBase

}

ctx.close();

ctx=null;

return dn;

Detailiertes Hibernate-Logging in Log4J

<!-- Limit the org.hibernate category to INFO since logging to DEBUG affects performance badly -->

<category name="org.hibernate">

<priority value="INFO"/>

</category>

<!-- Log SQL statements-->

<category name="org.hibernate.SQL">

<priority value="DEBUG"/>

</category>

<!-- Log the values assigned to the SQL parameters and results -->

<category name="org.hibernate.type">

<priority value="DEBUG"/>

</category>

<!-- Log transaction event/JTA -->

<category name="org.hibernate.transaction">

<priority value="DEBUG"/>

</category>

<category name="org.hibernate.event.def.AbstractFlushingEventListener">

<priority value="

Hibernate Fehler “object references an unsaved transientinstance - save the transient instance before flushing”

Prüfen, ob im Mapping ein unsaved-value definiert ist:

<id name="id" column="id" unsaved-value="0">

JDEV org.hibernate.QueryException bei org.hibernate.hql.ast.HqlToken

Stichworte:

JDev, JDeveloper, org.hibernate.QueryException, ClassNotFoundException : org.hibernate.hql.ast.HqlToken

Problem:

Im JDeveloper bekommt man mit Hibernate folgende Exception zur Laufzeit geworfen:


 

org.hibernate.QueryException: ClassNotFoundException: org.hibernate.hql.ast.


 

Ursache:

In der JDeveloper-Verzeichnisstruktur befindet sich im Toplink-Verzeichnis eine Version von antlr.jar -> Diese verträgt sich nicht mit dem antlr.jar, das Hibernate benötigt.


 

Lösung:

Das antlr.jar von Jdeveloper muss einfach gelöscht/ersetzt werden.

JDeveloper - DB-Schemas im Connection Manager hinzufügen

Stichworte:

JDeveloper 10.1.3 DB Connection Schema

Problem:

Per Default ist immer nur das Schema des Benutzers unter den eingerichteten Conenctions sichtbar

Lösung:

Zum Hinzufügen eines weiteren Schemas, muss unter Database die gewünschte DB gewählt werden und dann oben auf den "Trichter" geklickt werden.

Dort kann man dann sämtliche Schemas hinzufügen.

JDEV OC4J und Log4J Problem

Problem: no class definition found

Lösung: in orion-web.xml:

<web-app-class-loader search-local-classes-first="true"

include-war-manifest-class-path="true" />

JMX ab JDK 1.5

Ab JDK 1.5 kann man java über einen Parameter mitteilen, dass ein JMX-Server gestartet werden soll:

java -Dcom.sun.management.jmxremote AppName

Optional kann auch noch ein Port angegeben werden für den Remotezugriff

-Dcom.sun.management.jmxremote.port=1234
-Dcom.sun.management.jmxremote.authenticate=false

Im Tomcat kann man den Parameter über folgende Variable setzen

set JAVA_OPTS=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=12345

Das ganz kann man sich dann mit JConsole (befindet sich bin-Ordner des SDK) grafisch anschauen.

Wichtig: Folgende Dateien müssen in $JRE\lib\management angelegt werden:

- jmxremote.access (Zugriff für einen User bestimmen)

monitorRole readonly
controlRole readwrite


- jmxremote.password (Passwort für einen User bestimmen)

monitorRole QED
controlRole R&D


Der Zugriff auf Filesystemebne muss so eingeschränkt werden, dass nur der Benutzer, der die java.exe ausführt der Besitzer der Dateien ist und nur er Zugriff darauf hat.


In der Datei management.properties können viele Sicherheitseinstellungen gemacht werden.

Z.B. kann hier konfiguriert werden, ob der Remote-JMX-Zugriff per SSL erfolgen soll.

Connection Pooling unter Tomcat

Stichworte:
Connection Pooling, DB, DBCP, Tomcat, Webserver, Java, JDBC

Problem:
Connection-Pooling in Tomcat nutzen

Ursache:
-

Lösung:

Nutzung von Commons DBCP (http://jakarta.apache.org/commons/dbcp/index.html). Es kann über den Parameter poolPreparedStatements auch Caching von PreparedStatements aktiviert werden.

1) In der server.xml die Datasource definieren

<Resource name="jdbc/DB2PooledDS"
auth="Container"
type="javax.sql.DataSource"/>

<ResourceParams name="jdbc/DB2PooledDS">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>

<!-- Maximum number of dB connections in pool. Make sure you
configure your mysqld max_connections large enough to handle
all of your db connections. Set to 0 for no limit.
-->
<parameter>
<name>maxActive</name>
<value>100</value>
</parameter>

<!-- Maximum number of idle dB connections to retain in pool.
Set to 0 for no limit.
-->
<parameter>
<name>maxIdle</name>
<value>30</value>
</parameter>

<!-- Maximum time to wait for a dB connection to become available
in ms, in this example 10 seconds. An Exception is thrown if
this timeout is exceeded. Set to -1 to wait indefinitely.
-->
<parameter>
<name>maxWait</name>
<value>10000</value>
</parameter>

<!-- MySQL dB username and password for dB connections -->
<parameter>
<name>username</name>
<value>myuser</value>
</parameter>
<parameter>
<name>password</name>
<value>mypass</value>
</parameter>

<!-- Class name for mm.mysql JDBC driver -->
<parameter>
<name>driverClassName</name>
<value>COM.ibm.db2.jdbc.app.DB2Driver</value>
</parameter>


<!-- Prepared Statement Pooling aktivieren -->
<parameter>
<name>poolPreparedStatements</name>
<value>true</value>
</parameter>
<parameter>
<name>maxOpenPreparedStatements</name>
<value>50</value>
</parameter>



<!-- The JDBC connection url for connecting to your MySQL dB.
The autoReconnect=true argument to the url makes sure that the
mm.mysql JDBC Driver will automatically reconnect if mysqld closed the
connection. mysqld by default closes idle connections after 8 hours.
-->
<parameter>
<name>url</name>
<value>jdbc:db2:mydb</value>
</parameter>

</ResourceParams>


2) In web.xml der Anwendung angeben

<web-app id="WebApp">
...
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/DB2PooledDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

</web-app>


3) Connection im Java-Code öffenen über JNDI

try {

InitialContext initCtx = new InitialContext();

Context ctx = (Context) initCtx.lookup(“java:comp/env”);

//Context ctx = new InitialContext();

if (ctx == null) {

logger.error("No InitialContext available");

} else {

dataSource = (DataSource) ctx.lookup(“jdbc/DB2PooledDS”);

}

} catch (NamingException ne) {

logger.error(ne);

}

try {

Connection conn = getDataSource().getConnection();

} catch (SQLException e) {

logger.error(e);

}

Tomcat Remote Debuggen

Umgebungsvariablen setzen:

JPDA_ADDRESS=8888
JPDA_TRANSPORT=dt_socket

Tomcat starten mit

catalina jpda start.


Sollte es zu folgender Fehlermeldung kommen:

Transport dt_socket failed to initialize

Dann müssen die

dt_socket.dll und dt_shmem.dll

ins Tomcat-Bin Verzeichnis kopiert werden oder
Umgebungsvariable path=\bin setzen.


Alternative in die Umgebungsvariable JAVA_OPTS folgenden Eintrag:

set JAVA_OPTS=-Xrunjdwp:transport=dt_socket,server=y,address=9999,suspend=n

JSF ADF Projekt nach 10.1.3.1 Migrationsproblem

Stichworte:

JDEV, JDeveloper, JSF, ADF, Migration, Problem, 10.1.3.1, OC4J

Problem:

Nach der Migration eines JSF/ADF Projekts nach JDeveloper 10.1.3.1gibt es folgende Fehlermeldung zur Laufzeit

500 Internal Server Error

java.lang.NoSuchMethodError: oracle.adf.view.faces.context.AdfFacesContextFactory.createContext(Ljava/lang/Object;Ljava/lang/Object;)Loracle/adf/view/faces/context/AdfFacesContext; at oracle.adfinternal.view.faces.webapp.AdfFacesFilterImpl.doFilter(AdfFacesFilterImpl.java:187) at …

Ursache:

Das Projekt verwendet in seinem WEB-INF\lib-Pfad veraltete Libraries (JSF und ADF), die mit dem neuen OC4J nicht funktionieren.


Lösung:

Es müssen die Libraries aus dem JDEV 10.1.3.1 in Lib-Pfad kopiert werden.


Datum / Zahlen in/von lokale(n) String(s) wandeln


NumberFormat nf = NumberFormat.getNumberInstance();

double d = nf.parse("12,3332").doubleValue());

DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT,new Locale("de", "DE"));

String s = dateFormatter.format(datum);

Log4J Filter für eine Kategorie

Stichworte:

log4j, category, debug, error, info, only, nur eine, LevelRangeFilter

Problem:

Man möchte in einem File nur Logging-Messages einer Kategorie (und nicht der darüber liegenden auch noch), z.B. INFO speichern.

Lösung:

Es muss ein LevelRangeFilter verwendet werden:


<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>


<appender name="INFO" class="org.apache.log4j.DailyRollingFileAppender">

<param name="File" value="logs/PSASAS_info.log"/>

<param name="DatePattern" value="'.'yyyy-MM-dd"/>

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d %-4r [%t] %-5p (%c:%L)/>

</layout>


<filter class="org.apache.log4j.varia.LevelRangeFilter">

<param name="LevelMin" value="INFO" />

<param name="LevelMax" value="INFO" />

</filter>

</appender>



<root>

<priority value="debug"/>

<appender-ref ref="INFO"/>

</root>


</log4j:configuration>


Uncaught-Exceptions in WinForms-Anwendungen

static void Main()
{
// Für nicht abgefangene Exceptions aus dem ApplicationThread einen
// eigenen Exception-Handler definieren

Application.ThreadException += new
System.Threading.ThreadExceptionEventHandler(Application_ThreadException);

try
{
MainFormController controller = new MainFormController();
Application.Run(controller.View);
}

// Exceptions können auch schon währrend new MainFormController()
// auftreten, daher müssen diese auch schon abgefangen werden
catch (Exception ex)
{
ShowExceptionDialog("Main",ex);
}
}


private static void Application_ThreadException(object sender,System.Threading.ThreadExceptionEventArgs e)
{
// Diese Methode wird aufgerufen, wenn eine Exception nicht behandelt wurde
ShowExceptionDialog(sender,e.Exception);
}

ComboBox Items in verschiedenen Farben

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace WindowsApplication7
{
public class Form1 : System.Windows.Forms.Form
{
private System.ComponentModel.Container components = null;
private String[] arr;

private Image[] imageArr;
private System.Windows.Forms.ComboBox comboBox1;
public Font myFont;
public Form1()
{
InitializeComponent();
myFont = new System.Drawing.Font("Comic Sans", 11);
arr = new String[4];
arr[0] = "Smiley Red Face";
arr[1] = "Smiley Cry";
arr[2] = "Smiley Big Grin";
arr[3] = "Smiley Suss";
this.comboBox1.DataSource = arr; //set the combo's data source to out array
imageArr = new Image[4];

imageArr[0] = new Bitmap("C:\\smiley_redface.gif");
imageArr[1] = new Bitmap("C:\\smiley_cry.gif");
imageArr[2] = new Bitmap("C:\\smiley_biggrin.gif");
imageArr[3] = new Bitmap("C:\\smiley_suss.gif");
}
//You have your windows forms designer generated code here
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void comboBox1_DrawItem(object sender,
System.Windows.Forms.DrawItemEventArgs e)
{
// Let's highlight the currently selected item like any well
// behaved combo box should
e.Graphics.FillRectangle(Brushes.Bisque, e.Bounds);
e.Graphics.DrawString(arr[e.Index], myFont, Brushes.Blue,
new Point(imageArr[e.Index].Width*2,e.Bounds.Y));
e.Graphics.DrawImage(imageArr[e.Index], new Point(e.Bounds.X, e.Bounds.Y));
//is the mouse hovering over a combobox item??
if((e.State & DrawItemState.Focus)==0)
{
//this code keeps the last item drawn from having a Bisque background.
e.Graphics.FillRectangle(Brushes.White, e.Bounds);
e.Graphics.DrawString(arr[e.Index], myFont, Brushes.Blue,
new Point(imageArr[e.Index].Width*2,e.Bounds.Y));
e.Graphics.DrawImage(imageArr[e.Index], new Point(e.Bounds.X, e.Bounds.}
}
}
}

Events aus der WindowsMessageQueue direkt behandeln

protected override void WndProc(ref Message m)

{

// Werte aus winuser.h

System.IntPtr SC_MINIMIZE = new IntPtr(0xF020);

System.IntPtr SC_MAXIMIZE = new IntPtr(0xF030);

System.IntPtr SC_RESTORE= new IntPtr(0xF120);

if (m.WParam == SC_MINIMIZE)

{

if (this.WindowState!=FormWindowState.Maximized)

sizeBeforeMin=this.Size;

}

if (m.WParam == SC_MAXIMIZE)

{

sizeBeforeMin=this.Size;

}

if (m.WParam ==SC_RESTORE )

{

System.Diagnostics.Debug.WriteLine("WNDPROC wieder");

}

base.WndProc (ref m);

}

Erste x Zeilen zurückgeben

Appending FETCH FIRST 10 ROWS ONLY to the end of your SQL should do
the trick

Merges-Statement under 8i und 9i

Oracle9i Implementation:
In Oracle9i, the MERGE statement INSERTS and UPDATES the data with a single SQL statement.
MERGE INTO SALES_FACT D
USING SALES_JUL01 S
ON (D.TIME_ID = S.TIME_ID
AND D.STORE_ID = S.STORE_ID
AND D.REGION_ID = S.REGION_ID)
WHEN MATCHED THEN
UPDATE
SET d_parts = d_parts + s_parts,
d_sales_amt = d_sales_amt + s_sales_amt,
d_tax_amt = d_tax_amt + s_tax_amt,
d_discount = d_discount + s_discount
WHEN NOT MATCHED THEN
INSERT (D.TIME_ID ,D.STORE_ID ,D.REGION_ID,
D.PARTS ,D.SALES_AMT ,D.TAX_AMT ,D.DISCOUNT)
VALUES (
S.TIME_ID ,S.STORE_ID ,S.REGION_ID,
S.PARTS ,S.SALES_AMT ,S.TAX_AMT ,S.DISCOUNT);

Oracle8i Implementation:
In Oracle8i, you could choose to implement it as a sequence of DML statements.
UPDATE
(SELECT
S.TIME_ID ,S.STORE_ID ,S.REGION_ID,
S.PARTS s_parts ,S.SALES_AMT s_sales_amt ,S.TAX_AMT s_tax_amt ,S.DISCOUNT s_discount,
D.PARTS d_parts ,D.SALES_AMT d_sales_amt ,D.TAX_AMT d_tax_amt ,D.DISCOUNT d_discount
FROM SALES_JUL01 S, SALES_FACT D
WHERE D.TIME_ID = S.TIME_ID
AND D.STORE_ID = S.STORE_ID
AND D.REGION_ID = S.REGION_ID) JV
SET d_parts = d_parts + s_parts,
d_sales_amt = d_sales_amt + s_sales_amt,
d_tax_amt = d_tax_amt + s_tax_amt,
d_discount = d_discount + s_discount
;
INSERT INTO SALES_FACT (
TIME_ID,STORE_ID ,REGION_ID,
PARTS ,SALES_AMT ,TAX_AMT ,DISCOUNT)
SELECT
S.TIME_ID ,S.STORE_ID ,S.REGION_ID,
S.PARTS ,S.SALES_AMT ,S.TAX_AMT ,S.DISCOUNT
FROM SALES_JUL01 S
WHERE (S.TIME_ID, S.STORE_ID, S.REGION_ID) NOT IN (
SELECT D.TIME_ID, D.STORE_ID, D.REGION_ID
FROM SALES_FACT D
)
;



http://www.oracle.com/technology/products/oracle9i/daily/Aug24.html

Thema: Java-Code als Oracle Stored Procedure

1. Java Code in DB kompilieren:

create or replace and compile java source named helloworldinthedatabase as

public class HelloWorldInTheDatabase{

public static int test(int num) {

int temp;

temp = num + 42;

return temp;

}

}

**************************************************************************************************

2. Proz. oder Funktion erstellen: (Veröffentlichen der Klassen in der DB, Namen der

Java-Prozeduren und Parametertypen auf ihre Gegenstücke in PL/SQL abbilden)

create or replace function start_helloworld (num in number) return number

authid current_user

as language java name 'HelloWorldInTheDatabase.test(int) return int';

**************************************************************************************************

3. Prozedur start_helloword ausführen

SQL Server 2000 startet 1433 Port nicht unter XP SP 2

JDBC kann dadurch nicht zugreifen

Ursache:

Sie verwenden eine Version von Microsoft SQL Server 2000 oder Microsoft SQL Server 2000 Desktop Engine (auch MSDE), die bei Verwendung mit dieser Windows-Version bekannte Sicherheitsrisiken enthält. Der TCP/IP-Netzwerkport und der UDP- Netzwerkport von Microsoft SQL Server 2000, MSDE oder von beiden wurde deaktiviert, um das Sicherheitsrisiko möglicher Virusattacken auf dem Computer zu reduzieren.

Lösung:

Installieren Sie einen Patch oder das letzte Servicepack für Microsoft SQL Server 2000 oder MSDE von http://www.microsoft.com/sql/downloads/default.asp

13. März 2007

JMX mit Tomcat 4.x und JDK 1.4

Stichworte:

JMX, MX4J, J2K, JDK1.4

Problem:

Zur Serverüberwachung des Tomcat soll JMX unter JDK 1.4 verwendet werden.

Ursache:

JDK 1.4 unterstützt von Haus aus JMX noch nicht.

Lösung:

Es gibt ein JMX-Toolkit: MX4J unter http://mx4j.sourceforge.net/ (dort allerdings nur in Version 3, die keine RMI-Adaptor zur Verfügung stellt).

Mit MC4J gibt es einen grafischen Client (http://mc4j.org). In diesem sind noch die alten MX4J-Libs mit dem richtigen Adaptor vorhanden.

Die mx4j-tools-1.1.1.jar muss server\lib kopiert werden.



In der Datei conf\jk2.properties muss folgendes eingetragen werden:

mx.enabled=true

#RMI enablen

mx.jrmpPort=1099

mx.jrmpHost=localhost

#Optional auch den HttpAdaptor anschalten

mx.httpPort=9999

mx.httpHost=localhost


Einen Connect über einen Java-Client geht so:

import java.util.Hashtable;

import javax.naming.Context;

import mx4j.connector.rmi.jrmp.JRMPConnector;

public class JMXTest {

public JMXTest() {

}

static public void main(String[] sarr) {

try {

String jndiName = "jrmp";

JRMPConnector connector = new JRMPConnector();

Hashtable environment = new Hashtable();

environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");

environment.put(Context.PROVIDER_URL, "rmi://localhost:9000");

connector.connect(jndiName, environment);

mx4j.connector.RemoteMBeanServer server = connector.getRemoteMBeanServer();

Integer count = server.getMBeanCount();

System.out.println("MBeans: "+count.intValue());

}

catch (Exception e) {

e.printStackTrace();

}

}

}


Eigen MBean erstellen. Es sollte die mx4j-jmx.jar (befindet sich in der Standard-Distribution in server\lib) in common\lib kopiert werden, damit alle WebApps Zugriff auf die selbe JMX-Implementierung haben.

Zunächst ein Interface erstellen, dieses muss im Tomcat immer die Namensgebung

xxxxMBean

haben.

public interface SystemInfoMBean
{
public long getMaxMemory();

}

Die Implementierung der MBean sieht wie folgt aus:

public class SystemInfo implements SystemInfoMBean {


public SystemInfo() {
MBeanServer server = getServer();

ObjectName name = null;
try {
name = new ObjectName("Application:Name=SystemInfoBean,Type=System");
server.registerMBean(this, name);
} catch (Exception e) {
e.printStackTrace();
}

}


private MBeanServer getServer() {
MBeanServer mbserver = null;

ArrayList mbservers = MBeanServerFactory.findMBeanServer(null);

if (mbservers.size() > 0) {
mbserver = (MBeanServer) mbservers.get(0);
}

if (mbserver != null) {
System.out.println("Found our MBean server");
} else {
mbserver = MBeanServerFactory.createMBeanServer();
}

return mbserver;
}



public long getMaxMemory() {
return Runtime.getRuntime().maxMemory()/1024;
}
}

Java & Konstanten

Stichworte:

Constant, Konstante, static, final,

Problem:

Es wird in einer Klasse eine Konstante (über static final…) deklariert und in einer zweiten Klassen wird diese Konstante verwendet. Bei einer Wertänderung der Konstanten und der anschließenden Neukompilierung der Konstanten-Klasse wird beim Abfragen des Wertes in der zweiten Klasse immer noch der alte Wert angegeben.

Ursache:

Der Compiler substituiert Konstanten in den verwendeten Klassen.

Lösung:

Es müssen alle Klassen neukompiliert werden, die die Konstante verwenden. Der Compiler substituiert dort den Wert.