Anees has posted 4 posts at DZone. View Full User Profile

Domain Driven Swing Framework

01.18.2010
| 2929 views |
  • submit to reddit
I am going to introduce a domain driven framework and will explain how we can use it. The purpose of this framework was to ease development of the applications based on Swing framework. Today I launched its first milestone version.The framework can be downloaded from http://code.google.com/p/open-domain-driven-swing/downloads/list

The user just needs to create the domain objects and with the help of JPA annotations, and customized annotations, the user will define the structure of the form to be generated. I will proceed with the example which will help the user of this framework to generate the form.
/**
* @author Anees
*
*/
@Entity
@Table(name="dd_user")
@DomainTitle(title = "User Information")
public class UserDomain implements Serializable {

private static final long serialVersionUID = 1L;

private Integer id;
private String username;
private String password;

private String firstName;
private String middleName;
private String lastName;

private String address;

private Boolean testingCheckBoxes;

private String testingRadioButton;

private Date testingUserDate;

private UserTitlesDomain userTitle;

private List<userrolesdomain> userRoles;

private UserRanksDomain userRank;

private CountryDomain country;

private CityDomain city;

private UserEducationDomain userEducation;

private List<usergroupsdomain> userGroups;

private String stcPanelText;



/**
* @return the id
*/
@Id
@Column(name="id", length = 10)
@UIBindingComponent(label="ID", property="id", row="1" ,column="1")
public Integer getId() {
return id;
}

/**
* @param id the id to set
*/
public void setId(Integer id) {
this.id = id;
}

/**
* @return the username
*/
@Column(name="userName", length = 10)
@UiComponentPosition(positionColumn = "1", positionRow = "2", tableDisplayColumn= "1")
@PropertyConstraints(isMandatory = MandatoryType.TURE, maxLength = "8",
inputCaseType = StringInputCaseType.UPPERCASE, textInputDataType = TextFiledInputDataType.ONLYALPHA)
public String getUsername() {
return username;
}

/**
* @param username the username to set
*/
public void setUsername(String username) {
this.username = username;
}

/**
* @return the password
*/
@Column(name="password", length = 10)
@UIBindingComponent(label="Password", property="password", type= "pswd", row="3" ,column="1")
public String getPassword() {
return password;
}

/**
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}

/**
* @return the firstName
*/
@Column(name="firstName", length = 10)
@UiComponentPosition(positionColumn = "1", positionRow = "4", tableDisplayColumn ="2")
public String getFirstName() {
return firstName;
}

/**
* @param firstName the firstName to set
*/
public void setFirstName(String firstName) {
this.firstName = firstName;
}

/**
* @return the middleName
*/
@Column(name="middleName", length = 5)
@UiComponentPosition(positionColumn = "2", positionRow = "4", tableDisplayColumn = "3")
public String getMiddleName() {
return middleName;
}

/**
* @param middleName the middleName to set
*/
public void setMiddleName(String middleName) {
this.middleName = middleName;
}

/**
* @return the lastName
*/
@Column(name="lastName", length = 10)
@UiComponentPosition(positionColumn = "3", positionRow = "4")
public String getLastName() {
return lastName;
}

/**
* @param lastName the lastName to set
*/
public void setLastName(String lastName) {
this.lastName = lastName;
}

/**
* @return the address
*/
@Column(name="address", length = 10)
@UiComponentPosition(positionColumn = "1", positionRow = "6")
@UIComponentInterbinding(parentProperty = "city", childProperty="address")
public String getAddress() {
return address;
}

/**
* @param address the address to set
*/
public void setAddress(String address) {
this.address = address;
}

/**
* @return the userTitle
*/
@UiComponentPosition(positionColumn = "1", positionRow = "5", tableDisplayColumn = "4")
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = UserTitlesDomain.class)
@JoinColumn(name = "title", referencedColumnName = "id")
public UserTitlesDomain getUserTitle() {
return userTitle;
}

/**
* @param userTitle the userTitle to set
*/
public void setUserTitle(UserTitlesDomain userTitle) {
this.userTitle = userTitle;
}

/**
* @return the userRoles
*/
// @UnBound
@UiComponentPosition(positionRow="15", positionColumn="1")
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = UserRolesDomain.class)
@JoinColumns(value = { @JoinColumn(name = "userID", referencedColumnName = "id"),
@JoinColumn(name = "roleID", referencedColumnName = "id")})
public List<userrolesdomain> getUserRoles() {
return userRoles;
}

/**
* @param userRoles the userRoles to set
*/
public void setUserRoles(List<userrolesdomain> userRoles) {
this.userRoles = userRoles;
}

/**
* @return the userRank
*/
@UiComponentPosition(positionColumn = "1", positionRow = "7")
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = UserRanksDomain.class)
@JoinColumn(name = "rank", referencedColumnName = "id")
public UserRanksDomain getUserRank() {
return userRank;
}

/**
* @param userRank the userRank to set
*/
public void setUserRank(UserRanksDomain userRank) {
this.userRank = userRank;
}

/**
* @return the country
*/
@UiComponentPosition(positionColumn = "1", positionRow = "8")
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = CountryDomain.class)
@JoinColumn(name = "country", referencedColumnName = "id")
public CountryDomain getCountry() {
return country;
}

/**
* @param country the country to set
*/
public void setCountry(CountryDomain country) {
this.country = country;
}

/**
* @return the city
*/
// TODO: I have other option to play with ManyToOne annotation which has the targetEntity
// ************* =============== SOLUTION 1 ================ *********************
// Step 1: I will check the all OneToMany relationships in that Entity
// Step 2: I will check those properties have OneToMany Relationship are present in this domain object
// Step 3: I will check whether that property has unBound annotation
// Step 4: If that property has not unBound annotation then I will associate IAction class having
// InterdependentActionProcessor to the original property which will drive the child entity
// ************ =============== SOLUTION 2 ================= *********************
// Step 1: I will add @UIComponentInterbinding which will has InterdependentActionProcessor and create listener
// for parent property to drive the child property
@UIComponentInterbinding(parentProperty = "country", childProperty="city")//TODO: have to think more on this issue
@UiComponentPosition(positionColumn = "2", positionRow = "8")
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity=CityDomain.class)
@JoinColumn(name="city", referencedColumnName = "id")
public CityDomain getCity() {
return city;
}

/**
* @param city the city to set
*/
public void setCity(CityDomain city) {
this.city = city;
}

/**
* @return the userEducation
*/
@UiComponentPosition(positionColumn = "1", positionRow = "9")
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = UserEducationDomain.class)
@JoinColumn(name = "education", referencedColumnName = "id")
public UserEducationDomain getUserEducation() {
return userEducation;
}

/**
* @param userEducation the userEducation to set
*/
public void setUserEducation(UserEducationDomain userEducation) {
this.userEducation = userEducation;
}



/**
* @return the userGroups
*/
// @UnBound
@UiComponentPosition(positionRow="18", positionColumn="1")
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = UserGroupsDomain.class)
@JoinColumns(value = { @JoinColumn(name = "userID", referencedColumnName = "id"),
@JoinColumn(name = "groupID", referencedColumnName = "id")})
public List<usergroupsdomain> getUserGroups() {
return userGroups;
}

/**
* @param userGroups the userGroups to set
*/
public void setUserGroups(List<usergroupsdomain> userGroups) {
this.userGroups = userGroups;
}

/**
*
* @return
*/
@Transient
@UiComponentPosition(positionColumn = "1", positionRow = "13")
public Boolean getTestingCheckBoxes() {
return testingCheckBoxes;
}

/**
*
* @param testingCheckBoxes
*/
public void setTestingCheckBoxes(Boolean testingCheckBoxes) {
this.testingCheckBoxes = testingCheckBoxes;
}

/**
*
* @return
*/
@RadioButtons(values={@RadioButton(choice="O", label="One"),
@RadioButton(choice = "T", label="Two"),
@RadioButton(choice="F", label="Four"),
@RadioButton(choice="X", label="Five")})
@UiComponentPosition(positionColumn = "1", positionRow = "12")
public String getTestingRadioButton() {
return testingRadioButton;
}

/**
*
* @param testingRadioButton
*/
public void setTestingRadioButton(String testingRadioButton) {
this.testingRadioButton = testingRadioButton;
}

/**
*
* @return
*/
@UiComponentPosition(positionColumn = "1", positionRow = "11")
public Date getTestingUserDate() {
return testingUserDate;
}

/**
*
* @param testingUserDate
*/
public void setTestingUserDate(Date testingUserDate) {
this.testingUserDate = testingUserDate;
}

/**
* @return the userComponent
*/
@UnBound
@Transient
@StaticPresentation(userPanel=TestingPanel.class)
@UiComponentPosition(positionColumn = "1", positionRow = "10")
public void getUserComponent() {}

/**
* @return the stcPanelText
*/
@Transient
// @UnBound
@UiComponentPosition(positionColumn = "1", positionRow = "20")
@StaticPresentation(userPanel=TestingPanel.class)
public String getStcPanelText() {
return stcPanelText;
}

/**
* @param stcPanelText the stcPanelText to set
*/
public void setStcPanelText(String stcPanelText) {
this.stcPanelText = stcPanelText;
}
}
This framework will detect the return type of the method and based upon the return type it will generate the UI component. The length of the property will define the maximum length constraint of that specific component.

If the return type is “String” the framework will generate Text Compoenent unless defined Text Area or Password Field or RadioButton as annotations.
If the return type is Boolean it will generate the CheckBox. If the return type has Obect to return with the annotation @OneToMany the system will generate JTable, having the column based on the target Entity.
For @OneToOne relation the system will generate the TextField with uneditable mode unless specified and for @ManyToOne the system will generate JCombobox of the Target Entity.

@UiComponentPosition(positionColumn = "x", positionRow = "y") defines the position of the Component.
@UnBound annotation makes the property unavailable for all use cases. If the user wish to use this property in some use cases and don’t want to use in other it is advised to use this annotation carefully.
@UIComponentInterbinding(parentProperty = "country", childProperty="city") annotation helps the inter component binding.
@PropertyConstraints(isMandatory = MandatoryType.TURE, maxLength = "8", inputCaseType = StringInputCaseType.UPPERCASE, textInputDataType = TextFiledInputDataType.ONLYALPHA) annotation is used to define the component validation criteria.
Now the process of defining the structure of the UI Form It is very easy to generate the UI Form with this framework as Domain Object has all the necessary information for the form structure. Lets see one example which will help you to understand.

/**
* @author Anees
*
*/
@DomainObjects(values={@DomainObject(domainObject=UserDomain.class, position="1"),
@DomainObject(domainObject=UserGroupsDomain.class, position = "2")})

@ExceptDomainObjectPropertyPolicy(domainObject=UserDomain.class,
genralPolicy= DomainPerpertiesAllowType.ALLOW_ALL ,properties = { @DomainProperties(property="id") } )
public class UserProfileForm extends BaseComponentBuilder {

public final static String USE_CASE = UserProfileForm.class.getName();

@Override
public String getComponentIdentity() {
return USE_CASE;
}


@Override
public String getFormTitle() {
return "User Profile Form";
}
}
@DomainObjects(values={@DomainObject(domainObject=UserDomain.class, position="1"),@DomainObject(domainObject=UserGroupsDomain.class, position = "2")}) annotation defines the usage of the domain object. Position will specified the location the form based on this domain object. The position is the Tab position as each domain object creates the form in its own tab.

@ExceptDomainObjectPropertyPolicy(domainObject=UserDomain.class, genralPolicy= DomainPerpertiesAllowType.ALLOW_ALL ,properties = { @DomainProperties(property="id") } ) annotation will used to restrict the form generation. If we want to avoid any property to appear on the form we have two option one is to make it @UnBound or we need to specify in this annotation.

Apart from that we just need to create service folder in META-INF and then create a simple text file by the name of com.eagle.coders.swing.core.ui.interfaces.IComponent and inside this file we need the name of the all its implementations classes

@Actions(actions = { @Action(actionType=ActionTypes.SUBMIT_ACTION, exeution=SubmitUserForm.class) })Can be used to define the action associated with the usecase. The usecase must be annotated with this annotation. The execution parameter need the instance of IExecution to be passed. The usecase creator need to create the object of IExecution and provide the usecase specific implementation.

Now user need to use UIManager class to get the generated forms. The usage of UIManager is as follows:

UIManager.getUsecaseByKey(String componentID)

In this method we need to pass the usecaseID of the usecase. This will return Framework which is a full frame consist of Search, Submit, Cancel and Delete action

UIManager. getUsecaseByComponent( … )

In this method the user need to pass the instance of IComponent i.e. the instance of Usecase. Framework which is a full frame consist of Search, Submit, Cancel and Delete action

UIManager.getAllForms()

This method will return List of Frames present in the class path of this framework

UIManager. GetPanelByKey(String componentID);

This method will return JPanel by passing the usecase ID of the usecase Best of luck and wish you all enjoy my effort to make this framework one of the state of art Swing framework
0
Published at DZone with permission of its author, Anees Ur-rehman.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Peter Karussell replied on Mon, 2010/01/18 - 4:14am

How is that different to metawidget.org ?

And is it possible to communicate from the application to the database over http?

Anees Ur-rehman replied on Mon, 2010/01/18 - 4:47am in response to: Peter Karussell

metawidget what i believe creates only subcomponents based on bean not the whole form. This framework generate the whole form not just widget, bind with back end provide the validation etc. to communicate with database over http not yet provided

Nicolas de Pomereu replied on Wed, 2010/01/20 - 6:48am in response to: Peter Karussell

Peter, if you are interested we are going to release soon - mid-february - a framework that allows JDBC over HTTP. (It will be Open Source with LGPL license). We can send you a BETA version, just let me know.

Anees Ur-rehman replied on Thu, 2010/01/21 - 1:39am in response to: Nicolas de Pomereu

Nicolas, I will be please to have your beta version. let me know when it is ready.

Peter Karussell replied on Thu, 2010/01/21 - 7:59am in response to: Nicolas de Pomereu

> Peter, if you are interested

Yes I am.

> We can send you a BETA version

This would be nice of course.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.