Documentation

Trace:

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Next revision Both sides next revision
jvx:code_snippets [2018/02/06 10:30]
admin
jvx:code_snippets [2019/05/03 09:24]
admin [Detect Production mode]
Line 1770: Line 1770:
     }     }
 }); });
 +</​file>​
 +
 +==== Test UI with simple JUnit Test ====
 +
 +Sometimes you want a JUnit test case for a specific UI feature. If you need an application frame for your test, you could use following implementation:​
 +
 +A simple Test Launcher:
 +
 +<file java SimpleTestLauncher.java>​
 +/*
 + * Copyright 2016 SIB Visions GmbH
 + ​* ​
 + * Licensed under the Apache License, Version 2.0 (the "​License"​);​ you may not
 + * use this file except in compliance with the License. You may obtain a copy of
 + * the License at
 + ​* ​
 + * http://​www.apache.org/​licenses/​LICENSE-2.0
 + ​* ​
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 + * License for the specific language governing permissions and limitations under
 + * the License.
 + *
 + *
 + * History
 + *
 + * 11.08.2016 - [JR] - creation
 + */
 +package javax.rad.application;​
 +
 +import javax.rad.ui.IComponent;​
 +import javax.rad.ui.container.IDesktopPanel;​
 +import javax.rad.util.IRunnable;​
 +import javax.swing.JComponent;​
 +import javax.swing.SwingUtilities;​
 +
 +import com.sibvisions.rad.application.Application;​
 +import com.sibvisions.rad.ui.swing.impl.SwingApplication;​
 +
 +/**
 + * The <​code>​SimpleTestLauncher</​code>​ starts a {@link SwingApplication} and shows 
 + * a specific content in the application pane.
 + ​* ​
 + * @author René Jahn
 + */
 +public class SimpleTestLauncher
 +{
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +    // Initialization
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +    ​
 +    /**
 +     * Creates a new instance of <​code>​SimpleTestLauncher</​code>​.
 +     ​* ​
 +     * @param pComponent the component to show
 +     */
 +    public SimpleTestLauncher(final IComponent pComponent)
 +    {
 +        final SwingApplication app = new SwingApplication();​
 +        ​
 +        app.startup(SimpleTestApplication.class.getName(),​ null, null);
 +        app.setSystemExitOnDispose(false);​
 +        app.eventWindowClosed().addListener(new IRunnable()
 +        {
 +            public void run()
 +            {
 +                synchronized (SimpleTestLauncher.this)
 +                {
 +                    SimpleTestLauncher.this.notify();​
 +                }
 +            }
 +        });
 +        ​
 +        SwingUtilities.invokeLater(new Runnable()
 +        {
 +            public void run()
 +            {
 +                IDesktopPanel desktop = ((Application)app.getApplication()).getDesktopPane();​
 +                desktop.removeAll();​
 +                desktop.add(pComponent);​
 +                ​
 +                //because test was created before LaF was set!
 +                SwingUtilities.updateComponentTreeUI((JComponent)pComponent.getResource());​
 +            }
 +        });
 +        ​
 +        synchronized(this)
 +        {
 +            try
 +            {
 +                wait();
 +            }
 +            catch (InterruptedException ie)
 +            {
 +                //ignore
 +            }
 +        }
 +    }
 +
 +}   // SimpleTestLauncher
 +</​file>​
 +
 +This class wrapps application creation, because we need to set the UI component after the application was started!
 +
 +The second class is our Application:​
 +
 +<file java SimpleTestApplication.java>​
 +/*
 + * Copyright 2016 SIB Visions GmbH
 + ​* ​
 + * Licensed under the Apache License, Version 2.0 (the "​License"​);​ you may not
 + * use this file except in compliance with the License. You may obtain a copy of
 + * the License at
 + ​* ​
 + * http://​www.apache.org/​licenses/​LICENSE-2.0
 + ​* ​
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 + * License for the specific language governing permissions and limitations under
 + * the License.
 + *
 + *
 + * History
 + *
 + * 11.08.2016 - [JR] - creation
 + */
 +package javax.rad.application;​
 +
 +import javax.rad.application.genui.UILauncher;​
 +import javax.rad.remote.IConnection;​
 +
 +import com.sibvisions.rad.application.Application;​
 +
 +/**
 + * The <​code>​SimpleTestApplication</​code>​ is an always connected application ​
 + * implementation,​ for test cases.
 + ​* ​
 + * @author René Jahn
 + */
 +public class SimpleTestApplication extends Application
 +{
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +    // Initialization
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +
 +    /**
 +     * Creates a new instance of <​code>​SimpleTestApplication</​code>​.
 +     ​* ​
 +     * @param pLauncher the launcher
 +     */
 +    public SimpleTestApplication(UILauncher pLauncher)
 +    {
 +        super(pLauncher);​
 +    }
 +    ​
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +    // Overwritten methods
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +
 +    @Override
 +    protected String getApplicationName()
 +    {
 +        return "Test Application";​
 +    }
 +
 +    @Override
 +    protected IConnection createConnection() throws Exception
 +    {
 +        return null;
 +    }
 +    ​
 +    @Override
 +    public void notifyVisible()
 +    {
 +    }
 +    ​
 +    @Override
 +    protected void afterLogin()
 +    {
 +    }
 +
 +}   // SimpleTestApplication
 +</​file>​
 +
 +The application removes login screen and you can use it without manual tasks.
 +
 +Finally, our JUnit test case:
 +
 +<file java TestActivity.java>​
 +/*
 + * Copyright 2016 SIB Visions GmbH
 + ​* ​
 + * Licensed under the Apache License, Version 2.0 (the "​License"​);​ you may not
 + * use this file except in compliance with the License. You may obtain a copy of
 + * the License at
 + ​* ​
 + * http://​www.apache.org/​licenses/​LICENSE-2.0
 + ​* ​
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 + * License for the specific language governing permissions and limitations under
 + * the License.
 + *
 + *
 + * History
 + *
 + * 11.08.2016 - [JR] - creation
 + */
 +package com.sibvisions.forum;​
 +
 +import javax.rad.application.SimpleTestLauncher;​
 +import javax.rad.genui.UIFactoryManager;​
 +import javax.rad.genui.container.UIPanel;​
 +import javax.rad.genui.layout.UIBorderLayout;​
 +import javax.rad.model.reference.ReferenceDefinition;​
 +
 +import org.junit.BeforeClass;​
 +import org.junit.Test;​
 +
 +import com.sibvisions.apps.components.NavigationTable;​
 +import com.sibvisions.rad.persist.StorageDataBook;​
 +import com.sibvisions.rad.persist.jdbc.DBAccess;​
 +import com.sibvisions.rad.persist.jdbc.DBStorage;​
 +import com.sibvisions.rad.ui.swing.impl.SwingFactory;​
 +
 +public class TestActivity extends UIPanel
 +{
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +    // Initialization
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +
 +    /**
 +     * Initializes the unit test.
 +     ​* ​
 +     * @throws Exception if initialization fails
 +     */
 +    @BeforeClass
 +    public static void beforeClass()
 +    {
 +        UIFactoryManager.getFactoryInstance(SwingFactory.class);​
 +    }
 +    ​
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +    // Test methods
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +
 +    /**
 +     * Tests application start with a custom UI.
 +     ​* ​
 +     * @throws Exception if app start fails
 +     */
 +    @Test
 +    public void testStartApplication() throws Exception
 +    {
 +        DBAccess dba = DBAccess.getDBAccess("​jdbc:​oracle:​thin:​@localhost:​1521:​XE", ​
 +                                            "​username",​ "​password"​);​
 +        dba.open();
 +        ​
 +        DBStorage dbsContract = new DBStorage();​
 +        dbsContract.setWritebackTable("​CONTRACT"​);​
 +        dbsContract.setDBAccess(dba);​
 +        dbsContract.open();​
 +        ​
 +        DBStorage dbsActivity = new DBStorage();​
 +        dbsActivity.setWritebackTable("​ACTIVITY"​);​
 +        dbsActivity.setDBAccess(dba);​
 +        dbsActivity.open();​
 +        ​
 +        StorageDataBook sdbContract = new StorageDataBook(dbsContract);​
 +        sdbContract.open();​
 +        sdbContract.fetchAll();​
 +        sdbContract.setSelectedRow(0);​
 +        ​
 +        StorageDataBook sdbActivity = new StorageDataBook(sdbContract,​ dbsActivity);​
 +        sdbActivity.setMasterReference(new ReferenceDefinition(new String[] {"​CONT_ID"​}, ​
 +                                       ​sdbContract, ​
 +                                       new String[] {"​ID"​}));​
 +        sdbActivity.open();​
 +        ​
 +        setLayout(new UIBorderLayout());​
 +        ​
 +        NavigationTable nav = new NavigationTable();​
 +        nav.setDataBook(sdbActivity);​
 +        ​
 +        add(nav);
 +        ​
 +        new SimpleTestLauncher(this);​
 +    }
 +
 +}   // TestActivity
 +</​file>​
 +
 +The result:
 +
 +{{:​jvx:​testapp.png?​nolink|}}
 +
 +The database model (Oracle):
 +
 +{{:​jvx:​contract_activity.png?​nolink|}}
 +
 +The database objects (Oracle):
 +
 +<file sql create.sql>​
 +CREATE TABLE CONTRACT
 +(
 +  id    NUMBER(16) NOT NULL,
 +  title VARCHAR2(1000),​
 +  state NUMBER(1)
 +)
 +;
 +comment ON COLUMN CONTRACT.id
 +  IS '​PK';​
 +comment ON COLUMN CONTRACT.title
 +  IS '​Contract title';​
 +comment ON COLUMN CONTRACT.state
 +  IS '​Contract state';​
 +ALTER TABLE CONTRACT
 +  ADD constraint CONT_PK PRIMARY KEY (ID);
 +
 +CREATE TABLE ACTIVITY
 +(
 +  id                  NUMBER(16) NOT NULL,
 +  cont_id ​            ​NUMBER(16),​
 +  name                VARCHAR2(1000),​
 +  cost                NUMBER(10,​2),​
 +  valid_from ​         DATE,
 +  valid_to ​           DATE,
 +  responsible_user_id NUMBER(16),
 +  validated ​          ​CHAR(1) DEFAULT '​Y'​
 +)
 +;
 +comment ON COLUMN ACTIVITY.id
 +  IS '​PK';​
 +comment ON COLUMN ACTIVITY.cont_id
 +  IS '​Contract FK';
 +comment ON COLUMN ACTIVITY.name
 +  IS '​Activity name';
 +comment ON COLUMN ACTIVITY.cost
 +  IS '​Activity costs';​
 +comment ON COLUMN ACTIVITY.valid_from
 +  IS 'Valid from';
 +comment ON COLUMN ACTIVITY.valid_to
 +  IS 'Valid to';
 +comment ON COLUMN ACTIVITY.responsible_user_id
 +  IS '​Responsible User FK';
 +comment ON COLUMN ACTIVITY.validated
 +  IS '​Whether the activity is valid';​
 +ALTER TABLE ACTIVITY
 +  ADD constraint ACTI_PK PRIMARY KEY (ID);
 +ALTER TABLE ACTIVITY
 +  ADD constraint ACTI_CONT_FK FOREIGN KEY (CONT_ID)
 +  REFERENCES CONTRACT (ID);
 +ALTER TABLE ACTIVITY
 +  ADD constraint ACTI_RESP_USER_FK FOREIGN KEY (RESPONSIBLE_USER_ID)
 +  REFERENCES USERS (ID);
 +
 +
 +CREATE OR REPLACE TRIGGER TR_ACTIVITY_BR_IU
 +  before INSERT OR UPDATE ON activity  ​
 +  FOR each row
 +begin
 +
 +  IF (:​new.validated = '​N'​) then
 +    ​
 +    IF (:​new.valid_from IS NULL) then
 +      raise_application_error(-20000,​ 'Valid from can''​t be null!'​);​
 +    end IF;
 +  ​
 +  end IF;
 +  ​
 +end TR_ACTIVITY_BR_IU;​
 +/
 +
 +CREATE OR REPLACE TRIGGER TR_CONTRACT_BR_IU
 +  before INSERT OR UPDATE ON contract  ​
 +  FOR each row
 +begin
 +
 +  IF (:new.state = 4) then
 +      ​
 +    UPDATE activity ​
 +       SET validated = '​N'​
 +     WHERE cont_id = id
 +       AND validated = '​Y';​
 +       
 +  end IF;     
 +  ​
 +end TR_CONTRACT_BR_IU;​
 +/
 +
 +
 +ALTER TABLE CONTRACT disable ALL triggers;
 +ALTER TABLE ACTIVITY disable ALL triggers;
 +ALTER TABLE ACTIVITY disable constraint ACTI_CONT_FK;​
 +ALTER TABLE ACTIVITY disable constraint ACTI_RESP_USER_FK;​
 +INSERT INTO CONTRACT (id, title, state)
 +VALUES (1, '​Air',​ 1);
 +commit;
 +INSERT INTO ACTIVITY (id, cont_id, name, cost, valid_from, valid_to, responsible_user_id,​ validated)
 +VALUES (1, 1, 'Part 1', .2, NULL, NULL, NULL, '​Y'​);​
 +commit;
 +ALTER TABLE ACTIVITY enable constraint ACTI_CONT_FK;​
 +ALTER TABLE ACTIVITY enable constraint ACTI_RESP_USER_FK;​
 +ALTER TABLE CONTRACT enable ALL triggers;
 +ALTER TABLE ACTIVITY enable ALL triggers;
 +</​file>​
 +
 +==== Authentication/​User logging ====
 +
 +Some applications need detailed information about successful or failed user logins. If you have such requirement,​ you could create a custom security manager to solve the problem. The security manager will be used for user authentication,​ so it's the right place to start.
 +
 +We use the database security manager as base class:
 +
 +<file java LoggingSecurityManager.java>​
 +package com.sibvisions.apps.vaadin.web;​
 +
 +public class LoggingSecurityManager extends DBSecurityManager
 +{
 +    private PreparedStatement psLockedUsers;​
 +
 +    @Override
 +    protected boolean isPasswordValid(ISession pSession, String pPassword) throws Exception
 +    {
 +        // additional check: locked user
 +
 +        ResultSet resultSet = null;
 +
 +        try
 +        {
 +            psLockedUsers.clearParameters();​
 +            psLockedUsers.setString(1,​ pSession.getUserName());​
 +            psLockedUsers.execute();​
 +
 +            resultSet = psLockedUsers.getResultSet();​
 +
 +            if (resultSet.next())
 +            {
 +                throw new SilentAbortException();​
 +            }
 +        }
 +        finally
 +        {
 +            CommonUtil.close(resultSet);​
 +        }
 +
 +        return super.isPasswordValid(pSession,​ pPassword);
 +    }
 +
 +    @Override
 +    public synchronized void validateAuthentication(ISession pSession) throws Exception
 +    {
 +        String result = null;
 +
 +        Throwable error = null;
 +
 +        try
 +        {
 +            super.validateAuthentication(pSession);​
 +
 +            result = Constants.RESULT_OK;​
 +        }
 +        catch (SilentAbortException sae)
 +        {
 +            result = Constants.RESULT_IGNORE;​
 +            error = sae;
 +
 +            throw sae;
 +        }
 +        catch (SecurityException se)
 +        {
 +            result = Constants.RESULT_DENIED;​
 +            error = se;
 +
 +            throw se;
 +        }
 +        catch (Exception ex)
 +        {
 +            result = Constants.RESULT_ERROR;​
 +            error = ex;
 +
 +            error(ex);
 +
 +            throw ex;
 +        }
 +        finally
 +        {
 +            try
 +            {
 +                pSession.callAction("​dbLog",​ Constants.TYPE_LOGIN,​ error, result);
 +            }
 +            catch (Throwable thr)
 +            {
 +                error(thr);
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public synchronized void logout(ISession pSession)
 +    {
 +        String result = null;
 +        Throwable error = null;
 +
 +        try
 +        {
 +            super.logout(pSession);​
 +
 +            if (Boolean.parseBoolean((String)pSession.getProperty("​userlogout"​)))
 +            {
 +                result = Constants.RESULT_OK;​
 +            }
 +            else
 +            {
 +                result = Constants.RESULT_EXPIRED;​
 +            }
 +        }
 +        catch (Exception ex)
 +        {
 +            result = Constants.RESULT_ERROR;​
 +            error = ex;
 +
 +            error(ex);
 +        }
 +        finally
 +        {
 +            try
 +            {
 +                pSession.callAction("​dbLog",​ Constants.TYPE_LOGOUT,​ error, result);
 +            }
 +            catch (Throwable eLog)
 +            {
 +                error(eLog);​
 +            }
 +        }
 +    }
 +
 +    @Override
 +    protected void initStatements(Connection pConnection) throws Exception
 +    {
 +        super.initStatements(pConnection);​
 +
 +        psLockedUsers = prepareStatement(pConnection,​ "​select * from LOCKS where USERNAME = ?");
 +    }
 +
 +}     // LoggingSecurityManager
 +</​file>​
 +
 +The clue is that the log method **dbLog** was implemented in the session LCO of our application because we re-use the business logic in our security manager. Here's the missing method:
 +
 +<file java>
 +public void dbLog(int pAction, Throwable pError, String pStatus)
 +{
 +    ILogger log = LoggerFactory.getInstance(getClass());​
 +
 +    log.info("​Log action ", pAction, pError, pStatus);
 +    ​
 +    try
 +    {
 +        getDBAccess().executeProcedure("​log", ​
 +                                       ​pAction, ​
 +                                       ​SessionContext.getCurrentSession().getUserName(), ​
 +                                       new Timestamp(System.currentTimeMillis()));​
 +    }
 +    catch (Exception ex)
 +    {
 +        log.error(ex);​
 +    }
 +}
 +</​file>​
 +
 +==== Detect Production mode ====
 +
 +If you use the DBSecurityManager,​ it's possible to set the environment. The environment is a marker for the database connection, e.g.:
 +
 +<file xml>
 +<?xml version="​1.0"​ encoding="​UTF-8"?>​
 +
 +<​application>​
 +  <​securitymanager>​
 +    <​class>​com.sibvisions.rad.server.security.DBSecurityManager</​class>​
 +    <​environment>​prod</​environment>​
 +  </​securitymanager>​
 +  <​datasource>​
 +    <db name="​default">​
 +      <​url>​jdbc:​oracle:​thin:​@dbserver:​1521:​XE</​url>​
 +      <​username>​test</​username>​
 +      <​username_prod>​prod</​username_prod>​
 +      <​password>​test</​password>​
 +      <​password_prod>​prod</​password_prod>​
 +    </db>
 +  </​datasource>​
 +</​application>​
 +</​file>​
 +
 +The marker will be used as postfix for the detection of datasource credentials. In the example, the postfix is **prod** and the **username_prod** will be used instead of **username**. If not tag with environment postfix is available, the standard tag will be used. 
 +
 +If you want to use the environment in your source code, simply call:
 +
 +<file java>
 +String env = SessionContext.getCurrentSessionConfig().getProperty("/​application/​securitymanager/​environment"​);​
 +</​file>​
 </​file>​ </​file>​
This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information