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
jvx:code_snippets [2018/02/06 10:24]
admin
jvx:code_snippets [2020/07/01 13:46] (current)
cduncan [Remote Calls to JVx Without UI]
Line 1: Line 1:
 ~~NOTRANS~~ ~~NOTRANS~~
-~~Title: JVx code snippets~~+~~Title: JVx Code Snippets~~
  
 We have a list of useful code snippets for you. Simply use them for your application. All snippets are free to use and licensed under [[https://​www.apache.org/​licenses/​LICENSE-2.0|Apache 2.0]]. We have a list of useful code snippets for you. Simply use them for your application. All snippets are free to use and licensed under [[https://​www.apache.org/​licenses/​LICENSE-2.0|Apache 2.0]].
  
-==== Test DBStorages ​without ​Lifecycle ​objects ​====+==== Test DBStorages ​Without ​Lifecycle ​Objects ​====
  
-Access a DBStorage without JVx Server, Lifecycle Object, Security e.g. for Unit tests+Access a DBStorage without JVx Server, Lifecycle Object, Securitye.g.for unit tests.
  
 <file java ContactsAutoFrame.java>​ <file java ContactsAutoFrame.java>​
Line 39: Line 39:
 </​file>​ </​file>​
  
-==== A custom ​Application ​without menutoolbar... ====+==== A Custom ​Application ​Without MenuToolbaretc. ====
  
-Sometimes we need an application without overhead, e.g for Vaadin UI.+Sometimes we need an application without overhead, e.g., for Vaadin UI.
  
 <file java SimpleApplication.java>​ <file java SimpleApplication.java>​
Line 117: Line 117:
 </​file>​ </​file>​
  
-==== Test your business logic with JUnit ====+==== Test Your Business Logic With JUnit ====
  
-Tests our business logic without ​an application server, but with our Lifecycle objects. We test our server code without specific configuration or modifications for unit tests.+Tests our business logic not with an application server, but with our Lifecycle objects. We test our server code without specific configuration or modifications for unit tests.
  
-Business ​Object:+Business ​object:
  
 <file java UserRegistration.java>​ <file java UserRegistration.java>​
Line 166: Line 166:
 </​file>​ </​file>​
  
-A standard ​Lifecycle Object:+A standard ​life cycle object:
  
 <file java Session.java>​ <file java Session.java>​
Line 232: Line 232:
 </​file>​ </​file>​
  
-Unit Test:+Unit test:
  
 <file java> <file java>
Line 289: Line 289:
 </​file>​ </​file>​
  
-==== A very simple ​AbstractMemStorage ​implementation ​====+==== A Very Simple ​AbstractMemStorage ​Implementation ​====
  
-A server side memory storage with the column names: ID, NAME, PATH. The column PATH is not visible on the client-side, but is important for server-side. If "​error"​ is set as NAME, an Exception ​is thrown!+A server-side memory storage with the column names: ID, NAME, PATH. The column PATH is not visible on the client side, but is important for server side. If "​error"​ is set as NAME, an exception ​is thrown!
  
 <file java SimpleMemStorage.java>​ <file java SimpleMemStorage.java>​
Line 366: Line 366:
 </​file>​ </​file>​
  
-==== Change XML files very fast ====+==== Change XML Files Quickly ​====
  
-Our XML file+Our XML file:
  
 <file xml> <file xml>
Line 392: Line 392:
 </​file>​ </​file>​
  
-==== EventHandler ​without ​Listener ​interface ​====+==== EventHandler ​Without ​Listener ​Interface ​====
  
 Event definition: Event definition:
Line 446: Line 446:
 </​file>​ </​file>​
  
-==== EventHandler ​with Listener ​interface ​====+==== EventHandler ​With Listener ​Interface ​====
  
 The interface: The interface:
Line 535: Line 535:
 </​file>​ </​file>​
  
-Dispatch ​Events:+Dispatch ​events:
  
 <file java> <file java>
Line 651: Line 651:
 </​file>​ </​file>​
  
-==== Remote ​calls to JVx without ​UI ====+==== Remote ​Calls to JVx Without ​UI ====
  
-JVx is a full stack application framework, but all parts are independent. It is no problem to use only the communication classes or the Swing controls without GenUI. This snippet shows how it is possible to use JVx only on server side. You have full session handling and the client is built with your preferred UI or you client has no UI.+JVx is a full-stack application framework, but all parts are independent. It is no problem to use only the communication classes or the Swing controls without GenUI. This snippet shows how it is possible to use JVx only on server side. You have full session handling and the client is built with your preferred UI or if your client has no UI.
  
-Use JVx on server-side as usual. Integrate it into an application server like Tomcat or use it standalone.+Use JVx on server side as usual. Integrate it into an application server like Tomcat or use it standalone.
  
-Use the communcation ​classes to access server objects and actions. A simple object call could be implemented like the following snippet.+Use the communication ​classes to access server objects and actions. A simple object call could be implemented like the following snippet.
  
 Get the source code for a specific class via Server Object: Get the source code for a specific class via Server Object:
Line 693: Line 693:
 Use rdbContacts to insert/​update/​delete records. Use rdbContacts to insert/​update/​delete records.
  
-==== Use JVx Swing controls without ​JVx UI ====+==== Use JVx Swing Controls Without ​JVx UI ====
  
-JVx's Swing controls are independent of JVx UI. It is no problem to use a table that shows some database or in-memory records. It is also possible to use 3-tier or 2-tier architecture.+JVx's Swing controls are independent of JVx UI. It is no problem to use a table that shows some database or in-memory records. It is also possible to use three-tier or two-tier architecture.
  
-=== In-Memory ​data === +=== In-Memory ​Data === 
  
 Use JVxTable and integrate it in your Swing application. Use it like a standard JTable. Use JVxTable and integrate it in your Swing application. Use it like a standard JTable.
Line 750: Line 750:
 You have all JVx features like link cell editors, image choice editors - in your Swing application! You have all JVx features like link cell editors, image choice editors - in your Swing application!
  
-=== 2-tier data (database) ===+=== Two-Tier Data (Database) ===
  
 Replace the MemDataBook with following code: Replace the MemDataBook with following code:
Line 792: Line 792:
 </​file>​ </​file>​
  
-=== 3-tier data (database) ===+=== Three-Tier Data (Database) ===
  
 Remote DBAccess, DBStorage and DirectObjectConnection and replace the MasterConnection with following code: Remote DBAccess, DBStorage and DirectObjectConnection and replace the MasterConnection with following code:
Line 806: Line 806:
 </​file>​ </​file>​
  
-==== Example for automatic link cell editors ​===+==== Example for Automatic Link Cell Editors ​===
  
 The client code: The client code:
Line 1190: Line 1190:
  
    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-   // Interface ​implementation+   // Interface ​Implementation
    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
Line 1210: Line 1210:
        
    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-   // User-defined methods+   // User-Defined Methods
    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
Line 1583: Line 1583:
       ,(select E.EDUCATION from EDUCATIONS E where E.ID = CE.EDUC_ID) EDUCATION       ,(select E.EDUCATION from EDUCATIONS E where E.ID = CE.EDUC_ID) EDUCATION
   from CONT_EDUC CE   from CONT_EDUC CE
 +</​file>​
 +
 +==== Encrypt Passwords ====
 +
 +We don't encrypt passwords on client-side nor in the database. We use our middleware for that. It's super easy with server-side triggers/​events:​
 +
 +Add the method:
 +
 +<file java>
 +/**
 + * Encrypts a password, if password is changed.
 + ​* ​
 + * @param pEvent the storage event
 + * @throws Exception if encryption or data change fails
 + */
 +public void doEncryptPwd(StorageEvent pEvent) throws Exception
 +{
 +   IBean bn = pEvent.getNew();​
 +
 +   ​String sNew = (String)bn.get("​PASSWORD"​);​
 +   ​String sOld;
 +
 +   IBean bnOld = pEvent.getOld();​
 +
 +   if (bnOld != null)
 +   {
 +      sOld = (String)bnOld.get("​PASSWORD"​);​
 +   }
 +   else
 +   {
 +      sOld = null;
 +   }
 +
 +   if (!CommonUtil.equals(sOld,​ sNew))
 +   {
 +      //use the configuration of the selected application!
 +      bn.put("​PASSWORD",​ AbstractSecurityManager.getEncryptedPassword(
 +                           ​SessionContext.getCurrentSession().getConfig(),​ sNew));
 +   }
 +}   
 +</​file>​
 +
 +to your lifecycle object, e.g., Session.java.
 +
 +Add an event to your storage
 +
 +<file java>
 +//example storage
 +dbsUser = new DBStorage();​
 +dbsUser.setDBAccess(getDBAccess());​
 +dbsUser.setFromClause("​USER"​);​
 +dbsUser.open();​
 +
 +dbsUser.eventBeforeInsert().addListener(this,​ "​doEncryptPwd"​);​
 +dbsUser.eventBeforeUpdate().addListener(this,​ "​doEncryptPwd"​);​
 +</​file>​
 +
 +As last step, you need the password algorithm in your config.xml:
 +
 +<file xml>
 +<?xml version="​1.0"​ encoding="​UTF-8"?>​
 +
 +<​application>​
 +  <​securitymanager>​
 +    <​passwordalgorithm>​SHA</​passwordalgorithm>​
 +  </​securitymanager>​
 +  ...
 +</​application>​
 +</​file>​
 +
 +Choose one of the following algorithm: MD2, MD4, MD5, SHA, SHA-256, SHA-384, SHA-512.
 +
 +==== Fetch Data With DBStorage ====
 +
 +Sometimes you want to use DBAccess and DBStorage without JVx server and lifecycle objects. This is very easy because JVx was designed as library. Simply use DBStorage and DBAccess in your Servlets, applications,​ etc.
 +
 +The following example uses DBAccess and DBStorage to check if a user exists in the database and if an email address doesn'​t exist.
 +
 +<file java>
 +dba = DBAccess.getDBAccess("​jdbc:​oracle:​thin:​@localhost:​1521:​xe"​);​
 +dba.setUsername("​user"​);​
 +dba.setPassword("​password"​);​
 +//also possible, but needs rad/​apps/​myapp/​config.xml
 +//DBAccess dba = DBAccess.getDBAccess(DBSecurityManager.getCredentials(
 +//                             ​Configuration.getApplicationZone("​myapp"​).getConfig()));​
 +dba.open();
 +dba.getConnection().setAutoCommit(false);​
 +
 +DBStorage dbsUser = new DBStorage();​
 +dbsUser.setDBAccess(dba);​
 +dbsUser.setWritebackTable("​USERS"​);​
 +dbsUser.open();​
 +</​file>​
 +
 +If you prefer POJOs:
 +
 +<file java>
 +User user = dbsUser.createPOJO(User.class,​ bnUser);
 +</​file>​
 +
 +==== 50% Width With FormLayout ====
 +
 +If you have two components, e.g., GroupPanels,​ and if you want that each group is 50% of the parent width, you could use following code:
 +
 +<file java>
 +UIFormLayout layout = new UIFormLayout();​
 +        ​
 +IConstraints center = layout.getHCenterConstraints(0,​ 5, -1, 5);
 +
 +UILabel label = new UILabel();
 +label.setPreferredSize(5,​ 0);
 +
 +panel.add(label,​ center);
 +panel.add(gpanLeft,​ layout.getConstraints(center.getTopAnchor(),​
 +                                 ​layout.createAnchor(layout.getLeftMarginAnchor(),​ 0), 
 +                                 ​center.getBottomAnchor(),​
 +                                 ​layout.createAnchor(center.getLeftAnchor(),​ 0)));              ​
 +panel.add(gpanRight,​ layout.getConstraints(center.getTopAnchor(),​
 +                                 ​layout.createAnchor(center.getRightAnchor(),​ 0), 
 +                                 ​center.getBottomAnchor(),​
 +                                 ​layout.createAnchor(layout.getRightMarginAnchor(),​ 0)));
 +</​file>​
 +
 +==== Search Column in Condition ====
 +
 +If you create your own IStorage impelementation,​ it could be useful to know how to find the value for a specific column:
 +
 +<file java>
 +public static Object getEqualsValue(ICondition pFilter, String pColumn)
 +{
 +        if (pFilter instanceof OperatorCondition)
 +        {
 +                for (ICondition cond : ((OperatorCondition)pFilter).getConditions())
 +                {
 +                        if (cond instanceof Equals)
 +                        {
 +                                if (pColumn.equals(((Equals)cond).getColumnName()))
 +                                {
 +                                        return ((Equals)cond).getValue();​
 +                                }
 +                        }
 +                }
 +        }
 +        else if (pFilter instanceof Equals)
 +        {
 +                if (pColumn.equals(((Equals)pFilter).getColumnName()))
 +                {
 +                        return ((Equals)pFilter).getValue();​
 +                }
 +        }
 +        ​
 +        return null;
 +}
 +</​file>​
 +
 +The above method tries to find the first equals condition with the given column name.
 +
 +The method doesn'​t work with recursion because usually this is not necessary. If you want to know the different condition types, simply check ''​toString()''​ of ''​javax.rad.model.condition.BaseCondition''​.
 +
 +==== Connection Property Changed Listener ====
 +
 +It's easy to listen on connection property changed events:
 +
 +<file java>
 +MasterConnection appcon = new MasterConnection(createConnection());​
 +appcon.addPropertyChangedListener("​Application.mode",​ new IConnectionPropertyChangedListener()
 +{
 +    public void propertyChanged(PropertyEvent pEvent)
 +    {
 +        liProps.add(pEvent.getPropertyName() + " = " + pEvent.getNewValue());​
 +    }
 +});
 +</​file>​
 +
 +The above listener will be invoked whenever the connection property **Application.mode** will be changed.
 +
 +It's also possible to listen on all properties, with following snippet:
 +
 +<file java>
 +MasterConnection appcon = new MasterConnection(createConnection());​
 +appcon.addPropertyChangedListener(null,​ new IConnectionPropertyChangedListener()
 +{
 +    public void propertyChanged(PropertyEvent pEvent)
 +    {
 +        liProps.add(pEvent.getPropertyName() + " = " + pEvent.getNewValue());​
 +    }
 +});
 +</​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>​
This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information