The purpose of this document is to provide a brief description of the idea behind the configurable component and to show example use cases.
The idea behind the ConfigurableComponent originates from the common use case for components in the layout framework. There are many component compounds which basically represent one component consisting of two parts:
In order to make life easier for developers having to implement this pattern, the ConfigurationComponent has been introduced. The idea is to split the common implementation (like model and configuration changes etc.) from the actual business specifics.
As mentioned above, a configurable component consists of two
parts: configuration provider (IConfigurator) and
configurable part (IConfigurable). The sole purpose of the
configuration provider is to provide configuration values for
configuration properties and to notify the configurable part upon
configuration changes. The configurable part is responsible for
listening to the relevant changes and to update its contents according
to the new configuration.
The only thing clients have to do is to provide the
ConfigurableComponent with an IConfigurable and IConfigurator
implementation containing the business logic specifics.
Tired of abstract formulation already? Let's take a look at how it's done in a real application. First, we need to configure the component (just like any other) in an XML file. Since the ConfigurableComponent supports content editing, clients can also provide the appropriate command handler etc. to handle the changes.
<!-- Basic properties just like for any other edit component --> <component class="com.top_logic.project.pos.layout.configurable.ConfigurableComponent" name="MyConfigurableComponent" page="/jsp/myConfigurable.jsp" resPrefix="my.configurable.resource.prefix." buttonComponent="MyButtonComponent" isSecurityMaster="true" <!-- These are the names of the command handlers (which has to be registered in the CommandHandlerFactory on system startup) we're going to use for our editing capabilities. Clients might decide to leave it blank in order to suppress editing capabilities. --> applyHandlerName="myApplyChangesHandler" deleteHandlerName="myDeleteContentHandler"> <!-- we're going to use this one to check what model object's we actually support --> <inputFilter class="com.example.MyElementSelector" /> <!-- This is our configuration provider implementation --> <configurator class="com.example.MyConfigurator" name="MyConfigurator" resPrefix="my.configurator.resource.prefix." /> <!-- And this is the configurable implementation --> <configurable class="com.example.MyConfigurable" name="MyConfigurable" resPrefix="my.configurable.resource.prefix." /> </component> <!-- Since we need the editing capability, we also provide the button component just like for any other edit component here. --> <component class="com.example.myButtonComponent" target="MyConfigurableComponent" name="MyButtonComponent" />
The implementation itself is pretty straight forward.
/*
* Copyright (c) 2009 Business Operation Systems GmbH. All Rights Reserved
*/
package com.example;
import java.util.Arrays;
import java.util.Map;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import com.top_logic.layout.basic.Command;
import com.top_logic.layout.form.FormField;
import com.top_logic.layout.form.ValueListener;
import com.top_logic.layout.form.model.FormContext;
import com.top_logic.layout.form.model.SelectField;
/**
* A class demonstrating a possible client implementation for a
* {@link ConfigurableComponent}.
*
* @history 17.12.2009 wta created
* @author wta
*/
public class MyConfigurator extends AbstractConfigurator {
/**
* The name of our example property (free to rhyme here).
*/
private static final String PROPERTY_EXAMPLE = "MyProperty";
/**
* We're going to attach this one to the members in order to resolve which
* field changed their values and notify all others about configuration
* changes.
*/
private final ValueListener listener = new ValueListener() {
/*
* @see
* com.top_logic.layout.form.ValueListener#valueChanged(com.top_logic
* .layout.form.FormField, java.lang.Object, java.lang.Object)
*/
public void valueChanged(final FormField field, final Object oldValue, final Object newValue) {
// this will cause the basic implementation (AbstractConfigurator)
// to fire the appropriate event thus notifying the appropriate IConfigurable
// of property changes.
MyConfigurator.this.setProperty(field.getName(), newValue);
}
};
/**
* Just create a configurator based on the abstract implementation.
*
*/
public MyConfigurator(final Attributes attributes) throws SAXException {
super(attributes);
}
/*
* @see com.top_logic.layout.form.FormHandler#getApplyClosure()
*/
public Command getApplyClosure() {
// nothing to do here
return null;
}
/*
* @see com.top_logic.layout.form.FormHandler#getDiscardClosure()
*/
public Command getDiscardClosure() {
// nothing to do here
return null;
}
/*
* @see
* com.top_logic.project.pos.layout.configurable.AbstractComponentPart#createFormContext
* ()
*/
@Override
protected FormContext createFormContext() {
// create a new form context here and fill it with members.
final FormContext context = new FormContext("MyConfigurator", getResourcePrefix());
final SelectField selector = new SelectField(PROPERTY_EXAMPLE, Arrays.asList("one", "two", "three"));
context.addMember(selector);
return context;
}
/*
* @see
* com.top_logic.project.pos.layout.configurable.AbstractConfigurator#initialize(java
* .util.Map)
*/
@Override
protected void initialize(final Map properties) {
// initial property values go here.
properties.put(PROPERTY_EXAMPLE, "one");
}
}
/*
* Copyright (c) 2009 Business Operation Systems GmbH. All Rights Reserved
*/
package com.example;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import com.top_logic.layout.basic.Command;
import com.top_logic.layout.form.FormField;
import com.top_logic.layout.form.model.FormContext;
import com.top_logic.layout.form.model.StringField;
/**
* A class demonstrating a possible client implementation for a
* {@link ConfigurableComponent}.
*
* @history 17.12.2009 wta created
* @author wta
*/
public class MyConfigurable extends AbstractConfigurable {
/**
* The name of the viewer to display the example property (free to rhyme
* here)
*/
private static final String EXAMPLE_VIEWER = "MyViewer";
/**
* Just create a configurable based on the abstract implementation.
*/
public MyConfigurable(Attributes attributes) throws SAXException {
super(attributes);
}
/*
* @see com.top_logic.project.pos.layout.configurable.IConfigurationListener#configurationChanged(com.top_logic.project.pos.layout.configurable.ConfigurationEvent)
*/
public void configurationChanged(final ConfigurationEvent event) {
// okay, now THAT is the one we've been waiting for!
// here, we can check what property changed and react to changes.
for(final String propertyName : event.getPropertyNames()) {
// we react to changes to the example property only
if("MyProperty".equals(propertyName)) {
final Object value = event.getSource().getProperty(propertyName);
getFormContext().getField(EXAMPLE_VIEWER).setValue(value.toString());
}
}
}
/*
* @see com.top_logic.layout.form.FormHandler#getApplyClosure()
*/
public Command getApplyClosure() {
// nothing to do here
return null;
}
/*
* @see com.top_logic.layout.form.FormHandler#getDiscardClosure()
*/
public Command getDiscardClosure() {
// nothing to do here as well.
return null;
}
/*
* @see
* com.top_logic.project.pos.layout.configurable.AbstractComponentPart#createFormContext
* ()
*/
@Override
protected FormContext createFormContext() {
// create a new form context here and fill it with members.
final FormContext context = new FormContext("MyConfigurable", getResourcePrefix());
final StringField field = new StringField(EXAMPLE_VIEWER);
context.addMember(field);
return context;
}
/*
* @see
* com.top_logic.project.pos.layout.configurable.AbstractComponentPart#inputChanged(
* java.lang.Object, java.lang.Object)
*/
@Override
protected void inputChanged(final Object oldInput, final Object newInput) {
// this one is called when the ConfigurableComponent's input changes.
// We might want to clear our viewer?
if(hasFormContext()) {
final FormContext context = getFormContext();
final FormField field = context.getField(EXAMPLE_VIEWER);
field.setDefaultValue("INITIALIZE ME!");
field.reset();
}
}
}
/*
* Copyright (c) 2009 Business Operation Systems GmbH. All Rights Reserved
*/
package com.example;
import com.top_logic.basic.col.Filter;
/**
* A class demonstrating a possible client implementation for a
* {@link ConfigurableComponent}.
*
* @history 17.12.2009 wta created
* @author wta
*/
public class MyElementSelector implements Filter {
/*
* @see com.top_logic.basic.col.Filter#accept(java.lang.Object)
*/
public boolean accept(final Object anObject) {
// life's like a box of chocolates... you never know what's inside. :)
return true;
}
}