Pátek, 26. červen 2009

Eclipse: WTP Plugin and Name of Deployment

I have a WTP project in Eclipse called in like myproject_war. When I have deployed it into the JBoss server, then the deployment name was myproject_war.war. I wanted to deploy it as myproject.war only. I could not find any property in the "Properties" dialogue to change the deploument name, so I have searched my filesystem for "myproject_war" string. So, I have discovered the file
.settings/org.eclipse.wst.common.component in the project directory. I have changed the attribute name of the wb-module module there, from
<wb-module name="myproject_war">
to
<wb-module name="myproject">

Now, the project is deployed as myproject.war.

Pátek, 19. červen 2009

Java: Standalone Pattern and Lazy Initialization

Whats wrong with this piece of code?
public class MyClass {
private static MyClass instance = null;
public static MyClass getInstance() {
if (instance == null) instance = new MyClass();
return instance;
}
}

Nothing, if you have single thread application only. Otherwise add a magic word synchronized:
public class MyClass {
private static MyClass instance = null;
public static synchronized MyClass getInstance() {
if (instance == null) instance = new MyClass();
return instance;
}
}

Now, it's more safe, but a little bit slower. We may try to speed it up:
public class MyClass {
private static MyClass instance = null;
public static MyClass getInstance() {
if (instance != null) return instance; = new MyClass();
synchronized (MyClass.class) {
// yes, one more check in synchronized block
if (instance == null) instance = new MyClass();
}
return instance;
}
}

The last piece of code is right if, and only if there is no other method which may set instance = null;. In most cases it is safer to use just plain synchronized method.

Later, I have stumbled upon the web site http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html, which examines the synchronization thoroughly.

Čtvrtek, 21. květen 2009

Java logging.properties in the Classpath

Java utils logging (JUL) has been included in Java since version 1.4. Since then, one does not need to ship an extra JAR (log4j) with an application. It is possible to initialize JUL by a standard properties file logging.properties. I do not like, that

  • the user of the application has to provide a "hard to remember" parameter -Djava.util.logging.config.file=logging.properties to the JRE

  • and some default logging.properties cannot be packed into the JAR of the application.


Compare it with a very flexible initialization of JNDI:
The JNDI automatically reads the application resource files from all components in the applications' classpaths and JAVA_HOME/lib/jndi.properties, ...

I have solved this issues with just a few lines of code. The code has to be invoked before the logging is first used in the application. So, the most suitable place for this code is in the static constructor (initializer) of the class, where is main() method. Then you may pack a default logging.properties file into the JAR with your application (the same JAR where is the class with this code). And the user may still use the -Djava.util.logging.config.file=logging.properties property to use her/his own logging configuration.
/*
*
* Should be called before the first use of the JDK logging. The best place is
* in the static initializer of the class, which has the {@code main()} method.
*/
static {
String file = System.getProperty("java.util.logging.config.file");
if (loggingfile == null || loggingfile.isEmpty()) {
URL url = null;
try {
url = MyClass.class.getClassLoader().getResource("logging.properties");
if (url == null) {
System.err.println("Cannot find logging.properties.");
} else {
LogManager.getLogManager().readConfiguration(url.openStream());
}
} catch (Exception e) {
System.err.println("Error reading logging.properties from '" + url + "': " + e);
}
}
}

public static void main(String[] args) { ... }

If you need something more robust, then look into the Java sources for the aforementioned JNDI initialization :-).

Středa, 25. únor 2009

Ze C# na Javu

Tak jsem konečně přešel ze C# na Javu. I kdy6 se mi C# a Visula Studio líbily, vadil mi především uzavřený a překotný vývoj těchto produktů. Společnost Microsoft bohužel moc neřeší známé bugy ve starších verzích .NET a navíc mám pocit, že se i k novým bugům má pštrosí chování -- když bug nevidíme, tak neexistuje. Navíc některá řešení dodávaná s .NET, jako třeba lokalizace, jsou udělána jen napůl. A kdo s tím má problém, tak ať si přikoupí komerční knihovny. Na tom není nic špatného, kdyby aspoň v dokumentaci byly upozornění na tyto nedostatky. Ale bohužel většinou se člověk jen dočte, jak je všechnu super.

Tak mám práci v Javě a Eclipse, ale stále bohužel pracovní prostředí MS Windows. Až se zapracuji, tak si nainstaluji svůj oblíbený Linux. I když musím uznat, že co se týká uživatelské přívětivosti, je na tom Java hůře nž .NET. Například taková prkotina, jako seznam přechozích hodnot v input boxu mi občas znatelně chybí. A i v Eclipse se najdou chybky. Ale aspoň je to zdarma a open source.

Úterý, 26. srpen 2008

.NET Zobrazní chyby v DataGridView

Při události CellValidating u DataGridView (DGV) je možné uživatele informovat o chybě několika způsoby.
  1. Klasicky, dialogovým okénkem MessageBox.Show(...).
  2. Nastavit chybu řádky dgv.Rows[e.RowIndex].ErrorText = "Chyba" (pozor, pak se tato chyba musí mazat při EndEdit()).
  3. Nastavit chybu buňky dgv.Rows[e.ColumnIndex, e.RowIndex].ErrorText = "Chyba". Ale chybový obrázek se neobjeví, pokud se buňka edituje. Workaround pro toto je třeba v DataGridView FAQ.
  4. Zobrazit ToolTip.
Bod 1. je asi nejjednodušší, ale ne moc uživatelsky přívětivý, protože se dialogové okénko s chybou musí odsouhlasit, aby zmizelo. Body 2. a 3. jsou často uváděné v příkladech nebo internetových diskusích. Sice se zobrazí pěkná červená blikající ikonka, ale uživatel na ní musí najet myší, aby se dověděl, co je špatně. Proto mi připadá nejlepší bod 4. - zobrazení ToolTipu. Uživatel hned vidí, jakou chybu způsobil a může ji ihned napravit. Navíc je to i poměrně jednoduché.


//deklarace
private ToolTip toolTip = new ToolTip();

...

// incializace, treba v konstruktoru
this.toolTip.ToolTipIcon = ToolTipIcon.Error;
this.toolTip.ToolTipTitle = Properties.Resources.TitleError;
this.dataGridView.CellEndEdit += new DataGridViewCellEventHandler(dataGridView_CellEndEdit);
this.dataGridView.CellValidating +=new DataGridViewCellValidatingEventHandler(dataGridView_CellValidating);

...

// Schovej tool tip po dokonceni editace.
void dataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (this.toolTip.Active)
{
this.toolTip.Hide(this);
}
}

private void dataGridView_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
...
if (error)
{
this.CellValidatingError(e, "Nastala chyba XYZ.");
return;
}
...
}

// Zobraz tooltip, pipni, nastav e.Cancel na true.
public void CellValidatingError(DataGridViewCellValidatingEventArgs e, string msg)
{
e.Cancel = true;
// tooltip
Rectangle cellBounds = this.dataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);
Point location = new Point(cellBounds.Left + this.dataGridView.Location.X, cellBounds.Bottom + this.dataGridView.Location.Y);
this.toolTip.Show(msg, this, location, 3000);
// beep
System.Media.SystemSounds.Beep.Play();
}


Poznámka: při ToolTip.Hide() a ToolTip.Show() se nastaví na prvek Control, ve kterém je DataGridView vložené, v tomto příkladě je to this.

Pondělí, 11. srpen 2008

.NET C# Souborové operace s progress barem

K mému nemilému překvapení chybí v .NET operace pro kopírování/přesouvání souborů, ke kterým by šel pohodlně připojit nějaký progress bar s informací pro uživatele, jak dalece operace pokročila. Kopírování souborů lze udělat ručně, nebo se dají najít články o použití CopyFileEx z kernel32.dll v C#. Ovšem pro přesun souborů jsem nic rozumného nenašel. Přesun souboru se totiž někdy realizuje jako přejmenování, jindy jako kopírování a vymazání. To může trvat různě dlouho. Nakonec mne překvapil poměrně čerstvý (asi měsíc starý) článek na MSDN: http://msdn.microsoft.com/en-us/library/cc165446.aspx. Ten radí použít v C# třídu FileSystem z VisualBasicu (sic). Tato třída poskytuje při souborových operacích standartní windowsovská dialogová okna, což je pro mé účely postačující. Navíc si i nechá potvrdit přepsání nebo smazání souboru, umí i operovat s celými adresáři. Jestliže ale někdo potřebuje vlastní progress bar nebo jiný způsob informace o postupu souborové operace, tak má asi smůlu.

Poznámka: Pro zobrazní dialogů je potřeba u metod třídy FileSystem použít parametr Microsoft.VisualBasic.FileIO.UIOption.AllDialogs

Středa, 6. srpen 2008

.NET DataGridView a změna chování při stisku kláves

Na Internetu je posáno mnoho řešení, jak změnit chování DataGridView (DGV) při stisku kláves, např. při Enter přejít na další sloupec místo na další řádku. Bohužel, DGV a ani dokumentace k němu není v tomto ohledu přímočaré, a proto i mnoho lidí (dokonce i z Microsoftu) radí používat EditingControlShowing událost a hlídat KeyDown u objevivšího TextBoxu. To je ovšem takové lámání přes koleno. Nejlepší řešení je od Marka Rideouta, manažera DataGridView v Microsoftu. (Ano, DataGridView má asi svého ředitele :-)), viz http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=88530&SiteID=1

V podstatě stačí vyvtvořit podtřídu DGV a přepsat virtuální funkce:

protected override bool ProcessDialogKey(Keys keyData)
{
Keys keyCode = (keyData & Keys.KeyCode);
if (keyCode == Keys.Enter || keyCode == Keys.Tab)
{
return this.ProcessRightKey(keyData);
}
return base.ProcessDialogKey(keyData);
}

protected override bool ProcessDataGridViewKey(KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Tab)
{
return this.ProcessRightKey(e.KeyData);
}
return base.ProcessDataGridViewKey(e);
}