Berichten met label java
JavaOne Reloaded
Geplaatst door Marcel Offermans in Uncategorized op 5 juni 2009
On tuesday, june 23rd, luminis organizes a JavaOne reloaded event, where all the news and highlights from the 2009 JavaOne are presented. This will take place in the Arnhem offices, starting at 18:00. If you want to be present, please send a mail to Marcel. This year’s JavaOne is special for many reasons. Oracle just bought Sun. Microsoft gives their first keynote. JavaFX comes of age. Larry Ellison hints at a JavaFX frontend for OpenOffice. Enough reasons to be present!
Java .Net interoperability
Geplaatst door Floris Zwarteveen in Uncategorized op 1 juni 2009
Java .Net interoperability
Have you ever felt the urge and need to connect your Java program to a particular .Net assembly? And more specifically without the hassle of COM bridges, web services, .Net remoting or other service type components. I came across a project in which a simple Java program needed to be connected to an existing .Net assembly (btw written in C#). Everything had to be as simple as possible.
On the Internet a couple of articles were helpful: Espresso, C# method calls within Java Program and Calling a .NET Method from Java via JNI. However it didn’t quite work the way we liked so the ulimate solution came (it so often happens) from Jelle Hissink (http://www.codewise.nl)
The solution is based on:
- Use JNI for the communication between the Java VM and the .Net CLR
- Intercept the call to the name resolver and resolve the call to the C# class with your own method
Graphically this looks like:

Let’s work our way from the initial Java call to the final C# implementation.
Java
In Java we have the simple program:
class HelloWorld {
public static void main(String[] args)
{
DotNetBridge dotNetBridge = new DotNetBridge();
String getString = dotNetBridge.GetString();
System.out.println("GetString() returned: " + getString);
String newString = "Java2.Net world";
dotNetBridge.SetString(newString);
getString = dotNetBridge.GetString();
System.out.println("GetString() returned: " + getString);
}
}
Nothing special, just straightforward java code.
However in addition to this code we define an extra class with the DotNetBridge code on the java side. This is needed to kick start the bridging.
public class DotNetBridge
{
private native void Initialize(String localPath);
public native String GetString();
public native void SetString(String newString);
static
{
String path = DotNetBridge.class.getProtectionDomain().getCodeSource().getLocation().getPath();
int idx = path.lastIndexOf("/");
if (idx >= 0) {
path = path.substring(0, idx);
}
if (path.startsWith("/")) {
path = path.substring(1);
}
System.load(path + "/DotNetBridge.dll");
DotNetBridge dotNetBridge = new DotNetBridge();
dotNetBridge.Initialize(path);
}
}
In a few words what does it do? It is a class with native methods telling the java compiler that it can find the interface of the methods in this class but the implementation is to be found in a native Windows dll file. On top of that when to class is instantiated for the first time the static part will ensure that the Initialize method is called in which the full path of the dll file with the implementation is passed. Be a little more patient, later on we’ll see why we need this.
DotNetBridge
We now need a simple interface from the Java to the .Net world in which strings can be passed in and out. For this purpose we create a managed C++ project in Visual Studio of the name DotNetBridge. The project consists of:
- A C++ header file with the definition of the bridge interface
- A C++ file with the implementation of the bridge
- A reference to the .Net component with the implementation of the actual HelloWorld code
The header file DotNetBridge.h makes use of JNI (ofcourse) to define the interface of the bridge:
#include "jni.h" JNIEXPORT void JNICALL Java_DotNetBridge_Initialize(JNIEnv *env, jobject thisobject, jstring localPath); JNIEXPORT jstring JNICALL Java_DotNetBridge_GetString(JNIEnv *env, jobject thisobject); JNIEXPORT void JNICALL Java_DotNetBridge_SetString(JNIEnv *env, jobject thisobject, jstring newString);
I’ll not dive into the details of JNI too much (see JNI Primer) but this is the most minimal definition. There are tools to generate this type of definitions (javah –jni) but we leave those out of the equation today. For now watch the prefix Java_DotNetBridge_ which are needed in case of JNI. Also the env and thisobject parameters are specific for each JNI call.
The implementation in C++ has become much more easily with the new .Net framework primitives for managed C++. The two main methods on the interface are the get and set string methods, their implementation looks like:
using namespace System; using namespace System::Runtime::InteropServices; using namespace HelloWorldDotNet; #includeJNIEXPORT jstring JNICALL Java_DotNetBridge_GetString (JNIEnv *env, jobject thisobject) { IHelloWorld ^support = gcnew HelloWorld(); // Get a managed string. String ^managedString = support->GetString(); std::string str = managedToString(managedString); // Create the java string... jstring result = env->NewStringUTF(str.c_str()); return result; } JNIEXPORT void JNICALL Java_DotNetBridge_SetString (JNIEnv *env, jobject thisobject, jstring newString) { IHelloWorld ^support = gcnew HelloWorld(); std::wstring newWString = jstringToWString(env, newString); String ^ managedString = gcnew String(newWString.c_str()); support->SetString(managedString); }
To enable all this we do need some extra string management functions which can translate the java formatted strings to .Net strings and vice versa. Also the transition from the managed to unmanaged environment has its effect on the string management as can be seen from:
std::wstring jstringToWString(JNIEnv *env, jstring jstr)
{
jboolean iscopy;
const jchar *strPtr = env->GetStringChars(jstr, &iscopy);
const jchar *p = strPtr;
std::wstring result;
while (*p)
{
result += *(p++);
}
env->ReleaseStringChars(jstr, strPtr);
return result;
}
std::string managedToString(String ^mstr)
{
// Marshal the managed string to unmanaged memory.
IntPtr hGlobal = Marshal::StringToHGlobalAnsi(mstr);
char *stringPointer = static_cast(hGlobal.ToPointer());
// Create the java string...
std::string result = stringPointer;
// Always free the unmanaged string.
Marshal::FreeHGlobal(hGlobal);
return result;
}
Finally, as said earlier, we need to kick start the whole mechanism. Hence the managed C++ code has currently no idea where to find an implementation of the HelloWorld class in the call to: IHelloWorld ^support = gcnew HelloWorld();
By the way, the import of the namespace has sneaked into the C++ code quite silently with: using namespace HelloWorldDotNet; Without setting a reference to the .Net assembly in the projects’ properties the compiler will not be able to compile this C++ class. Setting such a reference can be done with:

In the Java implementation we have seen that each instantiation of the java bridge class results in a call to the initialize function of the DotNetBridge component. Finally it is time to dive into the details of its implementation. We could have done without it if we deploy all dlls to the java executable directories. From a administration perspective this is not very neat. System administrators normally require that a solution is delivered to one location and the software should take care of its own path settings.
In essence the initialize function of the DotNetBridge is always called once and exactly once with the full path specification of the actual executable directory. So put all dlls into this directory and intercept the name resolver. Because if the runtime tries to load the assembly with the implementation of the HelloWorld class it should look into one specific dll to find this implementation. So this will happen:
- On the initialize the directory path of the HelloWorld.dll is stored in a static variable
- The AssemblyResolve event is augmented with its own handler
- In the handler the directory path is used to load the HelloWorld class from the correct dll
std::wstring assemblyLoadPath;
System::Reflection::Assembly ^ MyResolveEventHandler( Object^ sender, ResolveEventArgs^ args )
{
String ^name = args->Name;
int idx = name->IndexOf(L',');
if (idx >= 0) {
name = name->Substring(0, idx);
}
name = String::Concat(name, gcnew String(L".dll"));
String ^basePath = gcnew String(assemblyLoadPath.c_str());
name = System::IO::Path::Combine(basePath, name);
System::Reflection::Assembly ^ result = System::Reflection::Assembly::LoadFile(name);
return result;
}
JNIEXPORT void JNICALL Java_DotNetBridge_Initialize (JNIEnv *env, jobject thisobject, jstring localPath)
{
assemblyLoadPath = jstringToWString(env, localPath);
AppDomain ^currentDomain = AppDomain::CurrentDomain;
currentDomain->AssemblyResolve += gcnew ResolveEventHandler( MyResolveEventHandler );
}
This will do the trick on the .Net bridge side. The final implementation of the C# code is straightforward.
C# code
It is a C# class library project. It consists of two classes: the interface and its implementation. The code of the interface definition looks like:
using System;
namespace HelloWorldDotNet
{
public interface IHelloWorld
{
void SetString(string stringValue);
string GetString();
}
}
Finally its implementation can be anything. In this case it is simple as:
using System;
namespace HelloWorldDotNet
{
public class HelloWorld : IHelloWorld
{
private static string lastString = "brave new world";
public void SetString(string stringValue)
{
lastString = stringValue;
}
public string GetString()
{
return String.Format("Hello {0}", lastString);
}
}
}
Finally
On purpose, in the code examples details such as exception handling and comments were left out. If you’re interested you can download all the code (see below). On the java side DOS command scripts are used to compile and run the example. You’ll find them easy and straightforward. The C++ and C# code are organized in two separate .Net projects. They were made with Visual Studio 2008. Either migrate to Studio 2008 or use the source code files to build a solution yourself.
If you can’t find your way through all the source files, this is the rough organization:

These are the main steps, with their correct order, to get it to work:
- Compile the HelloWorld C# project
- From the DotNetBridge project set the reference to the HelloWorld.dll created in the previous step
- (possibly) in the DotNetBridge project set the include directories to include the path to the correct JVM include directories (otherwise the jni.h can not be found)
- Compile the DotNetBridge project
- Change the compile.cmd and run.cmd scripts in the Java directory to reflect the correct paths.
- Run compile.cmd
- Run run.cmd
If unchanged you should see something like:

When you want a full download of all the code in this sample, click here.
And last but not least: have fun!
Developing a Confluence Plugin
Geplaatst door admin in Uncategorized op 27 mei 2009
Introduction
Does your company use Confluence? If you’re looking to extend its possibilities, it may not seem obvious straight away which of the 30+ plugin module types to choose.
Say you want to add a form to Confluence in order to submit (and process) some data. Once the processing is done, the results should be displayed, if possible on the same page. If you’re interested in how to accomplish this, you probably want to keep on reading; it seems easy to create such a plugin, but there’s a few issues that you’re likely to run into.
In this blog I’ll describe the more troublesome/unexpected ones that we encountered and will explain how they were handled (or sometimes: worked around).
Plugin Module Types
First of all, let’s pick the plugin module types to use. In order to embed (HTML) content in a page, the plugin type to use is the Macro Plugin. This is not sufficient, though; for something to happen, the macro needs to display some form elements (e.g. text fields, select fields and a submit button) and when the form is submitted, the right action should take place. For this to happen, we used an XWork Action; the form data is sent to and handled by the Action.
First of all, you need to get a simple Macro Plugin up-and running. Besides creating an Archetype and some plugin-specific steps, most of the steps required to do this can be found here.
Following that, you need to setup an XWork Action (check the ‘XWork Action’ link above to find out more). Once that is done, create a velocity macro (.vm file) containing a form that submits its data to the Action:
<form name="myForm" action="/confluence/plugins/actions/myaction.action" method="GET">
$action.inputText: <input type="text" name="myInput">
<p>
<input type="submit" value="Submit">
</form>
Note that he action to be used (on a ’submit’ of the form) should be defined as the XWork Action ‘myaction’ in atlassian-plugin.xml.
After that, display the form by embedding (all text and controls defined in) the .vm file into the macro by updating its execute(…) method (used when the macro is rendered):
public String execute(Map params, String body, RenderContext renderContext) throws MacroException {
Map context = MacroUtils.defaultVelocityContext();
context.put("action", new MyAction());
return VelocityUtils.getRenderedTemplate("templates/myaction.vm", context);
}
Note that the action that will handle the submitted form data is supplied through a “velocity context” map. Supplying it is only required if the action is queried when rendering the velocity macro (i.e. if you’re using elements in the .vm file that start with $action. – specifically, in this case: $action.inputText).
Also, it’s important to use only HTML body elements in the .vm file; a complete HTML syntax will result in anything on a page following the {macro} tag no longer being displayed! After this is done, it is possible to display / fill in the form (and submitting it to the XWork Action) through rendering the Macro that is wrapped around it.
Return from whence we came
As previously mentioned, we would like to ’stay’ in the same page. In order to achieve this, Confluence offers the possibility of a (server) redirect. Now, all we need to do is figure out from whence we came, since the macro can be embedded pretty much anywhere! Unfortunately, simply checking the request URL does not work (because of Confluence (pre)processing); a generic URL is returned (ending with /pages/viewpage.action). We solved it using a bit of a ’scrippety-trickery’ solution! Since we can’t find out the URL on the server-side, in the form that was submitted, we included a hidden element:
<input type="hidden" name="macroUrl">
and set it with the current URL by including a bit of JavaScript in the .vm file as well:
<script language="JavaScript"> document.myForm.macroUrl.value = window.location </script>
Don’t forget to add the matching getter and setter in the action that the velocity macro (form) posts to (in this case ‘MyAction’)! Once this is done, you can retrieve the Action value in the xwork/myaction part of the atlassian-plugin.xml configuration file (note that this is runtime info and not maven setting some properties!):
<action name="myaction" class="com.luminis.confluence.plugin.MyAction">
<result name="success" type="redirect">${macroUrl}</result>
</action>
Persistent data
In order to store userspecific data, Confluence uses a table in which key-value combinations can be stored: the BANDANA table. Writing to and reading from this table is done through the BandanaManager.
To get hold of the correct manager objects, all you need to do is declare a variable and a setter for the variable and Spring will auto-inject the right managers:
BandanaManager m_bandanaManager;
public void setBandanaManager(BandanaManager bandanaManager) {
m_bandanaManager = bandanaManager;
}
If, at some point, you can’t wait for Spring to inject them, you can also look them up manually, e.g. for the PlatformTransactionManager:
PlatformTransactionManager m_transactionManager;
// ...
Object o = ContainerManager.getComponent("transactionManager");
if (o instanceof PlatformTransactionManager) {
m_transactionManager = (PlatformTransactionManager)o;
}
Finally, the BandanaManager uses a ‘context’ to get and set values:
BandanaContext m_bandanaContext;
// ...
String rootKey = getClass().getPackage().getName();
m_bandanaContext = new ConfluenceBandanaContext(rootKey);
As soon as the BandanaManager, the PlatformTransactionManager and the ConfluenceBandanaContext are available, it’s possible to store data into the BANDANA table:
public void storeKeyValue(String key, Object val) {
storeDataWithinTransaction(key, val);
// Stopping and starting Confluence here is no problem.
Object o = m_bandanaManager.getValue(m_bandanaContext, key);
// o.equals(val) == true // assuming properly implemented 'equals()'
}
// Data should be stored within a transaction; use a Spring TransactionTemplate to wrap a transaction around the operation:
public void storeDataWithinTransaction(final String key, final Object val) {
TransactionTemplate tt = new TransactionTemplate(m_transactionManager);
TransactionCallback callback = new TransactionCallback() {
public Object doInTransaction(TransactionStatus transactionStatus) {
m_bandanaManager.setValue(m_bandanaContext, key, val);
return null;
}
};
tt.execute(callback);
}
Persistent ‘proprietary’ data
In case you want to define your own objects and store them as well, this will fail when using the BandanaManager, since it can’t find the class. Note that this is a tricky failure because after restarting Confluence, reading the value fails without any logging at all! Storing (new) values after that, however, does work. To make sure this does succeed, use XStream to convert the object to a(n XML) String before storing it:
XStream m_xStream = new XStream();
{
m_xStream.setClassLoader(getClass().getClassLoader());
m_xStream.alias("my-class", MyClass.class);
}
public void storeMyClass(MyClass mc) {
String xmlStr = m_xStream.toXML(mc);
storeKeyValue("specifickey", xmlStr);
}
And convert it back from XML after reading it:
public MyClass readMyClass() {
String s = (String)m_bandanaManager.getValue(m_bandanaContext, "specifickey");
return (MyClass)m_xStream.fromXML(s);
}
LWUIT – een lichtgewicht ui toolkit voor Java ME
Geplaatst door admin in Uncategorized op 17 april 2009
Introductie
Ooit al eens een Java ME applicatie ontwikkeld voor een specifiek type mobiele telefoon? Wanneer het aankomt op het ontwikkelen van een gelikte interface loop je er al snel tegen aan dat de MIDP implementatie van de telefoon van invloed is op hoe de widgets van de applicatie getoond worden. Nadeel daarvan is dat de UI er op een ander type mobiel nogal anders uit kan zien. Om die reden was men voorheen nog wel eens geneigd om wat dieper in de code te duiken en de wat specifiekere (geliktere) widgets op de (javax.microedition.lcdui.)Canvas uit te werken. Deze Canvas ondersteunt sinds MIDP-2.0 een fullscreen modus: het is mogelijk om de complete applicatie direct op Canvas te ontwikkelen. Hiervan maakt LWUIT gebruik.
Mogelijk, maar wel bijzonder tijds-intensief, tenzij je gebruik maakt van het werk van anderen die al wat voorwerk hebben verricht op dit gebied. Een mooi voorbeeld hiervan is “LWUIT”, een door Sun ontwikkelde open-source UI toolkit. In tegenstelling tot het ontwerpen van UI’s met standaard Java ME is het met LWUIT mogelijk om op een Swing-achtige manier user interfaces te maken: layout managers, maar ook de pluggable look & feel, het MVC concept en “lightweight componenten” (componenten maken geen gebruik van de native UI controls, maar verzorgen zelf hun rendering en event handling) zijn terug te vinden.
Setup simpele MIDlet
Een MIDlet maken waarin met LWUIT een paar knoppen wordt getoond is simpel voor elkaar te krijgen via de volgende stappen:
Binnen NetBeans is het handig om de LWUIT jar toe te voegen als library via ‘Tools’, ‘Libraries en dan de ‘New Library…’ knop. Geef naam, type = ‘Class Libraries’, dan: ‘OK’ en ‘Add JAR/Folder…’: en selecteer het bestand “LWUIT.jar” dat te vinden is in de ‘lib’ dir van de LWUIT installatie (unzippen van de zip file). Het ‘LWUIT_stripped.jar’ bestand is de LWUIT library zonder code die gebruik maakt van JSR 184 (M3G: Mobile 3D Graphics), JSR 75 (PIM: Personal Information Management, FC: File Connection) of JSR 226 (SVG: Scalable 2D Vector Graphics).
Start vervolgens een nieuw project, kies als categorie ‘Java ME’ en als project ‘Mobile Application’ en druk op ‘Next’. Het is niet nodig om NetBeans een default MIDlet aan te laten maken; het vinkje bij ‘Create Hello MIDlet’ mag weg. Druk weer op ‘Next’. Selecteer de WTK die je hebt geïnstalleerd als ‘Emulator Platform’. LWUIT vereist minimaal MIDP-2.0 bovenop CLDC-1.1, dus i.p.v. MIDP-2.1 die default geselecteerd is zou je MIDP-2.0 kunnen kiezen, maar dit hoeft niet. Druk op ‘Finish’. Maak nu een nieuwe MIDlet aan met de naam ‘MyMIDlet’ en kopieer de volgende code er naartoe:
import com.sun.lwuit.*;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.layouts.BoxLayout;
import javax.microedition.midlet.MIDlet;
public class MyMIDlet extends MIDlet {
Form form1;
public void startApp() {
Display.init(this);
form1 = new Form("LWUIT Demo Form");
form1.addCommand(new Command("Quit") {
public void actionPerformed(ActionEvent ae) {
destroyApp(true);
notifyDestroyed();
}
});
form1.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
Button l = new Button("Left");
l.setAlignment(Label.LEFT);
Button r = new Button("Right");
r.setAlignment(Label.RIGHT);
Button m = new Button("Middle");
m.setAlignment(Label.CENTER);
form1.addComponent(l);
form1.addComponent(r);
form1.addComponent(m);
form1.show();
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
}
Omdat het project de LWUIT jar nog niet kent zal je een aantal foutmeldingen zien: via de project properties kan je onder de ‘Build’ categorie ‘Libraries & Resources’ kiezen. Voeg de LWUIT library die je eerder hebt aangemaakt toe. Omdat er slechts 1 MIDlet in het project zit zal deze MIDlet automatisch gestart worden wanneer je het project right-clickt en dan ‘Run’ selecteert. Het resultaat hiervan is een simpele LWUIT applicatie met een paar knopjes!
MVC
Een aardige illustratie van het MVC concept bij LWUIT is de List. Voeg het volgende stuk code in voor de ‘form1.show()’ uit het vorige voorbeeld.
String[] bands = new String[]{
"AC/DShe",
"Public Enema",
"Beverly Beerbellies",
"Shirley Temple Pilots",
"Wynona Riders"};
List list = new List(bands);
form1.addComponent(list);
.
Resultaat
Zelf de implementatie van ListModel en ListCellRenderer verzorgen heeft als extra voordeel dat je de reeds bestaande data types direct kan gebruiken en er geen sprake hoeft te zijn van “state copying”: er is geen voorbepaald data type wat in de List past.
Ook goed om te weten is dat de List z’n Model alleen maar vraagt naar de data-elementen die op het scherm zichtbaar zijn. Dit (performance) aspect wordt belangrijker naarmate de hoeveelheid data groeit of bijv. vanaf een remote locatie moet worden binnengehaald.
Conclusie
Via de bovenstaande stappen is het relatief makkelijk om snel een simpele LWUIT applicatie te maken. Met name voor de ontwikkelaars die hun app op meerdere typen mobiele telefoons willen draaien zal het een aangename verrassing zijn om te zien dat deze voorbeeld-applicatie op alle mobieltjes (die MIDP-2.0/CLDC-1.1 supporten) in uiterlijk en gedrag hetzelfde zal zijn. In een volgende blog zal ik wat verder ingaan op “fancier features” zoals Transitions en Theming; het oog wil immers ook wat!
Enterprise Javabeans 3 in OSGi — part 2
Geplaatst door admin in Uncategorized op 29 januari 2009
This blog picks up on some unfinished items mentioned in the first blog. More specifically, this blog will focus on:
- auto-injecting session beans;
- entity-only bundles;
- transaction rollback.
Note that if the reader wants to run the example source, he or she is referred to the aformentioned previous blog in which more information on setting up the environment and getting the sources to compile/run can be found. To get the samples running, you always need to start ezb-command.jar and entitybeans.jar. Besides those 2 bundles, you either need to start combinedbean.jar (includes 2 stateless beans) or statelessbean1.jar and statelessbean2.jar (both bundles contain 1 stateless bean).
Auto-injecting session beans
In EJB3 (and therefore also in EZB) it is possible to inject a reference to a session bean through using the ‘@EJB’ annotation. When using this annotation, you can supply the product-specific name of the EJB to which it should be mapped (could be any kind of name – even though it is often a JNDI one). As it turns out, EZB almost completely supports this feature; both when the EJB to be injected is defined within the same bundle (use combinedbean.jar) and when it is defined in a different bundle (use statelessbean1.jar and statelessbean2.jar) the reference is injected properly and no lookup takes place:
@EJB(mappedName="SL2")
private Stateless2Remote m_stateless2 = null;
@EJB
private Stateless2Remote m_stateless2NotAutoInjected = null;
// ... and some code using it:
if (m_stateless2 == null) {
// '@EJB' injection did not take place, performing lookup
try {
m_stateless2 = (Stateless2Remote)new InitialContext().lookup(
String.format("%s_%s@Remote", "net.luminis.ezb.stateless2.impl.Stateless2Impl", Stateless2Remote.class.getName()));
} catch (NamingException e) {
e.printStackTrace(); // lookup failed.
}
}
Note though, that the mappedName must be set both here and in the implementation of the session bean to be injected. If this is not done, EZB cannot find/inject it. I.e. in the code above, at the point where m_stateless2 has been set, the member variable m_stateless2NotAutoInjected is still null.
Entity-only bundles
When deploying ones bundles you’re likely to combine related functionality. It’s possible to simply put everything in a single bundle, but not desirable; the more fine-grained your design, the easier it is to re-use a specific bit of functionality. When you decide to define an entity-only bundle, EZB requires that you include the line
net.luminis.ezb.entitybeans.Kangaroo
both in persistence.xml of the entity-bundle, but also in all bundles using the entity-bundle. This inconvenience may be corrected, but for now, it’s not a very flexible way of working; when you add an entity bean to your entity-bundle, you not only have to update the persistence.xml in that bundle, but also in all bundles using it!
Rollback
Finally, the source code attached to this blog also includes an example of how EZB handles a transaction rollback. It is possible to trigger such a rollback by using:
m_sessionContext.setRollbackOnly();
to signal the session to perform a rollback of every action associated with the current transaction. In this example the functionality is illustrated through using the command
ezb use_injected_max
which not only uses an injected session bean to create a Kangaroo entity, but also triggers a rollback if 4 Kangaroos have already been created. (Check the methods addKangaroo() and addKangarooWithMaximumCheck() in Stateless2Impl to find out more.
Enterprise Javabeans 3 in OSGI
Geplaatst door Peter Doornbosch in Uncategorized op 19 december 2008
Previous blogs (look here and here) have shown that it’s fairly easy to use (open)JPA within OSGi. Moving on on this path of attempting persistence solutions for OSGi led to an encounter with Easybeans (EZB from now on), an Enterprise Javabeans 3 (EJB3) container for OSGi.
This blog assumes that the felix 1.4.0 environment is used. All source code used and all relevant bundles can be found in this zip.
Some of the reasons EJB3 can be of interest when moving beyond what JPA has to offer:
- container managed transactions;
- dependency injection.
For more detailed information on the advantages of EJB3, refer to this article on javaworld
Using EZB within an OSGi context may be of interest to you if you’re working with an:
- EJB3 environment and you’re thinking about migrating towards OSGi;
- OSGi environment, but you don’t have a satisfactory solution for handling persistency issues.
In this blog we will use a simple example that uses some basic EJB3 functionality; we’ll use a command line client that connects to a stateless session bean. The session bean is able to interact with an entity bean called Wombat and is able to create, update and delete (Wombat) entities. We’ll walk through the specifics of each step below.
In order to execute a command, the client first has to connect to (i.e. obtain a reference to) the stateless session bean (EzbCommand.java):
private static final String STATELESS_REMOTE = "net.luminis.ezb.example1.stateless.StatelessRemote";
public void execute(String commandLine, PrintStream out, PrintStream err) {
ServiceReference ref = m_bundleContext.getServiceReference(STATELESS_REMOTE);
m_stateless = (StatelessRemote)m_bundleContext.getService(ref);
EZB registers each stateless session bean as an OSGi service when the bundle that contains the service (and using the org.ow2.easybeans.osgi.ejbjar.Activator) is started. Because of this, it is possible to use the service without EJB3/EZB specific code in the client or the service interface, even though you can still use JNDI to lookup the service. When trying this, be warned that there’s a class loader issue. When dealing with stateful beans, this mechanism does not work and a JNDI lookup must always be used.
Following that, the session bean receives a command from the client, e.g. a ‘create’ command. StatelessImpl.java then picks up the command and creates a new Wombat object with the supplied values. Once that is done, it uses the EntityManager injected into the implementation by EZB (because of the @PersistenceContext annotation – default EJB3 behaviour) to persist the newly created Wombat entity:
@PersistenceContext
private EntityManager m_entityManager = null;
public void createWombat(int id, String name) {
Wombat entity = new Wombat();
entity.setId(id);
entity.setName(name);
m_entityManager.persist(entity);
System.out.println("Created entity bean: " + findWombat(id));
The entity bean used (defined in Wombat.java) contains three EJB3 annotations, denoting it as an entity bean (’@Entity’), indicating which table to create/use for the entity (’@Table’) and indicating which of the bean’s attributes can be used as its primary key (’@Id’):
@Entity
@Table(name = "WOMBATS")
public class Wombat implements Serializable {
// [cut some code]
@Id
public int getId() {
return m_id;
}
Update the Felix configuration
Once again, please note that a felix 1.4.x environment should be used. For more information on previous releases, refer to the README in the zip file. In order to start the EZB bundles, a few changes need to be made in the configuration.
A pre-configured conf/config.properties file can be downloaded from here.
Of course, you can also apply the required changes yourself; an overview of the changes that need to be applied to the conf/config.properties file will follow.
Note that you’ll need the file config.properties of a previous release to copy two specific configuration entries: ‘org.osgi.framework.system.packages’ and ‘jre-1.x’ (depending on the JRE used, this is a list of packages that are exported by default).
Now, edit the config.properties of your newly installed Felix configuration and:
- Uncomment the ‘org.osgi.framework.system.packages’ entry and set the value to the one copied from a previous release.
- Add the complete ‘jre-1.x’ entry to (the bottom of) the file.
The ‘org.osgi.framework.system.packages’ entry should now look something like:
org.osgi.framework.system.packages=org.osgi.framework; version=1.4.0, \
org.osgi.service.packageadmin; version=1.2.0, \
org.osgi.service.startlevel; version=1.1.0, \
org.osgi.service.url; version=1.0.0, \
org.osgi.util.tracker; version=1.3.3 \
${jre-${java.specification.version}}
Note that the last line (with the ${jre-${java.specification.version}} construct) includes all items that are set in the copied ‘jre-1.x’ configuration and that this means that you must have at least 1 section in your config file that starts like:
jre-1.5=, \ javax.accessibility; \
Also, add the sun.rmi packages to the config.properties file, which can now be done more cleanly through:
org.osgi.framework.system.packages.extra= \ sun.rmi.server; \ sun.rmi.transport; \ sun.rmi.registry;
Finally, make sure Felix only logs a warning if it encounters unsupported fragment behavior through uncommenting the line with the felix.fragment.validation option:
felix.fragment.validation=warning
Download the EZB bundles
The required easybean jars can be downloaded from here. This blog is based on usage of the ‘1.1.0-M1′ release, select that release (or possibly a newer release) and then download the package available under “OSGi bundles of EasyBeans”. Unzip the file and note the location of the ‘bundles’ folder.
Install the EZB bundles
Note that this this step should be performed using a felix-1.0.x configuration or a felix-1.4.x configuration only!
In order to install the bundles, start Felix (make sure you’re using the updated configuration!) and execute the following commands in Felix:
-> cd file:/<absolute_path_to_bundle_folder>/ -> start org.apache.felix.configadmin-1.0.4.jar -> start org.apache.felix.dependencymanager-1.1.0-2008.07.10.jar -> start org.apache.felix.log-0.9.0-incubator-2007.12.14.jar -> start org.apache.felix.scr-1.0.6.jar -> start ow2-bundles-externals-commons-logging-1.0.8.jar -> start ow2-bundles-externals-commons-modeler-1.0.8.jar -> start ow2-util-event-api-1.0.8.jar -> install ow2-util-event-impl-1.0.8.jar -> start ow2-util-jmx-api-1.0.8.jar -> install ow2-util-jmx-impl-1.0.8.jar -> start slf4j-api-1.5.2.jar -> start slf4j-jcl-1.5.2.jar -> install easybeans-core-1.1.0-M1.jar -> install easybeans-component-carol-1.1.0-M1.jar -> install easybeans-component-jdbcpool-1.1.0-M1.jar -> install easybeans-component-joram-1.1.0-M1.jar -> install easybeans-component-jotm-1.1.0-M1.jar -> install easybeans-component-quartz-1.1.0-M1.jar -> install easybeans-component-hsqldb-1.1.0-M1.jar -> install easybeans-agent-1.1.0-M1.jar
Once all these bundles are Active or Installed, start the easybeans-agent bundle. Once that bundle is Active, all bundles should be. Now you can start the ezb-command.jar and the statelessbean.jar bundles (same command structure as above). If you want to build the bundles yourself, copy the file easybeans-core-1.1.0-M1.jar from the ezb bundles dir and use ant to create the bundles:
cd <example1-location> cp <ezb-bundledir-location>/easybeans-core-1.1.0-M1.jar lib ant clean bundle.all
Once that is done, you can use the jars that can be found in the output dir.
An interesting point to note for this section is the use of the ‘easybeans-agent’ bundle. This bundle is used to stop and start the ‘ow2-util-event-impl’, the ‘ow2-util-jmx-impl’, the ‘easybeans-core’ and all ‘easybeans-component’ bundles in the right order (also refer to ‘issues encountered’ section below).
Issues encountered
- When a bundle defines an entity bean in an exported package, it must import the package ‘javassist.util.proxy’ with ‘optional’ resolution;
- The fact that EZB uses the easybeans-agent instead of using dependencies was often a source of confusion, if not errors: it felt like the startup order could not always be reproduced and only by stopping the agent, shutting down the framework and restarting both, could problems be resolved;
- Running a client from the outside (not deployed in osgi / running in another JVM) that attempts to lookup (JNDI) a session bean was a real bother (and is therefore not included in this blog);
- Injecting a session bean service that is not defined in the same bundle is not (yet) possible;
- When using entity beans defined in a separate bundle, it is required to include the entity class name to be used both in the entity bundle itself and the bundle using it;
Maybe a follow-up of this blog will elaborate on the last two issues when moving on from using a single bundle to using multiple bundles.
Conclusion
All in all, the ability to use EJB3 related features from within OSGi is definitely a good thing, even though there’s a couple of usability issues to take into account still. Keeping in mind that the product is still in development and that the support is very good (when posting a question in the ow2 mailing, a response will usually follow within a day, if not within the hour), the EZB container is definitely one to keep an eye on!
Flex vs Java Deel 2: Effecten
Geplaatst door admin in Uncategorized op 27 oktober 2008
Inleiding
Dat zowel met Flex als Java i.c.m Swing relatief makkelijk een simpel formuliertje te maken is, geloof ik wel. Ik wil me richten op geavanceerdere features die beide platforms te bieden hebben. Er valt hierbij te denken aan die sexy overgangs-effecten met transparantie, glijdende componenten, enzovoort.
In dit artikel wordt een uiteenzetting gegeven wat beide platforms bieden aan voorzieningen om een GUI echt sexy te maken.
Ten slotte geef ik een head-to-head vergelijking, waarbij identieke schermen en efffecten worden geïmplementeerd.
Effecten in Flex
Gezien het feit dat Flex uit de Flash-stal komt, zal het niemand verbazen dat ook Flex een rijk scala aan kant-en-klare visuele effecten biedt. Als we een blik in de mx.effects package werpen zien we onder andere: blur, fade, glow, rotate, diverse masking effecten, etc…
Net zoals alle Flex elementen, kunnen effecten imperatief dan wel declaratief gebruikt worden.
Voorbeeld van imperatief gebruik:
private var currentResize:Resize;
private function getWider():void {
if (currentResize != null)
currentResize.end();
currentResize = new Resize(button);
currentResize.widthBy = 100;
currentResize.heightBy = 100;
currentResize.play();
}
private function getNarrower():void {
if (currentResize != null)
currentResize.end();
currentResize = new Resize(button);
currentResize.widthBy = -100;
currentResize.heightBy = -100;
currentResize.play();
}
<mx:Button id="button" label="Click me!" mouseDown="getWider()" mouseUp="getNarrower()" />
En dezelfde functionaliteit, maar dan declaratief:
<mx:Resize id="wide" widthBy="100" heightBy="100" /> <mx:Resize id="narrow" widthBy="-100" heightBy="100" /> <mx:Button label="Click me" mouseDownEffect="wide" mouseUpEffect="narrow" />
Een leuk aspect van Flex effecten is dat ze ook parallel kunnen lopen. Neem het volgende voorbeeld:
<mx:Parallel id="ef" target="{button}" duration="3000">
<mx:children>
<mx:Blur blurXTo="0" blurYTo="0"/>
<mx:Rotate angleFrom="0" angleTo="360" />
</mx:children>
</mx:Parallel>
<mx:Button id="button" label="Click me!" click="ef.end();ef.play()" />
Sequentiële afhandleing, d.w.z. achter elkaar, van effecten is ook mogelijk:
<mx:Sequence id="ef" target="{button}" duration="3000">
<mx:children>
<mx:Blur blurXTo="0" blurYTo="0"/>
<mx:Rotate angleFrom="0" angleTo="360" />
</mx:children>
</mx:Sequence>
<mx:Button id="button" label="Click me!" click="ef.end();ef.play()" />
De tijdsduur dat een effect actief is kan ook gezet worden en wel met de duration property. Standaard duurt een effect 1 seconde (1000 milliseeconden). Een voorbeeld:
<mx:Rotate id="ef" angleFrom="0" angleTo="360" duration="3000"/> <mx:Button id="button" label="Click me!" click="ef.end();ef.play()" />
Het laatste aspect dat ik hier ga behandelen ten aanzien van Flex effecten zijn de zogenaamde easing functions. Dit zijn functies die het verloop van de animatie bepalen, bijvoorbeeld of er een versnelling aan het begin en vertraging aan het einde van de animatie plaatsvindt. Het effect krijgt hierdoor een natuurlijker gevoel. In de package mx.effects.easing zijn diverse classes met easing functies aanwezig, maar er zelf een schrijven kan natuurlijk ook.
<mx:Script>
<![CDATA[
import mx.effects.easing.Cubic;
import mx.effects.easing.Linear;
]]>
</mx:Script>
<mx:Rotate id="efNoEasing" target="{button1}" angleFrom="0" angleTo="360" duration="3000" easingFunction="{Linear.easeNone}"/>
<mx:Rotate id="efWithEasing" target="{button2}" angleFrom="0" angleTo="360" duration="3000" easingFunction="{Cubic.easeInOut}" />
<mx:VBox paddingLeft="40" paddingTop="40">
<mx:Button id="button1" label="Click me (no easing)!" click="efNoEasing.end();efNoEasing.play()" />
<mx:Button id="button2" label="Click me (with easing)!" click="efWithEasing.end();efWithEasing.play()" />
</mx:VBox>
Effecten in Java
Componenteffeten in Java zijn niet zo rechttoe rechtaan als in Flex. De voor de hand liggende reden hiervoor is dat de API’s van standaard componenten hiertoe geen mogelijkheden bieden. Dit wil niet zeggen dat het niet mogelijk is. Als je bijvoorbeeld kijkt naar de Java2D demo (https://jdk6.dev.java.net/Java2DApplet.html) zie je dat Java wel degelijk de mogelijkheid biedt om funky dingetjes te doen.
Hoe zit dit nou?
Java biedt een zeer uitgebreide, maar ook zeer complexe API voor 2-dimesionale beeldbewerking. Het gros hiervan is verspreid over de java.awt, java.awt.geom en java.awt.image packages. Deze API’s bieden de developer de mogelijkheid om complexe figuren en krommingen te tekenen, clips te definiëren, afbeeldings en overige graphics transformaties te maken, enz..
De uitdaging zit ‘m echter in de manier waarop deze effecten toegepast worden. Alle visuele component klassen hebben een gemeenschappelijke ouder: java.awt.Component. Iedere keer dat de AWT thread het nodig acht dat een component zichzelf opnieuw tekent, roept het de public void paint(Graphics) methode van dat component aan. Deze methode is vervolgens verantwoordelijk voor het tekenen van de staat van het component. Het Graphics object biedt de mogelijkheid om een heel scala aan graphische operaties uit te voeren, zoals tekenen van stukken tekst en basale vormen, en het zetten van kleuren.
In versie 1.2 van Java is de java.awt.Graphics2D klasse geïntroduceerd, die een extensie is van Graphics, en geavanceerdere functionaliteit biedt zoals vorm- en coördinatentransformaties, geometrie, transparantie en kleurenbeheer.
Een aspect aan effecten wat hier nog niet aan bod is gekomen, is die van tijd. Een effect is immers een animatie, maar de paint methode is verantwoordelijk voor het tekenen van een component op een specifiek moment in de tijd. Het gebruik van threads is hier dus noodzakelijk.
Willen we nou een arbitrair lijkend effect op een component toepassen, zullen we dus zijn paint(Graphics) methode moeten overriden en met behulp van een Thread in de loop van de animatie eigenschappen van het component wijzigen.
Laten we als voorbeeld een nemen een om as draaiende JLabel.
We beginnen met het schrijven van een JLabel subclass die te roteren is:
class RotatableLabel extends JLabel {
private double rotation;
public RotatableLabel(String s) {
super(s);
}
public double getRotation() {
return rotation;
}
public void setRotation(double r) {
this.rotation = r;
repaint();
}
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
return new Dimension(Math.max(d.width, d.height), Math.max(d.width,
d.height));
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
AffineTransform tr = g2.getTransform();
tr.rotate(this.rotation, getWidth() / 2.0, getHeight() / 2.0);
g2.setTransform(tr);
super.paint(g);
}
}
In de paint methode wordt een zogenaamde rotate transform toegepast.
Vervolgens maken we een java.lang.Runnable waarin een instantie van RotatableLabel wordt geanimeerd:
class RotationAnimation implements Runnable {
RotatableLabel m_labelToRotate;
RotationAnimation(RotatableLabel labelToRotate) {
m_labelToRotate = labelToRotate;
}
public void run() {
double origRotation = m_labelToRotate.getRotation();
int steps = 100;
double step = Math.PI * 2 / 100;
double curRotation = origRotation;
for (int i = 0; i < steps; i++) {
double newRotation = curRotation + step;
if (newRotation > (Math.PI * 2)) {
newRotation = newRotation - (Math.PI * 2);
}
m_labelToRotate.setRotation(newRotation);
curRotation = newRotation;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
if (m_labelToRotate.getRotation() != origRotation) {
m_labelToRotate.setRotation(origRotation);
}
}
}
Bij het bovenstaande voorbeeld moet opgemerkt worden dat de grootte van het geanimeerde label is aangepast (zie getPreferredSize()) om de animatie te accomoderen.
codebase="." archive="/roller/luminis/resource/flexvsjava/javavsflex.jar" width="200" height="200">
Een mooi alternatief voor het zelf ontwikkelen van effectenframework is de open source library AnimatedTransitions. Deze biedt een vijftal standaard effecten, en de mogelijkheid er zelf nog meerdere aan toe te voegen. Het centrale concept in deze library is een zogenaamde screen transition: effecten worden alleen toegepast indien er op het scherm noemenswaardige wijziging ten aanzien van de zichtbare componenten plaatsvindt.
Een klein voorbeeldje:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.jdesktop.animation.transitions.ComponentState;
import org.jdesktop.animation.transitions.Effect;
import org.jdesktop.animation.transitions.EffectsManager;
import org.jdesktop.animation.transitions.ScreenTransition;
import org.jdesktop.animation.transitions.TransitionTarget;
import org.jdesktop.animation.transitions.EffectsManager.TransitionType;
import org.jdesktop.animation.transitions.effects.Move;
public class TransitionExample extends JPanel {
boolean state;
public TransitionExample() {
setLayout(null);
final JButton b = new JButton("Move me!");
b.setBounds(50, 50, 150, 30);
final JButton b2 = new JButton("Move me back!");
b2.setBounds(100, 100, 150, 30);
add(b);
Effect effect = new Move(new ComponentState(b), new ComponentState(b2));
EffectsManager.setEffect(b, effect, TransitionType.CHANGING);
final ScreenTransition st = new ScreenTransition(this, new TransitionTarget() {
public void setupNextScreen() {
removeAll();
if (state) {
add(b2);
} else {
add(b);
}
}
}, 1000);
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
st.start();
state = !state;
}
};
b.addActionListener(listener);
b2.addActionListener(listener);
}
}
codebase="." archive="/roller/luminis/resource/flexvsjava/javavsflex.jar,/roller/luminis/resource/flexvsjava/AnimatedTransitions-0.11.jar,/roller/luminis/resource/flexvsjava/TimingFramework-1.0.jar" width="300" height="200">
Een vergelijking in de praktijk
Om de vergelijking tussen Java en Flex wat meer te illustreren, zal ik een tweetal implementatie voorbeelden geven. Ten eerste een crossfading animatie, en vervolgens een component slide.
Om te beginnen met de crossfade. Een crossfade is een transitie van een scherm naar het andere in dezelfde ruimte. Het effect hierbij is dat geleidelijk de transparantie van het initiële component toeneemt en die van het volgende scherm afneemt. Hierbij krijg je een vloeiende overgang tussen de schermen.
Om te beginnen met de Flex implementatie.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="200" height="200">
<mx:Boolean id="fadeToggle" />
<mx:Fade id="fadeOut" duration="1000" alphaFrom="1.0" alphaTo="0.0"/>
<mx:Fade id="fadeIn" duration="1000" alphaFrom="0.0" alphaTo="1.0"/>
<mx:VBox height="100%" width="100%" paddingLeft="20" paddingTop="20" paddingRight="20" paddingBottom="20">
<mx:Canvas width="100%" styleName="content">
<mx:VBox id="box1" visible="{!fadeToggle}" hideEffect="{fadeOut}" showEffect="{fadeIn}" >
<mx:Image x="45" y="52" source="@Embed(source='../assets/images/duke.gif')"/>
<mx:Button label="Duke!!" enabled="false"/>
</mx:VBox>
<mx:VBox id="box2" visible="{fadeToggle}" hideEffect="{fadeOut}" showEffect="{fadeIn}">
<mx:Image x="45" y="52" source="@Embed(source='../assets/images/penduke.gif')"/>
<mx:CheckBox label="Duke with pen!!" enabled="false" selected="true"/>
</mx:VBox>
</mx:Canvas>
<mx:Button click="fadeToggle=!fadeToggle" label="Cross Fade"/>
</mx:VBox>
</mx:Application>
Hier worden twee containers, box1 en box2, gedeclareerd die in elkaar zullen overvloeien. Er zijn twee fade-effecten, fadeIn en fadeOut, die verantwoordelijk zijn voor de fading (transparantie).
Middels binding (fadeToggle) op het visiblity attribuut worden de containers zichtbaar en onzichtbaar gemaakt. De effecten worden geactiveerd middels de hideEffect en showEffect attributen van de containers.
Voor de Java implementatie dient er om te beginnen een subklasse van JPanel gemaakt te worden:
public class FadeablePanel extends JPanel {
private float m_opacity;
public FadeablePanel(float initialOpacity) {
super();
setOpaque(false);
m_opacity = initialOpacity;
}
/**
* Overridden to set the opaqueness of <code>comp</code> to false
*/
protected void addImpl(Component comp, Object constraints, int index) {
if (comp instanceof JComponent) {
((JComponent)comp).setOpaque(false);
}
super.addImpl(comp, constraints, index);
}
/**
*
* @return
*/
public float getOpacity() {
return m_opacity;
}
/**
*
* @param f
*/
public void setOpacity(float f) {
if (f >= 0.0 && f <= 1.0) {
m_opacity = f;
repaint();
}
}
/**
* Overridden to set the opacity to the level set using {@link #setOpacity(float)}.
*/
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
m_opacity));
super.paintComponent(g);
}
}
Deze klasse herimplementeert de paint methode om de gezette transparantie te tekenen. Vervolgens de implementatie van het animatie effect voor een enkel component:
public class FadeEffect {
private static final int SLEEP_TIME = 10;
private float m_fromAlpha;
private float m_toAlpha;
private int m_duration;
public FadeEffect(float fromAlpha, float toAlpha, int duration) {
if (fromAlpha < 0.0 || fromAlpha > 1.0)
throw new IllegalArgumentException("From alpha value must be between 0.0 and 1.0");
if (toAlpha < 0.0 || toAlpha > 1.0)
throw new IllegalArgumentException("From alpha value must be between 0.0 and 1.0");
m_fromAlpha = fromAlpha;
m_toAlpha = toAlpha;
m_duration = duration;
}
public void apply(final FadeablePanel componentToFade) {
new SwingWorker<Object, Object>() {
protected Object doInBackground() throws Exception {
int numSteps = m_duration / SLEEP_TIME;
float stepAmount = (m_toAlpha - m_fromAlpha) / (float)numSteps;
float currentOpacity = componentToFade.getOpacity();
for (int i = 0; i < numSteps; i++) {
float newOpacity = currentOpacity + stepAmount;
componentToFade.setOpacity(newOpacity);
currentOpacity = newOpacity;
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {}
}
if (componentToFade.getOpacity() != m_toAlpha) {
componentToFade.setOpacity(m_toAlpha);
}
return null;
}
}.execute();
}
}
Hier wordt bij de constructor de start- en eindtransparantie en de duur van de animatie opgegeven. De apply methode start de animatie, die middels een background thread wordt gedaan. Voor de echte crossfade een voorbeeld paneltje:
public class CrossFadeExamplePanel extends JPanel {
private static final FadeEffect FADE_IN = new FadeEffect(0.0f, 1.0f, 500);
private static final FadeEffect FADE_OUT = new FadeEffect(1.0f, 0.0f, 500);
private FadeablePanel panel1;
private FadeablePanel panel2;
public CrossFadeExamplePanel() {
super(new BorderLayout());
JLabel label1;
JLabel label2;
try {
ImageIcon icon1 = new ImageIcon(getClass().getResource("duke.gif"));
ImageIcon icon2 = new ImageIcon(getClass().getResource(
"penduke.gif"));
label1 = new JLabel(icon1);
label2 = new JLabel(icon2);
} catch (Exception e) {
label1 = new JLabel("Could not load image duke.gif");
label2 = new JLabel("Could not load image penduke.gif");
}
panel1 = new FadeablePanel(1.0f);
panel1.setLayout(new BorderLayout());
panel1.add(label1, BorderLayout.CENTER);
panel2 = new FadeablePanel(0.0f);
panel2.setLayout(new BorderLayout());
panel2.add(label2, BorderLayout.CENTER);
JPanel content = new JPanel();
content.setLayout(new OverlayLayout(content));
content.add(panel1);
content.add(panel2);
add(content, BorderLayout.CENTER);
JButton fadeButton = new JButton(new FadeAction());
JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
buttonPane.add(fadeButton);
add(buttonPane, BorderLayout.SOUTH);
}
private class FadeAction extends AbstractAction {
FadeAction() {
super("Cross fade");
}
public void actionPerformed(ActionEvent e) {
if (panel1.getOpacity() == 1.0f) {
FADE_IN.apply(panel2);
FADE_OUT.apply(panel1);
} else {
FADE_IN.apply(panel1);
FADE_OUT.apply(panel2);
}
}
}
}
Hier worden twee componenten over elkaar geplaatst, en middels de twee gedeclareerde fade-effecten wordt de crossfade bereikt.
codebase="." archive="/roller/luminis/resource/flexvsjava/javavsflex.jar" width="400" height="250">
Het volgende voorbeeld betreft een een component in twee delen waarbij het onderste gedeelte omhoog en omlaag ‘glijdt’, vaak ook wel het ‘accordion’ effect genoemd. Het onderste gedeelte heeft een knop in breedte die het uit- en invouwen initiëert.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="378" height="528" applicationComplete="init()">
<mx:Style>
Text {
padding-bottom: 10;
padding-left: 10;
padding-right: 10;
padding-top: 10;
}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.effects.Resize;
import mx.effects.Effect;
import mx.events.EffectEvent;
private var targetHeight:int = 100;
private var collapsed:Boolean = false;
private function init():void {
collapse.addEventListener(EffectEvent.EFFECT_END,
function():void
{
collapsed = true;
header.label = "Click to expand";
}
);
expand.addEventListener(EffectEvent.EFFECT_END,
function():void
{
collapsed = false;
header.label = "Click to collapse";
}
);
}
private function doCollapse():void {
var resize:Effect;
if (collapsed) {
resize = expand;
} else {
resize = collapse;
}
resize.play();
}
]]>
</mx:Script>
<mx:Parallel id="collapse">
<mx:Resize target="{panel1}" heightBy="100" />
<mx:Resize target="{panel2}" heightBy="-100" />
<mx:Move target="{panel2}" yBy="100"/>
</mx:Parallel>
<mx:Parallel id="expand">
<mx:Resize target="{panel1}" heightBy="-100" />
<mx:Resize target="{panel2}" heightBy="100" />
<mx:Move target="{panel2}" yBy="-100"/>
</mx:Parallel>
<mx:Canvas x="56" y="40" width="250" height="420"
horizontalScrollPolicy="off" verticalScrollPolicy="off" borderStyle="solid" cornerRadius="2">
<mx:VBox id="panel1" width="100%" height="66%" top="0" verticalScrollPolicy="off">
<mx:Text width="100%" height="100%">
<mx:text>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</mx:text>
</mx:Text>
</mx:VBox>
<mx:VBox id="panel2" width="100%" height="34%" bottom="0" verticalScrollPolicy="off">
<mx:Button id="header" label="Click to collapse" width="247" click="doCollapse()"/>
<mx:Text width="100%" height="100%">
<mx:text>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</mx:text>
</mx:Text>
</mx:VBox>
</mx:Canvas>
</mx:Application>
De truc zit hem voornamelijk in de collapse en expand effecten; het collapse effect vergroot het bovenste en onderste gedeelte, en verplaatst het onderste naar beneden. Het expand effect doet hetzelfde, maar met inverse waarden.
De Flex versie:
Vervolgens de Java implementatie:
public class SlideExamplePanel extends JPanel {
private static final String TEXT_1 = "Lorem ipsum dolor sit amet, " +
"consectetur adipisicing elit, " +
"sed do eiusmod tempor incididunt ut labore et " +
"dolore magna aliqua. Ut enim ad minim veniam, " +
"quis nostrud exercitation ullamco laboris nisi " +
"ut aliquip ex ea commodo consequat. Duis aute " +
"irure dolor in reprehenderit in voluptate velit " +
"esse cillum dolore eu fugiat nulla pariatur. " +
"Excepteur sint occaecat cupidatat non proident, " +
"sunt in culpa qui officia deserunt mollit anim " +
"id est laborum.";
private static final String TEXT_2 = "Lorem ipsum dolor sit amet, " +
"consectetur adipisicing elit";
private static final int TOP_HEIGHT_EXPANDED = 300;
private static final int BOTTOM_HEIGHT_EXPANDED = 100;
private boolean collapsed = false;
private JPanel topPanel;
private JPanel bottomPanel;
private JButton resizeButton;
/**
* Create the panel.
*/
public SlideExamplePanel() {
super();
topPanel = new JPanel(new GridLayout());
JTextPane textPane1 = new JTextPane();
textPane1.setContentType("text/html");
textPane1.setText(TEXT_1);
textPane1.setEditable(false);
textPane1.setBackground(topPanel.getBackground());
topPanel.add(textPane1, BorderLayout.CENTER);
bottomPanel = new JPanel(new BorderLayout());
resizeButton = new JButton(new ResizeAction());
resizeButton.setMargin(new Insets(5, 5, 5, 5));
bottomPanel.add(resizeButton, BorderLayout.NORTH);
JTextPane textPane2 = new JTextPane();
textPane2.setContentType("text/html");
textPane2.setText(TEXT_2);
textPane2.setEditable(false);
textPane2.setBackground(bottomPanel.getBackground());
bottomPanel.add(textPane2, BorderLayout.CENTER);
setLayout(null);
topPanel.setBounds(0, 0, 300, TOP_HEIGHT_EXPANDED);
bottomPanel.setBounds(0, 300, 300, BOTTOM_HEIGHT_EXPANDED);
add(topPanel);
add(bottomPanel);
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
Dimension size = getSize();
int newTopPanelHeight = (int)(size.getHeight() - bottomPanel.getHeight());
int newBottomPanelHeight = bottomPanel.getHeight();
int newBottomPanelY = newTopPanelHeight;
topPanel.setSize((int)size.getWidth(), newTopPanelHeight);
bottomPanel.setBounds(0, newBottomPanelY, (int)size.getWidth(), newBottomPanelHeight);
}
});
}
/**
*
*
*/
private class ResizeAction extends AbstractAction {
ResizeAction() {
super("Collapse");
}
public void actionPerformed(ActionEvent e) {
int currentHeight = getHeight();
int prefBottomHeaderHeight = (int)resizeButton.getSize().getHeight();
final int newTopHeight;
if (collapsed) {
newTopHeight = currentHeight - BOTTOM_HEIGHT_EXPANDED;
} else {
newTopHeight = (currentHeight - prefBottomHeaderHeight);
}
// Animation thread
new Thread(new Runnable() {
public void run() {
int increment = collapsed ? -3 : 3;
while (((int)topPanel.getHeight()) != newTopHeight) {
topPanel.setSize(topPanel.getWidth(), topPanel.getHeight() + increment);
bottomPanel.setBounds(0, bottomPanel.getY() + increment, bottomPanel.getWidth(), bottomPanel.getHeight() + increment);
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
}
collapsed = !collapsed;
putValue(NAME, collapsed ? "Expand" : "Collapse");
}
}).start();
}
}
}
In de klasse-constructor wordt dezelfde componenten hiërarchie opgebouwd als in de Flex applicatie. In de ResizeAction klasse wordt de collapse/expand logica uitgevoerd. In deze implementatie wordt gebruikt gemaakt van de void setBounds(int,int,int,int) methode van Component, om het effect te bereiken, op in principe dezelfde methode als in de eerder getoonde Flex applicatie. Door in een Thread de setBounds methode met aangepaste waarden aan te roepen wordt er een animatie van gemaakt.
codebase="." archive="/roller/luminis/resource/flexvsjava/javavsflex.jar" width="400" height="400">
Conclusie
Door naar de bovenstaande voorbeelden te kijken wordt een al snel duidelijk dat beide platforms een rijk scala aan component effecten mogelijk maakt.
Een aspect springt echter meteen in het oog: de mate van complexiteit die vereist is bij de Java implementaties om een met Flex vergelijkbaar resultaat te krijgen. Het is allemaal wel mogelijk, maar het vraagt aardig wat inspanning van de ontwikkelaar.
Nu zijn er wel een hoop libraries beschikbaar die ‘t leven van een Java-ontwikkelaar een stuk makkelijker maken, maar feit is wel dat de Swing/AWT API’s duidelijk niet zijn geschreven met effect-rijke GUI in het oog.
Java sources: Download
Flex vs Java Deel 1: Stijlen
Geplaatst door admin in Uncategorized op 27 oktober 2008
<h2>Inleiding</h2> <p> De veronderstelling is dat je met Flex sneller een gelikte UI in elkaar kunt zetten, maar is dat ook echt zo? Misschien voor een simpel “hello world” appje, maar hoe zit het met complexere UI’s met “custom” vormgeving? </p> <p> In dit eerste deel van de vergelijking tussen Java en Flex zullen we ons richten op de mogelijkheden die Java i.c.m. de Synth LAF en Flex bieden om componenten te skinnen. </p> <h2>Skinnen in Flex</h2> <p> In Flex kan skinnen van componenten op twee manieren: direct in de MXML declaratie van een component of in aparte stylesheets. Skinnen met behulp van stylesheets heeft als voordeel herbruikbaarheid, en wordt daarom ook gezien als best practice.<br/> Stylesheets in Flex lijken verdacht veel op de <a href=”http://www.w3.org/Style/CSS/”>W3C CSS</a>; niet ontoevallig dat Adobe ze ook bij die naam noemt.<br/> Er zijn echter wat aspecten die afwijken van de W3C standaard, en specifiek met betrekking tot selectors: <ul> <li>Geen ondersteuning voor ID selectors. Hiermee kan je in CSS een selector voor een specifiek element declareren, b.v. <code>#pageTitle</code></li> <li>Geen ondersteuning voor nesting selectors. Hiermee kan je alle elementen binnen een ander element selecteren. Voorbeeld: <code>ul a</code>, alle links binnen een unordered list</li> <li>Geen ondersteuning voor pseudo-classes. Meest bekende pseudo-selector is <code>a:hover</code>, waarmee je een de stilering van een link waar de muispointer op ligt kan definiëren</li> </ul> </p> <p> Laten we om te beginnen eens kijken hoe het skinnen werkt als je dat direct op in de component declaratie doet. Als voorbeeld maken we een knop: <pre> <mx:Button label=”Click Me” fontFamily=”Georgia” fontSize=”20″ fontStyle=”italic” textDecoration=”underline” color=”#0000FF”/> </pre> Zoals eerder vermeld, om alle stileringsinformatie op de componenten zelf te plaatsen zou nogal wat werk kosten. Welnu, een klein voorbeeld van hoe dezelfde skin er in Flex CSS uitziet: <pre> Button { color: #0000FF; fontFamily: Georgia; fontSize: 20; fontStyle: italic; textDecoration: underline; } </pre> </p> <p> <div><object classid=”clsid:D27CDB6E-AE6D-11cf-96B8-444553540000″ codebase=”https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0″ data=”/roller/luminis/resource/flexvsjava/ButtonExample.swf” height=”60″ type=”application/x-shockwave-flash2-preview” width=”200″ ><param name=”data” value=”/roller/luminis/resource/flexvsjava/ButtonExample.swf”/><param name=”loop” value=”false”/><param name=”menu” value=”false”/><param name=”movie” value=”/roller/luminis/resource/flexvsjava/ButtonExample.swf”/><param name=”quality” value=”high”/><param name=”scale” value=”exactfit”/><param name=”src” value=”/roller/luminis/resource/flexvsjava/ButtonExample.swf”/><param name=”type” value=”application/x-shockwave-flash2-preview”/><embed height=”60″ pluginspage=”https://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash” quality=”high” src=”/roller/luminis/resource/flexvsjava/ButtonExample.swf” type=”application/x-shockwave-flash2-preview” width=”200″ /></object></div> </p> <p> Voor diegenen die vaker met HTML en CSS werken zal het redelijk bekend voorkomen. Nu een wat geavanceerder voorbeeld: <pre> Button { color: #0080c0; font-size: 12; corner-radius: 10; border-color: #006699; border-thickness: 3; fill-colors: #FCFFF4, #006699; fill-alphas: 0.5, 0.5; text-roll-over-color: #FFFFFF; } </pre> </p> <p> <div><object classid=”clsid:D27CDB6E-AE6D-11cf-96B8-444553540000″ codebase=”https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0″ data=”/roller/luminis/resource/flexvsjava/AdvancedButtonExample.swf” height=”60″ type=”application/x-shockwave-flash2-preview” width=”150″ ><param name=”data” value=”/roller/luminis/resource/flexvsjava/AdvancedButtonExample.swf”/><param name=”loop” value=”false”/><param name=”menu” value=”false”/><param name=”movie” value=”/roller/luminis/resource/flexvsjava/AdvancedButtonExample.swf”/><param name=”quality” value=”high”/><param name=”scale” value=”exactfit”/><param name=”src” value=”/roller/luminis/resource/flexvsjava/AdvancedButtonExample.swf”/><param name=”type” value=”application/x-shockwave-flash2-preview”/><embed height=”60″ pluginspage=”https://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash” quality=”high” src=”/roller/luminis/resource/flexvsjava/AdvancedButtonExample.swf” type=”application/x-shockwave-flash2-preview” width=”150″ /></object></div> </p> <p> Hier wordt al meteen duidelijk dat Flex een rijk scala aan stijl- en effectattributen biedt, o.a. voor hoekafrondingen, meerdere kleuren van borders, gradients, transparantie. Tevens is hier te zien dat het ontbreken van pseudo-selectors wordt opgevangen door attributen als <code>overFillColors</code>, <code>selectedFillColorRatios</code>, enz. </p> <p> Toewijzen van stijlen aan componenten in Flex kan op de volgende manieren: <ul> <li>Door middel van type selectors: de naam van de componentklasse wordt als selector gedefinieerd.</li> <li>Door middel van class selectors: een component een stijlnaam te geven, die vervolgens als selector in de style sheet te declareren.</li> </ul> Overigens zijn combinaties van bovenstaande methodes ook mogelijk, hierover later meer. </p> <h3>Type selectors</h3> <p> Indien we voor een componentklasse een stijl willen definieren, hoeven we slechts zijn naam als CSS selector met de bijbehorende stijlattributen te declareren: <pre> Button { color: red; cornerRadius: 12; textSize: 12; } Label { color: black; } </pre> </p> <p> Een leuk aspect is de manier waarop componentovererving is verweven met stijldefinities. Nemen we als voorbeeld de <code>Text</code> klasse: deze erft van <code>Label</code>, en erft ook alle gedefinieerde stijlelementen van <code>Label</code>. <pre> Label { color: red; cornerRadius: 12; textSize: 12; } Text { color: black; } </pre> </p> <h3>Class selectors</h3> <p> De andere manier om stijlkoppelingen te bewerkstelligen is een stijl direct toewijzen een component, d.m.v. een zogenaamde class selector.<br/> Neem het volgende code fragment: <pre> .myButton { color: red; textSize: 12; } </pre> <pre> <mx:Button styleName=”myButton” label=”OK”/> </pre> </p> <p> Zoals eerder vermeld, zijn type selectors en class selectors ook te combineren. Type selectors worden toegepast op alle instanties van een component klasse, maar daarnaast kan een instantie van een component klasse ook een eigen stijl selecteren. De gedeclareerde attributen in deze stijl zullen alle attributen overschrijven die in de type selector staan. Als voorbeeld: <pre> Button { color: yellow; textSize: 12; } .myButton { color: red; } </pre> <pre> <mx:Button label=”Cancel”/> <!– Gele tekst –> <mx:Button styleName=”myButton” label=”OK”/> <!– Rode tekst –> </pre> </p> <h2>Java Synth LAF</h2> <p> De Java Synth LAF is, zoals de naam al zegt, een Pluggable Look & Feel implementatie voor Swing. Dit wil ook zeggen dat Synth niet per definitie als skin voor een applicatie wordt gebruikt, maar dat deze, zoals alle Swing Look and Feels, eerst geïnstalleerd moet worden. <pre> URL file = …; // De URL naar de Synth style file SynthLookAndFeel synth = new SynthLookAndFeel(); try { synth.load(file); UIManager.setLookAndFeel(synth); } catch (Exception e) { e.printStackTrace(); } </pre> </p> <p> Een van de dingen die al meteen opvallen is dat Synth LAF een referentie naar een bestand nodig heeft. Dit is een bestand waar alle stijlen zijn gedefiniëerd; Synth heeft namelijk geen ‘default’ skin, dus de aanweizigheid van dit bestand is vereist. </p> <p> Wat staat er nou in zo’n Synth stijl bestand? Hier een klein voorbeeld: <pre> <synth> <color id=”fg_color” value=”#00364e”/> <color id=”bg_color” value=”#0f79a9″/> <font id=”std_font” name=”Arial” size=”12″ style=”PLAIN”/> <style id=”default”> <font idref=”std_font/> <state> <color idref=”fg_color” type=”FOREGROUND”/> <color idref=”bg_color” type=”BACKGROUND”/> </state> </style> <bind style=”default” type=”region” key=”.*”/> <!– … overige stijlen… –> </synth> </pre> Dit is een voorbeeld van een Synth Style bestand in zijn meest kale vorm: er wordt alleen een standaard stijl gegeven die geldt voor alle componenten. Aan deze stijl wordt gekoppeld een voor- en achtergrondkleur en een font.<br/> Een interessant detail aan dit voorbeeld is dat het illustreert hoe stijlelementen, zoals kleuren en lettertypen, herbruikt kunnen worden. <br/> De functie van het bind element behandelen we verderop. </p> <p> Nu naar een wat ingewikkelder component, een button: <pre> <style id=”defaultbutton”> <state id=”def”> <imagePainter method=”buttonBackground” path=”button_up.png” sourceInsets=”8 8 8 8″ paintCenter=”true” /> <insets top=”8″ left=”8″ bottom=”8″ right=”8″ /> <font name=”Arial” size=”12″ style=”BOLD” /> <color value=”#000000″ type=”TEXT_FOREGROUND” /> </state> <state value=”PRESSED”> <imagePainter method=”buttonBackground” path=”button_pressed.png” sourceInsets=”8 8 8 8″ paintCenter=”true” /> <insets top=”8″ left=”8″ bottom=”8″ right=”8″ /> </state> <state value=”MOUSE_OVER”> <imagePainter method=”buttonBackground” path=”button_over.png” sourceInsets=”8 8 8 8″ paintCenter=”true” /> <insets top=”8″ left=”8″ bottom=”8″ right=”8″ /> </state> <state value=”DISABLED” clone=”def”> <color value=”#999999″ type=”TEXT_FOREGROUND” /> </state> </style> <bind style=”defaultbutton” type=”region” key=”BUTTON”/> </pre> </p> <p> <applet code=”ButtonStyleExample.class” codebase=”.” archive=”/roller/luminis/resource/flexvsjava/flexvsjavastyles.jar” width=”300″ height=”100″></applet> </p> <p> In dit voorbeeld zijn eigenlijk al bijna alle kernconcepten van de Synth Style vervat. Een Synth Style bestand bevat een of meerdere style declaraties, die een unieke identificatie hebben (het id attribuut). Een style slaat in de praktijk op de skin van een specifieke componentklasse, in dit voorbeeld buttons. </p> <p> Binnen een style worden een of meerdere states gedefiniëerd. Een state is ook letterlijk de toestand waarin het component zich bevindt. In dit voorbeeld zijn een viertal states gedefinieerd: de standaard wanneer er niets bijzonders met het component gebeurt, wanneer hij ingedrukt wordt (<code>PRESSED</code>), wanneer hij disabled is (<code>DISABLED</code>) en wanneer de mousepointer er over zweeft (<code>MOUSE_OVER</code>). Overige beschikbare states zijn: <code>ENABLED</code>, <code>FOCUSSED</code>, <code>SELECTED</code>, <code>DEFAULT</code>. </p> <p> Een state bevat de stijlattributen van een component in een specifieke toestand. DIt kunnen zijn kleuren voor de voor- of achtergrond (het <code>color</code> element), lettertypes voor tekst (het <code>font</code> element), afbeeldingen (het <code>imagePainter</code> element), en de grensruimte van een component (het <code>insets</code> element). </p> <p> Overerving van stijlen is ook mogelijk, en wel door middel van het clone attribuut binnen het style element. <pre> <style id=”textfield”> <state> <color value=”yellow” type=”BACKGROUND”/> <color value=”blue” type=”TEXT_FOREGROUND”/> <font name=”Arial” size=”14″ style=”BOLD” /> <insets top=”4″ left=”4″ bottom=”4″ right=”4″ /> </state> </style> <style id=”passwordfield” clone=”textfield”> <state> <color value=”orange” type=”BACKGROUND”/> <color value=”black” type=”TEXT_FOREGROUND”/> </state> </style> <bind style=”textfield” type=”region” key=”TextField”/> <bind style=”passwordfield” type=”region” key=”PasswordField”/> </pre> </p> <p> <applet code=”TextFieldStyleExample.class” codebase=”.” archive=”/download/attachments/14812620/flexvsjavastyles.jar” width=”300″ height=”100″></applet> </p> <p> In Java Synth wordt een stijl gekoppeld aan een component door middel van het bind element. Dit element biedt de mogelijkheid om een stijl te binden aan een component. Het bind element heeft 3 attributen die van belang zijn: <ul> <li><code>style</code>: de referentie naar een elders gedeclareerde stijl</li> <li><code>type</code>: de soort koppeling, d.w.z. generieke of specifieke koppeling</li> <li><code>key</code>: de sleutel van de koppeling, d.w.z. de naam van een component of de naam van de klasse.</li> </ul> </p> <p> Beginnend met de declaratie van een stijl: <pre> <style id=”buttonStyle”>…</style> </pre> Een bind element moet in ieder geval een referentie hebben naar de gedeclareerde stijl, in dit geval <code>buttonStyle</code>: <pre> <bind style=”buttonStyle” … /> </pre> Als voorbeeld willen we alle knoppen in onze applicatie binden aan de stijl <code>buttonStyle</code>. Dit doen we door als type attribuut ‘region’ op te geven, en als key attribuut een van de constanten uit de <code>javax.swing.plaf.synth.Region</code> klasse te gebruiken, in dit geval de waarde van <code>javax.swing.plaf.synth.Region.BUTTON</code>: <pre> <bind style=”buttonStyle” type=”region” key=”Button” /> </pre> </p> <p> Om een enkel component een afwijkende stijl te geven is wat meer werk nodig. Nu vertellen we de binding dat het moet kijken naar componenten die een specifieke naam hebben. Als voorbeeld maken we een binding die de stijl <code>buttonStyle</code> koppelt aan componenten met naam <code>XXX</code>: <pre> <bind style=”buttonStyle” type=”name” key=”XXX” /> </pre> Met deze binding hebben we in feite nog niets gekoppeld. Dit gebeurt pas als er een component wordt aangemaakt die <code>XXX</code> als naam heeft: <pre> JButton b = new JButton(”Klik mij”); b.setName(”XXX”); </pre> </p> <p> Een leuke feature van binden is dat er ook reguliere expressies gebruikt kunnen worden. Bijvoorbeeld: <pre> <bind style=”knopStijl” type=”name” key=”knop.*” /> </pre> Dit bindt alle componenten wiens naam begint met <code>knop</code> aan de stijl <code>knopStijl</code>. </p> <p> Een ander aspect van stijl koppelingen in Synth is dat er meerdere stijlen gebonden kunnen worden aan componentent. Neem de volgende stijldeclaraties en bindings: <pre> <style id=”Globaal”> <state> <color value=”blue” type=”FOREGROUND”/> </state> </style> <style id=”Knop”> <state> <color value=”orange” type=”FOREGROUND”/> </state> </style> <style id=”SpecialeKnop”> <state> <color value=”red” type=”BACKGROUND”/> </state> </style> <bind style=”Globaal” type=”region” name=”*”/> <bind style=”Knop” type=”region” name=”Button”/> <bind style=”SpecialeKnop” type=”name” name=”speciale_knop.*”/> </pre> Bovenstaande code geeft alle componenten een gele tekstkleur, behalve knoppen die een blauwe tekstkleur krijgen. Indien de naam van een knop (die reeds de <code>Knop</code> stijl heeft) begint met <code>speciale_knop</code>, zal deze ook de stijl <code>SpecialeKnop</code> toegewezen krijgen, wat inhoudt dat de achtergrond rood wordt. </p> <h2>Een vergelijking in de praktijk</h2> <p> Welnu, een praktijk voorbeeldje. We stellen ons als opdracht: het maken van een button met afgeronde hoeken en een achtergrond met een verticale 2-kleuren gradient (wit en groen). Tevens heeft de button een mouse-over effect waarbij de tweede gradient kleur wijzigt naar donkergroen. </p> <h3>Implementatie in Flex</h3> <p> In Flex is dit een fluitje van een cent. Om te beginnen met de ronde hoeken, welke te verwezenlijken zijn met het ‘corner-radius’ stijl attribuut: <pre> Button { cornerRadius: 12; /* De mate van afronding */ } </pre> Vervolgens de gradient, waar een tweetal stijattributen van belang zijn: fillColors en fillColorRatios. Het eerst attribuut definieert de kleuren, het tweede de relatieve posities (op een schaal van 0 tot 255) waar maximale verzadiging is. <pre> Button { cornerRadius: 12; /* De mate van afronding */ fillColors: #FFFFFF, #00FF00; /* wit, groen */ fillColorRatios: 0, 255; } </pre> Ten slotte het mouse-over effect, waarbij de overFillColors en overFillColorRatios worden gebruikt. <pre> Button { cornerRadius: 12; /* De waarde geeft de mate van hoekafronding */ fillColors: #FFFFFF, #33FF00; /* wit, groen */ fillColorRatios: 0, 255; overFillColors: #FFFFFF, #00CC00; /* wit, donkergroen */ overFillColorRatios: 0, 255; } </pre> </p> <p> <div><object classid=”clsid:D27CDB6E-AE6D-11cf-96B8-444553540000″ codebase=”https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0″ data=”/roller/luminis/resource/flexvsjava/ButtonGradientExample.swf” height=”60″ type=”application/x-shockwave-flash2-preview” width=”150″ ><param name=”data” value=”/roller/luminis/resource/flexvsjava/ButtonGradientExample.swf”/><param name=”loop” value=”false”/><param name=”menu” value=”false”/><param name=”movie” value=”/roller/luminis/resource/flexvsjava/ButtonGradientExample.swf”/><param name=”quality” value=”high”/><param name=”scale” value=”exactfit”/><param name=”src” value=”/roller/luminis/resource/flexvsjava/ButtonGradientExample.swf”/><param name=”type” value=”application/x-shockwave-flash2-preview”/><embed height=”60″ pluginspage=”https://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash” quality=”high” src=”/roller/luminis/resource/flexvsjava/ButtonGradientExample.swf” type=”application/x-shockwave-flash2-preview” width=”150″ /></object></div> </p> <h3>Implementatie in Java Synth LAF</h3> Hier lopen we al meteen tegen 2 issues aan: Synth LAF heeft geen ondersteuning voor afgeronde hoeken in componenten (de zogenaamde corner radius) en gradients. In principe is een gradient achtergrond wel te maken, maar dit kost wat werk. Eerst moeten we een painter coderen die gradients kan maken: <pre> public class GradientPainter2 extends SynthPainter { public void paintTextFieldBackground(SynthContext context, Graphics g, int x, int y, int w, int h) { GradientPaint gp = new GradientPaint((float)(x + (w/2)), (float)y, Color.WHITE, (float)(x + (w/2)), (float)(y + h), new Color(0, 200, 0)); Graphics2D g2 = (Graphics2D)g; g2.setPaint(gp); g2.fillRect(x, y, w, h); g2.setPaint(null); } } </pre> Vervolgens declareren we deze in de Synth configuratie als een object, en maken een painter op basis van dit object: <pre> <object id=”gradient”/> <style id=”button”> <painter method=”buttonBackground” idref=”gradient”/> </style> </pre> Hiermee hebben we helaas nog geen afgeronde hoeken. Een andere taktiek is dus vereist. De simpelste methode is het maken van achtergrondafbeeldingen die de gradient en afgeronde hoeken al hebben. Deze afbeeldingen worden door Synth geschaald op de grootte van de button <pre> <style id=”button”> <state> <imagePainter method=”buttonBackground” path=”buttonUp.png” sourceInsets=”8 8 8 8″ paintCenter=”true”/> </state> <state value=”MOUSE_OVER”> <imagePainter method=”buttonBackground” path=”buttonDown.png” sourceInsets=”8 8 8 8″ paintCenter=”true”/> </state> </style> </pre> Interessant om te vermelden is dat de bovenstaande methode met achtergrondafbeeldingen ook in Flex toegepast kan worden: <pre> Button { up-skin: Embed(source=”buttonUp.png”); over-skin: Embed(source=”buttonDown.png”); } </pre> <h2>Conclusie</h2> <p> Als we de twee implementaties nu vergelijken, valt meteen op dat Synth erg afhankelijk is van externe graphics wil er een beetje leuk resultaat uit komen. Dit heeft tot gevolg dat er een grote afhankelijkheid op een designer is, en dat de grootte van de applicatie aanzienelijk toeneemt door alle extra graphics die erbij komen. </p> <p> Flex daarentegen biedt een developer wat meer ruimte en mogelijkheden om in de loop van een ontwikkeltraject zelf nog aanpassingen te kunnen doen. Hierbij moet opgemerkt worden dat bij ieder nadeel weer en voordeel zit: indien er voor wordt gekozen om de skin volledig met externe graphics op te bouwen, vereist het minder vaardigheden van de designer, die zich niet bezig hoeft te houden met MXML en/of Actionscript. </p> <p> Wel is het zo dat skins in Synth op een veel consistentere manier zijn op te bouwen. Vrijwel alle stijlelementen zijn herbruikbaar, dus er zullen minder snel foutjes insluipen. Tevens heeft dit tot gevolg dat skin refactorings (denk aan verandering van fonts of kleuren) een relatief eenvoudige operatie zullen zijn. Dit zou in Flex toch wat meer aandacht en tijd kosten, aangezien CSS geen hergebruik (anders gezegd: centrale declaratie) van stijlelementen mogelijk maakt. </p> <p> Wat verder opvalt is dat de de hoeveelheid CSS code voor een Flex applicatie t.o.v. Synth Style bestand vele malen compacter is. </p>
Wicket in Action review
Geplaatst door Marcel Offermans in Uncategorized op 17 oktober 2008
<p>Wicket in Action, by Martijn Dashorst and Eelco Hillenius, is a great book about a good and mature web framework called Wicket. The book wastes no time and dives right in, explaining all aspects of web development with nicely documented examples.</p> <p>Web frameworks have come a long way. As with any type of user interface, they are evolving fast and there are many different ones around. Choosing the right one becomes more difficult because of that. The main reasons for using Wicket should be:</p> <ul> <li>the nice separation of logic and layout, allowing interaction designers and developers to work together efficiently;</li> <li>the natural way in which you can develop your applications in Java, using patterns familiar from other UI frameworks such as Swing;</li> <li>the component friendly architecture, which allows easy integration of Wicket in component frameworks such as OSGi and derivatives like Spring.</li> </ul> <p>The area where Wicket shines are complex, highly dynamic websites and applications.</p> <p>What’s good about this book is that it is written in a very easy going style and still covers all aspects of Wicket well. You can both read it front to back, in which case a cheese store website will keep popping up, showcasing all features as they are explained. Diving right in worked well for me too, directly going to the chapters containing the topics I was interested in.</p> <p>The book is well structured, starting with the basics and covering important topics like internationalization, all aspects of securing your website, maintainability, compatibility with all kinds of browsers using advanced features like Ajax based interaction with fallbacks in case browsers don’t allow any Javascript. It also explains how to debug your site and how to write efficient automated tests that hook into Wicket directly. Some of that stuff is interesting even if you don’t use Wicket.</p> <p>If you’re looking for a book that gives you a good background on developing highly dynamic websites in Java, I can definitely recommend reading Wicket in Action. It’s a good addition to the online documentation found on their website and definitely entertaining.</p>
Whitepaper: Using JMX for software manageability
Geplaatst door admin in Uncategorized op 15 oktober 2008
Recently we’ve been doing research in the application of Java Management Extensions (JMX) for software manageability. We’ve put together a whitepaper with practical examples and best practices.
From the introduction:
This white paper will outline some of the technical challenges when dealing with manageability
issues in software systems. The main focus will be on the JMX standard, which is the de facto
manageability standard in the Java world. At first, this paper will start with outlining the
technological aspects of JMX by introducing the API with a simple test case, showing the ease of
adding new management functionality to a pre-existing system. Another chapter outlines some
of the common design decisions which have to be taken when developing a management
solution, such as when to use manageability features and what internals of the system to
expose. Finally, some insights on various JMX management agents will be given.
Full text: Using JMX for software manageability
