I am trying to convert my class to support builder in order to prettify my code, this is the code I am using and I try to define my method called addSMTPIntegration to use builder.
this is my class:
public class IntegrationsPage extends SettingsTab {
private static final By newIntegrationBth = Locators.findBy("settings_page_integrations_page_add_new_button");
private IntegrationsTable integrationsTable;
private SmtpIntegrationForm smtpIntegrationForm;
private ConfirmPopup confirmPopup;
public IntegrationsPage(DriverWrapper driver){
super(driver, "integrations",newIntegrationBth);
integrationsTable = new IntegrationsTable(driver);
smtpIntegrationForm = new SmtpIntegrationForm(driver);
confirmPopup = new ConfirmPopup(driver);
}
public void addSMTPIntegration(String name, String server, String port, String fromAddress, boolean mode, String userName, String password){
clickNewIntegrationButton();
smtpIntegrationForm.chooseIntegration(IntegrationType.SMTP);
smtpIntegrationForm.setIntegrationName(name);
smtpIntegrationForm.setIntegrationServer(server);
smtpIntegrationForm.setIntegrationPort(port);
smtpIntegrationForm.setIntegrationFromAddress(fromAddress);
smtpIntegrationForm.setIntegrationAuth(mode);
smtpIntegrationForm.setIntegrationUserName(userName);
smtpIntegrationForm.setIntegrationPassword(password);
smtpIntegrationForm.clickSaveButton();
LOG.i("SMTP configuration passed successfully");
}
private void clickNewIntegrationButton(){
clickButton(newIntegrationBth);
}
public IntegrationsRow waitIntegrationRowTable(String configurationName) {
return integrationsTable.waitRowDisplay(configurationName);
}
public boolean deleteIntegration(String integrationName) {
integrationsTable.findRow(integrationName).clickRow();
integrationsTable.delete(integrationName);
confirmPopup.clickYes();
return integrationsTable.findRow(integrationName) == null;
}
}
I am trying to define my addSMTPIntegration method to support builder in a manner that I would be able to build it in the following way:
smtpIntegrationForm.chooseIntegration(IntegrationType.SMTP).setIntegrationName(name).setIntegrationServer(server).... etc
This is my smtpIntegrationForm class:
public class SmtpIntegrationForm extends IntegrationCommonSection {
private static final By integrationServerBy = Locators.findBy("settings_page_integrations_page_integration_server_name_txt");
private static final By integrationPortBy = Locators.findBy("settings_page_integrations_page_integration_port_txt");
private static final By integrationFromAddressBy = Locators.findBy("settings_page_integrations_page_integration_from_address_txt");
SmtpIntegrationForm(DriverWrapper driver){
super(driver);
}
void setIntegrationServer(String server){
setText(integrationServerBy, server);
}
void setIntegrationPort(String port){
setText(integrationPortBy, port);
}
void setIntegrationFromAddress(String address){
setText(integrationFromAddressBy, address);
}
void chooseIntegration(IntegrationType integrationType){
clickButton(By.cssSelector("li[class~='qa_" + integrationType.value + "']"));
}
and this is IntegrationCommonSection class:
class IntegrationCommonSection extends PageElement {
private static final By integrationNameBy = Locators.findBy("settings_page_integrations_page_integration_name_txt");
private static final By integrationAuthBy = Locators.findBy("settings_page_integrations_page_integration_auth_bth");
private static final By integrationUserNameBy = Locators.findBy("settings_page_integrations_page_integration_username_txt");
private static final By integrationPasswordBy = Locators.findBy("settings_page_integrations_page_integration_password_txt");
private static final By integrationSaveBthBy = Locators.findBy("settings_page_integrations_page_integration_save_bth");
private static final By integrationTestBthBy = Locators.findBy("settings_page_integrations_page_integration_test_bth");
IntegrationCommonSection(DriverWrapper driver){
super(driver);
}
void setIntegrationName(String name){
clearAndSetCharacters(integrationNameBy, name);
}
void setIntegrationAuth(boolean mode){ //true - with auth, false - no auth
if(!isCheckBoxEnabled(integrationAuthBy) && mode) {
clickButton(integrationAuthBy);
}
}
void setIntegrationUserName(String userName){
setText(integrationUserNameBy, userName);
}
void setIntegrationPassword(String password){
setText(integrationPasswordBy, password);
}
void clickSaveButton(){
clickButton(integrationSaveBthBy);
}
void clickTestButton(){
clickButton(integrationTestBthBy);
}
}
just return "this" :
SmtpIntegrationForm setIntegrationServer(String server){
setText(integrationServerBy, server);
return this;
}
In order to do that, you need to modify the methods of SmtpIntegrationForm with the return of this object so that you can construct the statements in a builder pattern. There is nothing you can do in addSMTPIntegration() method to achieve this.
chooseIntegration(), setIntegrationName(), setIntegrationServer(), etc. methods inside SmtpIntegrationForm should have return type of SmtpIntegrationForm and the last statement in these methods should be return this; in order for you to achieve this.
Make the changes as:
public class SmtpIntegrationForm extends IntegrationCommonSection {
private static final By integrationServerBy = Locators.findBy("settings_page_integrations_page_integration_server_name_txt");
private static final By integrationPortBy = Locators.findBy("settings_page_integrations_page_integration_port_txt");
private static final By integrationFromAddressBy = Locators.findBy("settings_page_integrations_page_integration_from_address_txt");
SmtpIntegrationForm(DriverWrapper driver){
super(driver);
}
SmtpIntegrationForm setIntegrationServer(String server){
setText(integrationServerBy, server);
return this;
}
SmtpIntegrationForm setIntegrationPort(String port){
setText(integrationPortBy, port);
return this;
}
SmtpIntegrationForm setIntegrationFromAddress(String address){
setText(integrationFromAddressBy, address);
return this;
}
SmtpIntegrationForm chooseIntegration(IntegrationType integrationType){
clickButton(By.cssSelector("li[class~='qa_" + integrationType.value + "']"));
return this;
}
}
Related
I tried to instantiate a class with other class A using setter and fetch variable to class B using getter, but it return null. I understand, since I created new instance of class, thats why it is null. What can be other approach?
public class ContextBrowser {
String browser;
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
}
public class SetBrowser{
public void setCurrentBrowser(String browser){
ContextBrowser contextBrowser = new ContextBrowser();
contextBrowser.setBrowser(browser);
}
}
public class getBrowser{
public String readBrowser(){
ContextBrowser contextBrowser = new ContextBrowser();
return contextBrowser.getBrowser()
}
}
public class ContextBrowser {
/*
Singleton class
*/
private String browser;
private static ContextBrowser contextBrowserInstance;
// private constructor
private ContextBrowser() {}
public static ContextBrowser getContextBrowserInstance() {
if(contextBrowserInstance == null) {
contextBrowserInstance = new ContextBrowser();
}
return contextBrowserInstance;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
}
public class SetBrowser{
public void setCurrentBrowser(String browser){
ContextBrowser contextBrowser = ContextBrowser.getContextBrowserInstance();
contextBrowser.setBrowser(browser);
}
}
public class GetBrowser{
public String readBrowser(){
ContextBrowser contextBrowser = ContextBrowser.getContextBrowserInstance();
return contextBrowser.getBrowser();
}
}
Improved code with thread safe class:
public class ContextBrowser {
private String browser;
private ContextBrowser() {}
public static synchronized ContextBrowser getInstance(){
return instance.get();
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
private static ThreadLocal<ContextBrowser> instance = new ThreadLocal() {
#Override
protected ContextBrowser initialValue() {
return new ContextBrowser();
}
};
}
I have two classes:
public class UnoLoginPageUi {
public final Input username = new Input("id=username");
public final Input password = new Input("id=password");
public final Button loginButton = new Button("name=login");
}
and
public class DuoLoginPageUi {
public final Input username = new Input("id=usernameField");
public final Input password = new Input("id=passwordField");
public final Button loginButton = new Button("id=submitButton");
}
and in one common class I want to make something like that:
public void loginUsingUsernameAndPassword(String username, String password, String pageType) {
getUi(pageType).username.waitForToBeDisplayed();
getUi(pageType).username.setValue(username);
getUi(pageType).password.setValue(password);
getUi(pageType).loginButton.click();
}
where getUi() is a method that gas argument pageType (which is UNO or DUO).
private Class getUi(String pageType) {
if (pageType.equals("UNO")) {
return new DuoLoginPageUi();
}
else if (pageType.equals("DUO")) {
return new UnoLoginPageUi;
}
return null;
}
However it doesn't work as this method need to in type of this two pages with selectors - how to deal with that ?
You can create a interface called LoginPageUi. And let your UnoLoginPageUi and DuoLoginPageUi implement that interface.
Then your getUi method will be private LoginPageUi getUi(String pageType).
Off topic: I would recommend to implement an enum instead of String pageType.
Create a common abstraction for the two classes
public abstract class LoginPageUi {
public final Input username = new Input("id=username");
public final Input password = new Input("id=password");
public final Button loginButton = new Button("name=login");
}
and have UnoLoginPageUi and DuoLoginPageUi extend that
public class UnoLoginPageUi extends LoginPageUi {
public static String getPageType() { return "UNO"; }
}
public class DuoLoginPageUi extends LoginPageUi {
public static String getPageType() { return "DUO"; }
}
The method would return the common abstraction
private LoginPageUi getUi(String pageType) {
if (pageType.equals(DuoLoginPageUi.getPageType())) {
return new DuoLoginPageUi();
}
else if (pageType.equals(UnoLoginPageUi.getPageType())) {
return new UnoLoginPageUi;
}
return null;
}
I also hope you realize that every time you call getUi(pageType) it is returning a new instance. by the time you call getUi(pageType).loginButton.click(); the instance returned has no values set.
Refactor:
public void loginUsingUsernameAndPassword(String username, String password, String pageType) {
LoginPageUi ui = getUi(pageType);
if (ui != null) {
ui.username.waitForToBeDisplayed();
ui.username.setValue(username);
ui.password.setValue(password);
ui.loginButton.click();
}
}
create Parent class or interface for both called UI:
public abstract class Ui{
}
public interface Ui{
}
and extend it:
public class UnoLoginPageUi extends Ui{
public final Input username = new Input("id=username");
public final Input password = new Input("id=password");
public final Button loginButton = new Button("name=login");
}
public class DuoLoginPageUi extends Ui {
public final Input username = new Input("id=usernameField");
public final Input password = new Input("id=passwordField");
public final Button loginButton = new Button("id=submitButton");
}
or
public class UnoLoginPageUi implements Ui{
public final Input username = new Input("id=username");
public final Input password = new Input("id=password");
public final Button loginButton = new Button("name=login");
}
public class DuoLoginPageUi implements Ui {
public final Input username = new Input("id=usernameField");
public final Input password = new Input("id=passwordField");
public final Button loginButton = new Button("id=submitButton");
}
then return parent reference as:
private Ui getUi(String pageType) {
if (pageType.equals("UNO")) {
return new DuoLoginPageUi();
}
else if (pageType.equals("DUO")) {
return new UnoLoginPageUi;
}
return null;
}
Suppose I have a Crypto Class.
public class Crypto{
// This method returns the instance of Crypto class for the key.
// If instance for key hasn't been created a new instance is created.
// If already created for key the same instance is returned.
public static Crypto getInstance(String key){
}
}
How do I implement this pattern? I mean this is singleton design pattern but different instances for different keys and where do I save the instance?
I think you can use a map :
Class Crypto{
Map<String,Crypto> map = new HashMap<String,Crypto>();
private Crypto(){
}
public static Crypto getInstance(String key){
if(map.contains(key)){
return map.get(key);
}
else{
// switch on the key and create your Cryptos
map.put(key,new Crypto();
}
return map.get(key);
}
}
Sounds like you want the multiton pattern. This is a simple example for an implicit synchronized version:
import java.util.HashMap;
import java.util.Map;
public class Multiton {
private static final class MultitonHolder {
static final Map<String, Multiton> INSTANCES = new HashMap<>();
static {
INSTANCES.put("key-1", new Multiton());
INSTANCES.put("key-2", new Multiton());
INSTANCES.put("key-3", new Multiton());
INSTANCES.put("key-4", new Multiton());
INSTANCES.put("key-5", new Multiton());
}
}
private Multiton() {}
public static Multiton getInstance(final String key) {
return MultitonHolder.INSTANCES.get(key);
}
}
Example with additional fields:
import java.util.HashMap;
import java.util.Map;
public class Multiton {
private static class MultitonHolder {
static final Map<String, Multiton> INSTANCES = new HashMap<>();
static {
INSTANCES.put("key-1", new Multiton(1));
INSTANCES.put("key-2", new Multiton(2));
final Multiton multiton3 = new Multiton(3);
multiton3.setAnotherField(3);
INSTANCES.put("key-3", multiton3);
final Multiton multiton4 = new Multiton(4);
multiton3.setAnotherField(4);
INSTANCES.put("key-4", multiton4);
}
}
private final int someField;
private int anotherField;
private Multiton(final int someField) {
this.someField = someField;
}
public static Multiton getInstance(final String key) {
return MultitonHolder.INSTANCES.get(key);
}
public int getSomeField() {
return someField;
}
private void setAnotherField(final int anotherField) {
this.anotherField = anotherField;
}
public int getAnotherField() {
return anotherField;
}
}
Similar with the above examples
public class Crypto {
private ArrayList<String> keyList = new ArrayList<String>();
private static Crypto uniqInstance;
private Crypto() {
}
public static Crypto getInstance(){
if (uniqInstance==null){
synchronized (Crypto.class){
if(uniqInstance==null){
uniqInstance = new Crypto();
}
}
}
return uniqInstance;
}
public void addKey(String key){
this.keyList.add(key);
}
public String toString(){
String s="";
for(String str:keyList){
s+=str+" ";
}
return s;
}
}
I tested it with the following:
public class CryptoApp {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Crypto crypto;
crypto = Crypto.getInstance();
crypto.addKey("my123");
crypto.addKey("my456");
System.out.println(crypto);
Crypto crypto2;
crypto2 = Crypto.getInstance();crypto2.addKey("my789");System.out.println(crypto2);
}
}
I have been struggling to find a good way of implementing PubSub with Autobahn for android. I am currenty using the Singleton pattern to use the same AutobahnConnection in my whole app. I got the calls and subscribing working but when i unsubscribe and then come back to the same fragment and try to subscribe again it doesnt work. Below my current Autobahn Class:
package nl.w3s.hulpverlener.utils;
import nl.w3s.hulpverlener.helper.DebugHelper;
import android.util.Log;
import de.tavendo.autobahn.Autobahn;
import de.tavendo.autobahn.Autobahn.SessionHandler;
import de.tavendo.autobahn.AutobahnConnection;
import de.tavendo.autobahn.AutobahnOptions;
public final class AutobahnService{
private static AutobahnService INSTANCE;
private static AutobahnConnection connection;
private AutobahnOptions options;
private boolean connected = false;
private String url = "http://johelpen.w3s.nl/";
private String websocketUrl;
private AutobahnService() {
connection = new AutobahnConnection();
options = new AutobahnOptions();
options.setReceiveTextMessagesRaw(true);
websocketUrl = CommonUtilities.STAGING_WEBSOCKET_URL;
connect();
}
public static AutobahnService getInstance() {
if(INSTANCE == null)
INSTANCE = new AutobahnService();
else
INSTANCE.connect();
return INSTANCE;
}
public void connect() {
if(!connection.isConnected()) {
connection.connect(websocketUrl, new SessionHandler() {
#Override
public void onOpen() {
connected = true;
Log.i(DebugHelper.TAG_DEBUG, "CONNECTED");
}
#Override
public void onClose(int p_intCode, String p_strReason) {
connected = false;
Log.i(DebugHelper.TAG_DEBUG, "DISCONNECTED");
}
}, options);
}
}
public void doCall(final String callType, final Class<?> classRef, final Autobahn.CallHandler autobahnEventHandler, final Object... arguments) {
connection.call(url + "#" + callType, classRef, autobahnEventHandler, arguments);
}
public void doSubscribe(final String callType, final Class<?> classRef, final Autobahn.EventHandler autobahnEventHandler) {
connection.subscribe(url + callType, classRef, autobahnEventHandler);
}
public void doUnsubscribe(final String callType) {
connection.unsubscribe(url + callType);
}
}
When I look at my logs it doesnt disconnect while unsubscribing and resubscribing.
How can I use generics propery in my particular case? The code first, then the explanation:
AbstractConstraint.java
public abstract class AbstractConstraint {
public abstract Constraint[] getConstraints();
}
AccountConstraint.java
public class AccountConstraint extends AbstractConstraint {
private Constraint<Range<Integer>> accountIdConstraint;
private Constraint<String> usernameConstraint;
private Constraint<String> passwordConstraint;
private Constraint<String> emailConstraint;
private AccountConstraint(Builder builder) {
this.accountIdConstraint = builder.accountIdConstraint;
this.usernameConstraint = builder.usernameConstraint;
this.passwordConstraint = builder.passwordConstraint;
this.emailConstraint = builder.emailConstraint;
}
#Override
public Constraint[] getConstraints() {
return new Constraint[] {
this.accountIdConstraint,
this.usernameConstraint,
this.passwordConstraint,
this.emailConstraint
};
}
public static class Builder extends ConstraintBuilder<AccountConstraint> {
private Constraint<Range<Integer>> accountIdConstraint;
private Constraint<String> usernameConstraint;
private Constraint<String> passwordConstraint;
private Constraint<String> emailConstraint;
public Builder() {
this.accountIdConstraint = null;
this.usernameConstraint = null;
this.passwordConstraint = null;
this.emailConstraint = null;
init();
}
public Builder accountId(final int val) {
this.accountIdConstraint = new Constraint<>(operation, truthed, new Range<>(val), "accountId");
return this;
}
public Builder accountId(final int min, final int max) {
this.accountIdConstraint = new Constraint<>(operation, truthed, new Range<>(min, max), "accountId");
return this;
}
public Builder accountId(final Range<Integer> accountId) {
this.accountIdConstraint = new Constraint<>(operation, truthed, accountId, "accountId");
return this;
}
public Builder username(final String username) {
this.usernameConstraint = new Constraint<>(operation, truthed, username, "username");
return this;
}
public Builder email(final String email) {
this.emailConstraint = new Constraint<>(operation, truthed, email, "email");
return this;
}
#Override
public AccountConstraint build() {
return new AccountConstraint(this);
}
}
}
ConstraintBuilder.java
public abstract class ConstraintBuilder<T> {
protected boolean truthed;
protected Operation operation;
protected void init() {
truthed = true;
operation = Operation.IS;
}
public ConstraintBuilder not() {
truthed = false;
return this;
}
public ConstraintBuilder like() {
operation = Operation.LIKE;
return this;
}
public abstract T build();
}
I want to be able to call new AccountConstraint.Builder().not().username("test"); but this is not possible as I lose the 'reference to the builder' at new AccountConstraint.Builder().not()., ie. I cannot select username("test") anymore.
In what ways could I fix this? I do want that the AccountBuilder.Builder extends ConstraintBuilder<AccountConstraint.Builder> such that I do not have to duplicate the commonly shared methods then.
Regards.
EDIT: I managed to get it working:
See the answer below for the changes.
I hope I haven't broken any Java fundamentals with this solution, I hope it is more of a solution than a dirty hack.
I would be pleased if someone could review this edit.
I think this should work:
Builder builder = (Builder) new AccountConstraint.Builder().not();
builder = builder.username("test");
Your issue is that:
new AccountConstraint.Builder().not()
returns a ConstrainBuilder<T>, which doesn't necessarily have access to username(final String). So, you cast it to a Builder builder, and then call username(final String) on builder.
EDIT:
You can turn this into one line:
((Builder) (new AccountConstraint.Builder().not())).username("test");
EDIT 2:
You could override not() in Builder: make it call super.not() and cast the return to a Builder. As in:
public Builder not()
{
return (Builder) super.not();
}
If casting is acceptable, an alternative to Steve's answer would be to override methods like not() in Builder and narrow the type like this:
public Builder not() {
return (Builder) super.not();
}
That way the caller doesn't have to cast each time.
You probably need recursive generics.
Something like this should work:
public abstract class ConstraintBuilder<T, B extends ConstraintBuilder<T,B>> {
private final Class<B> concreteBuilderType;
public ConstraintBuilder(Class<B> concreteBuilderType) {
if (!concreteBuilderType.isInstance(this)) {
throw new IllegalArgumentException("Wrong type");
}
this.concreteBuilderType = concreteBuilderType;
}
...
public B not() {
truthed = false;
return concreteBuilderType.cast(this);
}
}
The concrete Builder() constructor would have to call super(Builder.class).