stSoftware
12 Beaconsfield St | Newport, New South Wales 2106 | 1300 78 73 78

stSoftware blog


How does group contact categories work?
18 Apr 2013

How does it work: In the example below, by selecting the parent category called 'Group 1' , all the sub categories 'Finance,Other,Supplier' are selected automatically. Or we can only select the sub category (Like the category 'Marketing' In the example below) Search for contact by category How to group categories: Go to Admin and open the 'pick list manger' and select the table called 'Contact Category', this table holds the list of categories. Create new category like 'Group 1', this category will...

Read More

Using GWT super dev mode
15 Apr 2013

NOTE: You MUST be using GWT version 2.5.1 or better. The source maps were broken in GWT 2.5 Add to your GWT module :- com/aspc/cms/gwt/site/Site.gwt.xml Add to the build.properties file:- gwt.main.package=com.aspc.cms.gwt.site.Site  Start the superdev server:- Add the property SUPER_DEV_MODULE to the webserver startup In Chrome enable source map Select the Java source from the chrome "sources tab":- Now you'll be able to step through the Java source code in chrome

Read More

Reliably handling externally processed records in an highly concurrent environment.
12 Apr 2013

To reliably process records in a concurrent environment which interface with a external system (not included in the database transaction) we must adopt a "three step approach". The three steps are:- Take ownership of the record by marking as "processing" and prevent other processes from doing the same by using an optimistic locking strategy. Process the record itself in the external system (payment gateway, send email etc) Record the result of the processing in the database by marking the record... * Handling race conditions when processing record. * * Firstly this process need to take ownership of the record using * optimistic locking strategy to prevent other processes grabbing the * same record. * http://en.wikipedia.org/wiki/Optimistic_concurrency_control * * Process the record ( email in this case) * * Mark the record as completed. */ MutableDataSource mds = getConnection().getMutableDataSource(); // find the email to be sent email = (DBEmailSend) mds.findKey(gk); mds.markSavePoint("SEND_START");// Mark the start point. for (int i = 0; true; i++) { try { /* * record the current transaction so that if a concurrent process trys to change the email then a dirty cache will be thrown. */ email.forceLockedTransaction(); /** * check and change the status of the email. Change the status * to a intermediate status and save. * * The save process on this record is atomic and only one * process will be able to successfully change the status to * "PROCESSING". * */ String sendStatus = email.getString(DBEmailSend.DBFIELD_SEND_STATUS); if (sendStatus.equals(DBEmailSendStatus.LIST_ENTERED) == false && sendStatus.equals(DBEmailSendStatus.LIST_QUEUED) == false) { throw new Exception("Send status must be ENTERED or QUEUED"); } email.setValue(DBEmailSend.DBFIELD_SEND_STATUS, DBEmailSendStatus.LIST_PROCESSING); mds.save("Email send started processing:" + email); } catch (DirtyCacheException dce) { /** * A concurrent change has been detected. Rollback to the start * point and retry. If the other process has successfully taken * ownership of the record we will skip. */ mds.rollbackTo("SEND_START"); if (i == 9) { String errorMsg = "Cannot send email: " + s + " as repeatedly failed in dirty cache "; LOGGER.error(errorMsg); throw new Exception(errorMsg); } else { long msecs = (long) (1000.0 * Math.random()); Thread.sleep(msecs); continue; } } /** * The only way that we would successfully get to this step is that * we have taken ownership of the record by setting the status to * "PROCESSING". * * The actual send process will set the status to "OK" or "FAILED". * * If the machine is turned off or crashes at any point the only * email records that we need to check the actual status of is the * ones that are with the status "PROCESSING" */ email.send(mds); break; }

Read More

Multi-Threads: SyncBlock a replacement of the synchronized keyword
12 Apr 2013

When you call the method take() on a SyncBlock object you'll block up until the specified maximum number of seconds and then an error will be thrown if you are unable to obtain the lock on this object. You must ALWAYS call release() on any sync SyncBlock that you have obtained the lock on. The SyncBlock differs from the keyword synchronized in that it is interruptible and that it will timeout if it blocks for too long. The SyncBlock enhances a normal java.lang.concurrent.Lock in that it will interrupt... 6687 * notify all the class listeners 6688 * @param type the type of change MODIFY,DELETE or CREATE 6689 * @param gk the global key to notify of. 6690 */ 6691 private void notifyDBClassListeners( final String type, final GlobalKey gk) 6692 { 6693 String key = gk.getClassId().toString(); 6694 6695 /** 6696 * DEADLOCK found when this was a synchronized block. 6697 * now we are using a SyncBlock lock object which will timeout after 2 minutes if not successful. 6698 * 6699 * Once you have taken the lock the next statement must be the start of the try block so we never leave this 6700 * section without releasing the lock. 6701 */ 6702 dbClassListenersLock.take(); 6703 try 6704 { 6705 ArrayList list = (ArrayList)dbClassListeners.get( key); 6706 6707 if( list != null) 6708 { 6709 for( int i = 0; i < list.size(); i++) 6710 { 6711 DBClassListener listener; 6712 6713 listener = (DBClassListener)list.get( i); 6714 6715 if( type.equals( DBData.NOTIFY_MODIFIED)) 6716 { 6717 listener.eventObjectModified( gk, this); 6718 } 6719 else if( type.equals( DBData.NOTIFY_DELETED)) 6720 { 6721 listener.eventObjectDeleted( gk, this); 6722 } 6723 else if( type.equals( DBData.NOTIFY_CREATED)) 6724 { 6725 listener.eventObjectCreated( gk, this); 6726 } 6727 else 6728 { 6729 LOGGER.error( "Wrong type:" + type); 6730 } 6731 } 6732 } 6733 } 6734 catch( Throwable t) 6735 { 6736 LOGGER.warn( "ignored exception in listener", t); // Sr, 12/05/2005 Bug #5224 6737 } 6738 finally 6739 { 6740 /** 6741 * Always release the lock if obtained. 6742 */ 6743 dbClassListenersLock.release(); 6744 } 6745 } com/aspc/remote/util/misc/SyncBlock.java 107 /** 108 * release the lock 109 */ 110 public void release() 111 { 112 syncLock.unlock(); 113 } 114 115 /** 116 * take the lock and throw an error if you can't get it. 117 */ 118 public void take() 119 { 120 try 121 { 122 SyncLock tempLock = syncLock; 123 if (syncLock.tryLock(blockSeconds, TimeUnit.SECONDS) == false) 124 { 125 Thread ownerThread = syncLock.getOwner(); 126 127 if( ownerThread.isAlive() == false) 128 { 129 synchronized( this) 130 { 131 if( tempLock == syncLock) 132 { 133 syncLock = new SyncLock(); 134 } 135 } 136 137 LOGGER.fatal(this + " never released by " + ownerThread); 138 take(); 139 return; 140 } 141 142 StringBuilder sb = new StringBuilder( toString()); 143 sb.append("\n"); 144 Thread currentThread = Thread.currentThread(); 145 146 sb.append("Failed to get lock for thread: " + currentThread + "\n"); 147 148 for (StackTraceElement ste : currentThread.getStackTrace()) 149 { 150 sb.append("\t" + ste + "\n"); 151 } 152 153 if( ownerThread != null) 154 { 155 sb.append("\nLock held by thread: " + ownerThread + "\n"); 156 157 for (StackTraceElement ste : ownerThread.getStackTrace()) 158 { 159 sb.append("\t" + ste + "\n"); 160 } 161 sb.append("Interrupting holding thread"); 162 ownerThread.interrupt(); 163 } 164 else 165 { 166 sb.append("NO OWNER THREAD found"); 167 } 168 169 LOGGER.fatal(sb.toString()); 170 171 throw new DataBaseError("could not get the lock on: " + name); 172 } 173 } 174 catch (InterruptedException ex) 175 { 176 Thread.interrupted(); 177 LOGGER.warn( "could not take lock on " + name, ex); 178 Thread.currentThread().interrupt(); 179 throw new DataBaseError("could not get the lock on: " + name, ex); 180 } 181 } 182 183 class SyncLock extends ReentrantLock 184 { 185 public SyncLock( ) 186 { 187 super( true); 188 } 189 /** 190 * get the owner thread 191 * @return the owner thread 192 */ 193 @Override 194 public Thread getOwner()//NOPMD 195 { 196 return super.getOwner(); 197 } 198 } com/aspc/remote/util/misc/selftest/TestSyncBlock.java 88 89 /** 90 * check we recover from a lock that is never released. 91 */ 92 public void testNeverReleased() throws InterruptedException 93 { 94 final SyncBlock block = new SyncBlock( "never release", 2); 95 96 Runnable r = new Runnable( ) 97 { 98 public void run() 99 { 100 block.take(); 101 } 102 }; 103 Thread t = new Thread( r); 104 t.start(); 105 106 t.join( 120000); 107 108 block.take(); 109 } 110 111 /** 112 * check that we actually do block 113 */ 114 @SuppressWarnings("empty-statement") 115 public void testBlock() throws InterruptedException 116 { 117 final SyncBlock block = new SyncBlock( "long time", 10); 118 119 Runnable r = new Runnable( ) 120 { 121 public void run() 122 { 123 block.take(); 124 try 125 { 126 Thread.sleep(120000); 127 } 128 catch (InterruptedException ex) 129 { 130 LOGGER.warn("interrupted"); 131 } 132 finally 133 { 134 block.release(); 135 } 136 } 137 }; 138 Thread t = new Thread( r); 139 t.start(); 140 141 t.join( 1000); 142 143 try 144 { 145 block.take(); 146 fail( "should not succeed"); 147 } 148 catch( Throwable tw) 149 { 150 ;// this is good 151 } 152 t.interrupt(); 153 154 t.join( 5000); 155 block.take(); 156 } 157 158 159 /** 160 * check that deadlocks are handled 161 * @throws Exception a test failure 162 */ 163 public void testDeadlockHandled() throws Exception 164 { 165 a=new A(); 166 b=new B(); 167 Thread at = new Thread( a); 168 169 at.start(); 170 Thread bt = new Thread( b); 171 172 bt.start(); 173 174 long start = System.currentTimeMillis(); 175 while( start + 120000 > System.currentTimeMillis()) 176 { 177 if( a.calling && b.calling ) break; 178 Thread.sleep(100); 179 } 180 181 synchronized( marker) 182 { 183 marker.notifyAll(); 184 } 185 LOGGER.info("waiting for detection"); 186 at.join(240000); 187 bt.join(240000); 188 189 if( a.theException == null && b.theException == null) 190 { 191 fail( "The threads were not interrupted"); 192 } 193 194 assertFalse( "should have finished", at.isAlive()); 195 assertFalse( "should have finished", bt.isAlive()); 196 } 197 198 class A implements Runnable 199 { 200 private final SyncBlock block = new SyncBlock("A block", 2); 201 boolean calling; 202 Throwable theException; 203 204 public void run() 205 { 206 try 207 { 208 callB(); 209 } 210 catch( Throwable e) 211 { 212 theException = e; 213 LOGGER.warn( "got cancelled", e); 214 } 215 } 216 217 public void hello() 218 { 219 block.take(); 220 try 221 { 222 LOGGER.info("hello A"); 223 } 224 finally 225 { 226 block.release(); 227 } 228 } 229 230 private void callB() throws InterruptedException 231 { 232 block.take(); 233 try 234 { 235 calling=true; 236 synchronized( marker) 237 { 238 marker.wait(120000); 239 } 240 LOGGER.info("call B"); 241 b.hello(); 242 } 243 finally 244 { 245 block.release(); 246 } 247 } 248 } 249 250 class B implements Runnable 251 { 252 boolean calling; 253 Throwable theException; 254 private final SyncBlock block = new SyncBlock("A block", 2); 255 256 public void run() 257 { 258 try 259 { 260 callA(); 261 } 262 catch( Throwable e) 263 { 264 theException = e; 265 LOGGER.warn( "got cancelled", e); 266 } 267 } 268 269 public void hello() 270 { 271 block.take(); 272 try 273 { 274 LOGGER.info("hello B"); 275 } 276 finally 277 { 278 block.release(); 279 } 280 } 281 282 private void callA() throws InterruptedException 283 { 284 block.take(); 285 try 286 { 287 calling=true; 288 synchronized( marker) 289 { 290 marker.wait(120000); 291 } 292 LOGGER.info("call A"); 293 a.hello(); 294 } 295 finally 296 { 297 block.release(); 298 } 299 } 300 } 301 302 private A a; 303 private B b;

Read More

Multi-Threads: secondary cache design
12 Apr 2013

1 /* 2 * Copyright (c) 2013 ASP Converters pty ltd 3 * 4 * www.aspconverters.com.au. 5 * 6 * All Rights Reserved. 7 * 8 * This software is the proprietary information of 9 * ASP Converters Pty Ltd. 10 * Use is subject to license terms. 11 */ 12 import com.aspc.DBObj.*; 13 import com.aspc.DBObj.Listeners.*; 14 15 /** 16 * Use a secondary cache to fetch a "Thing". We are in a multi-machine / multi-processor / 17 * multi-user / multi-threaded environment. Records can and do change at any time from one 18 * line to the next. A few points to look out for:- 19 * 20 * 1) All work must be done with local variables so that we don"t get Null 21 * Pointer Exceptions when the cache is cleared while we are in this method. 22 * 23 * 2) Threads can take their copies of object variables which are only flushed/sync"d when 24 * synchronized is called on the object. 25 * 26 * 3) Database queries etc. can take a while to run (specially if the query returns multiple rows) 27 * a record in the result set maybe changed and a message sent/clear cache called before the 28 * result is returned. This case must be handled ( it happens a lot with bulk records) 29 * 30 * 4) Synchronizing the method synchronizes the whole Object. So for complex objects like Company 31 * or DBClass which may have many secondary caches we would be blocking a fetch of something 32 * that is in memory due to a fetch of something else that is not. 34 * 5) Having complex logic within the synchronized block which calls other objects with 35 * synchronized blocks it is easy to cause Java deadlocks. A deadlock within Java will NEVER 36 * return unlike a normal database deadlock. 37 */ 38 public class Bits extends DBObject implements DependanceListener, ReloadEventListener 39 { 40 /** 41 * Std. DBObject constructor. 42 * 43 * @param def The class of this object 44 * @param dataSource The datasource for this object 45 * @throws Exception A serious problem occurred 46 */ 47 public Bits(DBClass def, DataSource dataSource) throws Exception 48 { 49 super( def, dataSource); 50 } 51 52 /** 53 * Sample secondary cache. 54 * 55 * Step 1. 56 * Enter a synchronized block so that we see a clean version of cache of "Thing" and the cache 57 * of thing is prevented while we are within this block. 58 * 59 * Step 2. 60 * If the cache handle is null then create a new handle and set the local copy. If any 61 * clear cache events are now called it"ll clear the handle and the next call will reload the 62 * cache but this call will continue with the local handle. 63 * 64 * Step 3. 65 * If what the handle points to is null then do the Slow search and set the local handle. 66 * There is some question on whether we should sync the setting of the local handle, I 67 * believe not as another thread not getting the new value (which is flushed fairly frequently) 68 * would just result in another search. If the object"s version of the handle hasn't been 69 * cleared i.e. same as the local version it is now set. 70 * 71 * Step 4. 72 * Return the value of the local handle which will never be null. 73 */ 74 public Thing getCacheThing() throws Exception 75 { 76 Thing holder[] = null; 77 78 // OPTIONAL A: Safer if sync block is here 79 synchronized( this)// Step 1. 80 { 81 holder = cacheThing; 82 83 if( holder == null) 84 { 85 // OPTIONAL B: Faster if the sync block is here. ( must have A or B) 86 holder = new Thing[1]; 87 88 cacheThing=holder; // Step 2 89 } 90 } 91 92 if( holder[0] == null) // Step 3. 93 { 94 DBQuery q = new DBQuery( Thing.DBCLASS_NAME, getDS()); 95 96 q.addClause( /* A complex/slow search criteria */); 97 98 DBObject obj = q.findOne(); 99 // Enter a new synchronized block to prevent reordering of instructions 100 synchronized( this) 101 { 102 holder[0] = obj; 103 } 104 } 105 106 return holder[0]; // Step 4. 107 } 108 109 /** 110 * A dependent of Bits has been added. This may effect the secondary cache so we should clear it. 111 * 112 * @param addedKey The dependent added 113 * @param sourceFieldKey The field that points to this object 114 */ 115 public void eventDependantAdded( GlobalKey addedKey, GlobalKey sourceFieldKey) 116 { 117 clearCache( addedKey); 118 } 119 120 /** 121 * A that we are watching has been changed 122 * 123 * @param obj The DBObject that was reload. 124 */ 125 public void eventReload( DBObject obj ) 126 { 127 clearCache( obj.getGlobalKey()); 128 } 129 130 /** 131 * A dependent of Bits has been removed. This may effect the secondary cache. 132 * 133 * @param removedKey The DBObject was removed. 134 * @param sourceFieldKey The linked field 135 */ 136 public void eventDependantRemoved( GlobalKey removedKey, GlobalKey sourceFieldKey) 137 { 138 clearCache( removedKey); 139 } 140 141 /** 142 * We should only clear the cache if the record changed could have possibly effected the cache. 143 * This method we be called MANY times. So it is cheaper just to clear the cache if in any doubt. 144 * Eg. If you are holding the primary security for this Company and the class of the changed 145 * object is "security" don"t go selecting it here to work out if you should clear it or not. 146 * 147 * This is automatically called by eventDataLoaded() in DBObject which does a programmer check 148 * that you have call the super.clearCache( changedKey); 149 */ 150 protected void clearCache( GlobalKey changedKey) 151 { 152 super.clearCache( changedKey); 153 154 if( /* only clear if the changed object effects the secondary cache */) 155 { 156 synchronized( this)// minimize the time we spend in synchronized blocks 157 { 158 cacheThing = null; 159 } 160 } 161 } 162 163 private Thing[] cacheThing; 164 }

Read More

Support for Search Engine Friendly URLs?
11 Apr 2013

Article titled with non URL friendly characters will be automatically translated to a friendly path when published. http://en.wikipedia.org/wiki/Clean_URL http://www.seochat.com/c/a/search-engine-optimization-help/tools-for-seo-search-engine-friendly-urls/

Read More

Does stSoftware's CMS include intuitive CMS authoring?
9 Apr 2013

1) When authoring a new article the user selects the type of article (specific article types can be customized as required). 2) Edit the newly created article. 3) Publish the article once ready

Read More

Does stSoftware's CMS include the ability to move pages within the navigation structure?
9 Apr 2013

Pages and menu items are easily re-ordered, by changing the items sequence number.

Read More

Does stSoftware's CMS allow for the creation of CMS managed e-newsletter campaigns?
9 Apr 2013

The system has a central Contact Relationship Management (CRM) module. The standard features include bulk emailing and email templates. Email templates with "mail merge" substitutions is a standard feature. All reports can be exported. New contacts can be entered. Sections of the standard forms can be hidden as required or new forms can be created. Easily import contacts using the import wizard, which includes a number of predefined templates. Custom layout can also be used.

Read More

Release Notes: stSoftware's CMS Upgrade
8 Apr 2013

stSoftware's CMS Upgrade Includes new Page Controls which greatly advance the intuitiveness of page editing, a separate Article menu to easily set access priviledges for Article Authors, and a new icon on folders to quickly visualise when they are 'Public'. Page Controls The new Page Controls, offer a simple to understand icon link straight to the exact section of the site builder to edit the site, page, template or style sheet of the page you are viewing. Edit the Current Site: takes you to the...

Read More

stSoftware's CloudBlocks
6 Apr 2013

We are highly experienced in successfully planning and implementing custom cloud and web systems for a variety of organizations, including those with complex data requirements. CloudBlocks universal modules and full range of enterprise level tools provide a substantial head start, reducing costs, time and risk "Our decision to use the ST Engine (CloudBlocks) as the foundation for our system proved to be cost-effective both in terms of development time and resources. ST enabled us to meet our aggressive...

Read More

What databases are supported by Cloud Blocks.
5 Apr 2013

The system has been tested on the following databases PostgreSQL 9.1+ MySQL 5+ Sybase 12+ Oracle 12.5+ Microsoft SQL server 2007+ The Cloud Blocks stores all the data in simple key/value pair tables, which are support by all major databases. All data access is done via the JDBC driver for the database. The main requirements of the database is that it's ACID compliant and has a JDBC driver for the current Java version.

Read More

JobTrack for SME's
2 Apr 2013

Easy online access to the best CRM and management tools to quote and run jobs. View Job P&L's, invoice and record payments, create purchase orders for jobs or inventory. Work better as a team with Job summaries, user grouping and shared documents/files online. Keep up to date with calendar, auto email/sms alerts and day sheets. Talk to us about adding your website for a complete fully integrated, no hassle online business solution. Get connected and work on the go! JobTrack lets you run your business...

Read More

Data Model now supports XPath for text fields
1 Apr 2013

Support for XPath as a standard field type has been added. When a field is marked as XPath then the XPath syntax highlighter will be used by default and the field value will be validated to be a valid XPath expression. XPath syntax editor HTML Editor JavaScript Editor XML Editor CSS Editor

Read More

What are the network and server availability guarantees?
29 Mar 2013

We use Australian web Hosting with 99.99% up time guarantee for non scheduled downtimes The standard hosting package comes with a 2 hour hardware replacement. http://www.digitalpacific.com.au/hosting/dedicated-hosting/dedicated-servers/

Read More

Does Cloud CMS include a powerful search engine with the ability to prioritise results?
29 Mar 2013

Simple search form Sample Advanced search form

Read More

Is stSoftware's CMS responsive Design catering to both desktop and mobile screen sizing?
29 Mar 2013

The system will detect the different form factors and apply different Cascading Style Sheets accordingly. Each component in the site can be hidden or shown based on the browser's form factor.

Read More

Does stSoftware's CMS meet WCAG 2.0 standards?
29 Mar 2013

We check that our custom websites and content complies with http://www.w3.org/TR/WCAG and checked with http://achecker.ca/checker Accessibility checks are built right into the WYSIWYG text editors and validation messages are given when known issues are detected in the site. Our custom sites comply with WCAG 2.0 AAA. There is no need for alternative "accessible" versions of the custom sites as we do not use or recommend the use of unfriendly technologies such as flash or Java applets.

Read More

Does stSoftware's site builder allow the use of frames?
29 Mar 2013

The site designer allows for any valid HTML/CSS to be used. Dynamic HTML components can be edited in a HTML editor. Cascading Style Sheets can be edited in a CSS editor. Rich text components can be edited in a text editor.

Read More

Does stSoftware's CMS include an audit trail and full version control of pages including roll-back ability?
29 Mar 2013

A full audit trail is automatically recorded for every change in the system. Selecting records as they were at any point of time is standard functionality. On each standard screen there is a log tab which shows the changes to this and related records. Each transaction record every change to every record within that transaction. The journal can be searched using a number of very suffocated commands. Any transaction or set of transaction can be reversed by an administrator.

Read More