• Home
  • About
  • Doom II
  • Flog
  • Inspiration

Archive for the ‘Computing’ Category

You can use the search form below to go through the content and find a specific post or page:

Mar 06

AddThis in JQueryUI Modal Dialog Doesn’t Accept Input

If you’ve used AddThis social networking sharing in a JQueryUI dialog, you may have encountered its email sharing form not accepting keyboard or mouse input. This occurs when your JQueryUI dialog has modal set to true, which means all input events are processed only for fields within that section of the DOM. Since AddThis’ email popup markup is appended to <body>, mouse and keyboard events for those DOM nodes are blocked.

What if you want your JQueryUI dialog to be modal but allow a nested modal window from another library? The simple solution is to set modal as false and manually create your overlay <div> that will gray out the rest of the page view to indicate visual focus should be placed on only the dialog.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$('#yourDialog').dialog({
	autoOpen: false,
	draggable: false,
	modal: false,
	resizable: false,
	width: 435,
	minHeight: 494,
	zIndex: 500,
	open: function() {
		$('body').append('<div class="ui-widget-overlay" ' +
			'style="width: ' + $(document).width() + 'px; height: ' +
			$(document).height() + 'px; z-index: 500;"></div>');
	},
	close: function() {
			$('body .ui-widget-overlay').remove();
		}
	});

Using the ui-widget-overlay class on your overlay <div> will follow the same theme as automatically generating a modal dialog would. This example also creates and destroys a DOM node for each instance; you may want to instead use a permanent <div> with $(‘body .ui-widget-overlay’).hide() and $(‘body .ui-widget-overlay’).show() used on the open/close events.

Nov 17

RichFaces #{rich:element()} Workaround

Working with the Seam Framework and RichFaces for over a year, I found their components work in the general case. i.e., the simplest of early oughts HTML forms. But complex AJAX views cause all sorts of difficulties once you get into EL expressions for iterated components (e.g., from <rich:datatable>) within <f:subview>-named scopes.

The biggest challenge has been to get each customized component instance to display and behave correctly within the same view, avoiding situations where the rendered=”" attribute doesn’t evaluate to the expected value (as elements are still in the component tree when rendered=”false”.) For example, the <c:if> test=”" expression is evaluated when the component tree is built, so if any variables within test=”" reference a render-time component such as var=”" of <rich:datatable>, that EL statement may evaulate to null or false. Do not use <c:if> nested in an iterated component like <rich:datatable>. It will only cause hands to be thrown up in the air with disgust.

Another big problem is RichFaces’ rich:element() function not taking namespace into consideration, so the nearest id=”" component-matching sibling or even first match in the whole view will be returned. A parent <f:subview> will not be used unless you explicitly prefix the rich:element() parameter with it.

<f:subview id="mySubview">
<h:inputtext id="myInput" />
<h:graphicimage value="img.png" onclick="#{rich:element('mySubview:myInput').focus()" />
</f:subview>

The issue I had was <f:subview> instances used in customized components, where I would have to require an id=”" attribute to be defined for every usage of components. e.g., <customlibrary:customtag id=”yourComponentId” …/>. This is just tag soup and kinda amateur hour.

I found the best workaround was a JavaScript hack that takes into account how JSF generates the HTML markup of id=”" attributes on each DOM element sourced from the component tree. It is the clientId of each JSF component in the tree concatenated by the ‘:’ character. By truncating up to the last ‘:’, you can attain the parent subview and then append the string parameter usually passed into rich:element() or rich:component(). For example, the oncomplete=”" attribute of <a4j:commandbutton>, the previous method to attain a local DOM reference:

var domObj = #{rich:element('yourComponentId')};

becomes:

var domObj = document.getElementById(this.id.substring(0, this.id.lastIndexOf(':')) + ':yourComponentId');

And that fixes all the wonkiness with changing visibility or using JQuery animations to manipulate JSF components in a contemporary manner. I mean, really RichFaces.

Sep 25

Used hardware. It’s not old, it’s vintage! (It’s old)

I thought about posting this on Craigslist but it’s seriously not even worth the effort meeting people to off-loading such junk. To storage the lot goes!

  • 8-port 10mbps Ethernet Soho Hub
    The manufacturer seemingly wants to 100% hide its brand from the packaging, manual, and equipment. Ethernet 8/16-Port 10Base-T Stackable Hub.
  • Linksys Wireless-G USB Network Adapter with SpeedBooster
    Ideal for your desktop to avoid whinging about too many cords tangling up precious domestic space. Go wireless and give your missus one less reason to complain.
  • Logitech Cordless Desktop 967461-0403
    Wireless keyboard and mouse combo. See above.
  • Tekram Ultra SCSI DC-315U
    50 pin. What up.
  • Intel Core 2 Duo Processor E6300 1.86GHz 1066MHz FSB 2 MB L2 Cache
    Melt it into an ashtray maybe?
  • StarTech ST100S Ethernet card
    Designed for Windows 98!
  • TMS418169DZ-60 SIMM DRAM 2x8mb
    Show the ladies your VGA graphics in Ultima! Guaranteed panty dropper!

Also four CaseLogic flip books for storing CD/DVDs. 30x, 100x, and two 200x.

Are landlines relevant anymore? No. But I have a GE 26920GE2 cordless phone.

Mar 25

Seam Framework AJAX reRender Causing “duplicate attribute”

When using RichFaces’ <a4j:support> to reRender components in the current view upon a certain event, a couple times I’ve come across an XML parsing error in the AJAX response which prevents the rerendering process from completing. The AJAX progress indicator icon just continues into infinite despite the request completing its cycle, albeit in an error state. Firebug’s panel indicates the issue is “duplicate attribute”, where I track down the id=”" attribute as being added twice to at least one of the components being rerendered.

I’ve never been able to break the problem down to a simplified example that I can submit to RichFaces’ JIRA tracking system (or just fix myself) because it seems to be very context specific. Once, the duplicate id=”" was added to <rich:calendar>, despite that component rerendering fine on another page when auto-refreshing one date dependent on a birth date field. Another time, it occurred within <h:selectOneRadio>, but only to the first <f:selectItem> child, with a “:0″ added to the end of the second id=”" added to the component tree’s problem node. I find the problem component is usually the first item rerendered within the list of ids passed to the reRender=”" attribute of <a4j:support>.

The solution I’ve used to fix the problem is ad-hoc and likely won’t always work: reRender another level up in the component tree. It will take longer for more components to redraw themselves by possibly causing more retrievals from the persistence layer but at least that obtuse programming error buried within RichFaces’ codebase disappears. It’s this nonsense that makes working with the RichFaces library a hair-pulling experience at times. Well, that and the Ruskie broken English found in the documentation and support forum.

Oct 19

H2 and DB2 (in)Compatibility Mode

Creating TestNG cases at work, I was having issues creating proper data sets for our test suite since the data model is so complex. I was manually instantiation each class’ entity, populating the object, persisting, then committing the transaction manually. It became obscene for certain objects that referenced up to ten different classes with multiple constraints each. We already have SQL scripts to pre-populate our local JBoss environment so it was a possible solution to use that same data for our tests. I posed the question of a better solution on the interweb and it was suggested I try another DBMS that supports DB2 used by our site and also operates in-memory to increase speed. We already were using Hypersonic but it doesn’t support SQL import from DB2. So I tried H2, using this JDBC connect string to enable the DB2 compatibility mode and trigger our create + populate scripts on the test suite’s startup.

jdbc:h2:mem:localDB;MODE=DB2;INIT=RUNSCRIPT FROM ‘~/workspace/database/create.sql’\;RUNSCRIPT FROM ‘~/workspace/database/populate.sql’

As the H2 features page indicates, “for certain features, this database can emulate the behavior of specific databases. Not all features or differences of those databases are implemented.” Enter what I found out was in fact DB2 incompatibility mode.

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE MAIL_RECEIPT (
    MAIL_RECEIPT_KEY INTEGER NOT NULL,
    DATE TIMESTAMP NOT NULL,
    USER VARCHAR(255) NOT NULL,
    CONTENT VARCHAR(32) NOT NULL,
    FROM VARCHAR(255) NOT NULL,
    TO VARCHAR(1000),
    CC VARCHAR(1000),
    BCC VARCHAR(1000),
    SUBJECT VARCHAR(255) NOT NULL,
    BODY VARCHAR(2000) NOT NULL,
    PRIMARY KEY (MAIL_RECEIPT_KEY)
);

org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement

DB2 allows FROM as a column name. H2 does not. And no, I didn’t originally write this schema so don’t blame me for its poor naming.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
CREATE VIEW REPORT_CAGE_MOVEMENT AS
        SELECT CURRENT_ROOM.ROOM_NAME AS CURRENT_ROOM_NAME,
                CURRENT_ROOM.ROOM_ID AS CURRENT_ROOM_ID,
                ANIMAL_PROTOCOL.PROTOCOL_NUMBER,
                PERSON.LAST_NAME,
                PERSON.FIRST_NAME,
                CAGE.BARCODE_VALUE,
                MOVE_FROM.CAGE_ID AS CAGE_ID,
                MOVE_FROM.ROOM_ID AS FROM_ROOM_ID,
                MOVE_TO.ROOM_ID AS TO_ROOM_ID,
                MOVE_FROM.TO_DATE AS EXIT_DATE,
                MOVE_TO.FROM_DATE AS ENTRY_DATE,
                FROM_ROOM.ROOM_NAME AS FROM_ROOM_NAME,
                TO_ROOM.ROOM_NAME AS TO_ROOM_NAME
        FROM CAGE_MOVEMENT AS MOVE_FROM
        INNER JOIN CAGE_MOVEMENT AS MOVE_TO
                ON MOVE_FROM.CAGE_ID = MOVE_TO.CAGE_ID
                AND MOVE_FROM.TO_DATE + 1 DAY = MOVE_TO.FROM_DATE
        INNER JOIN ROOM AS FROM_ROOM
                ON MOVE_FROM.ROOM_ID = FROM_ROOM.ROOM_ID
        INNER JOIN ROOM AS TO_ROOM
                ON MOVE_TO.ROOM_ID = TO_ROOM.ROOM_ID
        INNER JOIN CAGE
                ON MOVE_FROM.CAGE_ID = CAGE.CAGE_ID
        INNER JOIN PERSON
                ON CAGE.CAGE_OWNER_KEY = PERSON.PERSON_KEY
        INNER JOIN ANIMAL_PROTOCOL
                ON CAGE.ANIMAL_PROTOCOL_ID = ANIMAL_PROTOCOL.ANIMAL_PROTOCOL_ID
        INNER JOIN ROOM AS CURRENT_ROOM
                ON CAGE.ROOM_ID = CURRENT_ROOM.ROOM_ID

org.h2.jdbc.JdbcSQLException: Column “CURRENT_ROOM.ROOM_NAME” not found

Aliases defined in an INNER JOIN clause cannot be referenced in the SELECT’s expression (isn’t that how Hibernate builds all its join queries?)

1
GRANT CONNECT ON DATABASE TO USER SOMEUSER;

org.h2.jdbc.JdbcSQLException: Table “DATABASE” not found

Uh…

1
2
3
4
5
6
7
CREATE TRIGGER BI_STRG_ALLCTIN
NO CASCADE BEFORE INSERT ON STORAGE_ALLOCATION
REFERENCING NEW AS NEW_STORAGE
FOR EACH ROW
MODE DB2SQL
WHEN (NEW_STORAGE.STORAGE_ALLOCATION_KEY IS NULL)
SET (NEW_STORAGE.STORAGE_ALLOCATION_KEY) = (NEXTVAL FOR TCP_POID_SEQUENCE);

org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement “…”; expected “AFTER”

Well it looks like a DB2 trigger doesn’t need that AFTER keyword.

So we’ve switched back to Hypersonic and I’m now creating an XML-based data set using DBUnit.

Jul 07

Seam Framework JSF Component Validation: At Least One Checkbox

For some reason Seam Framework doesn’t include form validation to ensure at least one checkbox is selected out of a group of checkboxes. Like a radio button, but more than one may be selected. One custom code sample was bullshit because validation takes place in JSF lifecycle before the model is updated, so you must wait for the component tree to be built before such a validation can take place.

The solution I found wasn’t optimal but hey, I’m not the only one wanting this simple form functionality.

/resources/WEB-INF/web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
.
.
.
	<context-param>
		<param-name>facelets.LIBRARIES</param-name>
		<param-value>/WEB-INF/compositions.taglib.xml</param-value>
	</context-param>
</web-app>

/resources/WEB-INF/compositions.taglib.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "facelet-taglib_1_0.dtd">
<facelet-taglib>
    <namespace>http://mytaglib.com/jsf</namespace>
    <tag>
    	<tag-name>atLeastOneValidator</tag-name>
    	<validator>
    		<validator-id>atLeastOneValidator</validator-id>
    	</validator>
    </tag>
</facelet-taglib>

/src/main/validator/AtLeastOneValidator.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package yourpackage.validator;
 
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlSelectBooleanCheckbox;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
 
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.faces.Validator;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.ui.component.html.HtmlLabel;
 
@Name("atLeastOneValidator")
@Validator
@BypassInterceptors
@Scope(ScopeType.CONVERSATION)
public class AtLeastOneValidator implements javax.faces.validator.Validator, Serializable {
 
	private static final long serialVersionUID = -4249428843435574402L;
 
	private static Map<String, Map<String, Boolean>> formCheckboxes;
 
	public void validate(FacesContext context, UIComponent component, Object value)
		throws ValidatorException {
 
		List<HtmlSelectBooleanCheckbox> checkboxes = new ArrayList<HtmlSelectBooleanCheckbox>();
		String groupWith = "";
		UIComponent rootComponent = FacesContext.getCurrentInstance().getViewRoot();
 
		if (!(component instanceof HtmlSelectBooleanCheckbox)) {
			throw new ValidatorException(createErrorMessage("atLeastOneValidator can only be used on HtmlSelectBooleanCheckbox components."));
		}
 
		if (component.getAttributes().get("groupWith") != null) {
			groupWith = (String)component.getAttributes().get("groupWith");
		}
 
		if (formCheckboxes == null) {
			formCheckboxes = new HashMap<String, Map<String, Boolean>>();
		}
 
		if (formCheckboxes.get(groupWith) == null) {
			formCheckboxes.put(groupWith, new HashMap<String, Boolean>());
		}
 
		// Store this component's value, queuing to be checked at the last
		// checkbox in the group to see of any component has the value
		// set to "true"
		formCheckboxes.get(groupWith).put((String)component.getAttributes().get("id"), (Boolean)value);
 
		// retrieve all checkboxes under this group in the component tree
		getCheckboxes(rootComponent, checkboxes, groupWith);
 
		// Last checkbox in the component tree of this group, so
		// now check whether at least one is checked.
		if (component.equals(checkboxes.get(checkboxes.size()-1))) {
			boolean atLeastOneChecked = false;
			String styleClass = "";
 
			for (String componentId : formCheckboxes.get(groupWith).keySet()) {
				if (formCheckboxes.get(groupWith).get(componentId)) {
					atLeastOneChecked = true;
					break;
				}
			}
 
			// highlight or unmark checkboxes/labels
			if (!atLeastOneChecked) {
				styleClass = "required";
			}
			for (HtmlSelectBooleanCheckbox checkbox : checkboxes) {
				if (checkbox.getParent() instanceof HtmlLabel) {
					((HtmlLabel)checkbox.getParent()).setStyleClass(styleClass);
				} else {
					checkbox.setStyleClass(styleClass);
				}
			}
 
			if (!atLeastOneChecked) {
				throw new ValidatorException(createErrorMessage("At least one " +
					((groupWith == null || groupWith.length() == 0) ? "" : "\"" + groupWith + "\" ")
					+ "checkbox must be selected."));
			}
		}
 
    }
 
	/**
	 * Recursively traverse a component tree to retrieve all the
	 * HtmlSelectBooleanCheckbox objects containing a specific groupWith=""
	 * attribute.
	 *
	 * @param component Initial invocation should be the root component.
	 * @param checkboxes Initial invocation should be instantiated blank list.
	 * @param groupWith Name/label given to the group of checkboxes. null
	 *                  or a blank string returns ALL checkboxes in the tree.
	 * @return List of child checkboxes in a component tree.
	 */
	protected List<HtmlSelectBooleanCheckbox> getCheckboxes(UIComponent component, List<HtmlSelectBooleanCheckbox> checkboxes, String groupWith) {
 
		if (component != null) {
			for (UIComponent childComponent : component.getChildren()) {
				if (childComponent instanceof HtmlSelectBooleanCheckbox) {
					if ((groupWith == null || groupWith.length() == 0) ||
					    groupWith.equals(childComponent.getAttributes().get("groupWith"))) { 
						checkboxes.add((HtmlSelectBooleanCheckbox)childComponent);
					}
				} else if (childComponent.getChildCount() > 0) {
					getCheckboxes(childComponent, checkboxes, groupWith);
				}
			}
		}
 
		return checkboxes;
	}
 
	private FacesMessage createErrorMessage(String s) {
		FacesMessage message = new FacesMessage();
		message.setDetail(s);
		message.setSummary(s);
		message.setSeverity(FacesMessage.SEVERITY_ERROR);
		return message;
	}
 
}

/view/atLeastOneValidatorTest.xhtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:s="http://jboss.com/products/seam/taglib"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:rich="http://richfaces.org/rich"
                xmlns:mytaglib="http://mytaglib.com/jsf">
 
	<h:form>
		<h:messages />
		.
		.
		.
		<s:label>
			<h:selectBooleanCheckbox value="male" groupWith="Sex">
				<mytaglib:atLeastOneValidator />
			</h:selectBooleanCheckbox>
			<h:outputText value="Duder" />
		</s:label>
		<s:label>
			<h:selectBooleanCheckbox value="female" groupWith="Sex">
				<mytaglib:atLeastOneValidator />
			</h:selectBooleanCheckbox>
			<h:outputText value="Dudette" />
		</s:label>
		<s:label>
			<h:selectBooleanCheckbox value="unknown" groupWith="Sex">
				<mytaglib:atLeastOneValidator />
			</h:selectBooleanCheckbox>
			<h:outputText value="???" />
		</s:label>
		.
		.
		.
	</h:form>
 
</ui:composition>
Jun 16

Hibernate’s Inexplicable Failure

DEBUG [SequenceGenerator] Sequence identifier generated: 1025
DEBUG [AbstractBatcher] about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
DEBUG [ConnectionManager] aggressively releasing JDBC connection
DEBUG [ConnectionManager] releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
DEBUG [AbstractSaveEventListener] generated identifier: 1025, using strategy: org.hibernate.id.SequenceGenerator
DEBUG [AbstractSaveEventListener] generated identifier: 1025, using strategy: org.hibernate.id.ForeignGenerator
DEBUG [AbstractFlushingEventListener] processing flush-time cascades
DEBUG [AbstractEntityManagerImpl] mark transaction for rollback

It’s a one-to-one foreign key generated by a DB2 sequence to persist multiple entities in one transaction. It seems to be created and assigned to the two entities mapped to each other but I have no clue why the JPA/Hibernate transaction decides it’s a no go as all the required fields are assigned in the objects being persisted. I’ve been working on this Seam Framework wizard for a week and this is an example of roadblocks in the dozens that have taken me to varying edges of sanity when it comes to troubleshooting this project.

I can only take solace in knowing once these niggles are straightened out, the rest of the system will be easy to implement. But the technological learning curve and unseen issues derived from requirement complexities make me believe Seam is a terrible framework solution. Absolutely terrible.

Update July 2: This error was occurring because a RunTimeException was thrown in the Hibernate library which wasn’t displaying in the server’s log or on the JBoss stack trace because the entrance method was invoked using EL. Wrapping my persist() call with a try {} containing a generic Exception catch() revealed it was a constraint violation occurring on a value set as a Hibernate TrueFalseType (char(1) with ‘T’ or ‘F’ values) when it should’ve been a YesNoType (char(1) with ‘Y’ or ‘N’ values).

May 01

Disable <rich:inplaceInput> in Seam

You can’t. But you can use <h:outputText> for read-only visitors and <rich:inplaceInput> for write-enabled logged-in users. Customize the inplaceInputTest.xhtml s:hasRole() parameter to give the proper boolean value on whether the logged-in user is an administrator or not of the field you want to control access of.

/resources/WEB-INF/web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
.
.
.
	<context-param>
		<param-name>facelets.LIBRARIES</param-name>
		<param-value>/WEB-INF/compositions.taglib.xml</param-value>
	</context-param>
</web-app>

/resources/WEB-INF/compositions.taglib.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "facelet-taglib_1_0.dtd">
<facelet-taglib>
    <namespace>http://mytaglib.com/jsf</namespace>
    <tag>
        <tag-name>inplaceInput</tag-name>
        <source>../compositions/inplaceInput.xhtml</source>
    </tag>
</facelet-taglib>

/view/compositions/inplaceInput.xhtml

1
2
3
4
5
6
7
8
9
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:rich="http://richfaces.org/rich">
 
	<rich:inplaceInput value="#{value}" showControls="true" editEvent="ondblclick" layout="block" rendered="#{isManager}" />
	<h:outputText value="#{value}" rendered="#{not isManager}" />
 
</ui:composition>

/view/inplaceInputTest.xhtml

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:s="http://jboss.com/products/seam/taglib"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:rich="http://richfaces.org/rich"
                xmlns:mytaglib="http://mytaglib.com/jsf">
 
	<mytaglib:inplaceInput value="Example text" isManager="#{s:hasRole('module_manager')}" />
 
</ui:composition>
Apr 27

JBoss Seam, Hibernate, DB2, and Lucene Indexing

Yeah, it’s all happening. Migrating a CMS to Java’s Seam framework using an existing DB2 schema/dataset, the Lucene search indexes on a few tables caused DB2 error SQL1584N. The bizarre part was it was only happening locally on my machine, but not for two others at work, who each have the same amount of RAM. It may have been because I was on Ubuntu and the other devs use Red Hat Enterprise Linux.

System temporary tablespace with page size of at least pagesize could not be found.
Explanation:
A system temporary table space was required to process the statement. There was no system temporary table space available that had a page size of pagesize or larger.

The statement cannot be processed.

User Response:
Create a system temporary table space with a page size of at least pagesize.

sqlcode: -1584

sqlstate: 57055

Following the instructions for ensuring system temporary table spaces page sizes meet requirements, I ran DB2 query:

LIST TABLESPACES SHOW DETAIL

…checking for the entry with Type = System managed space, Contents = System Temporary data, I confirmed the page size used was 32k, which is the highest possible with DB2.

After googling a bit, it seemed the solution was to increase the bufferpool size used by the tablespace giving me this error. The instructions for adjusting the bufferpool are to simply run DB2 query:

SELECT * FROM syscat.bufferpools

Confirm the bufferpool size, then increase the number of pages available until the Lucene indexing doesn’t shit the bed. e.g.,

ALTER BUFFERPOOL ibmdefaultbp SIZE 2000

During this process, you may get the error:

SQL20169W The buffer pool is not started.

sqlcode: +20169

sqlstate: 01654

This just means the change won’t take effect until DB2 is restarted, so execute:

db2 force applications all
db2stop
db2start

Now, if you have the Seam observer Java annotation setup for Lucene indexing, this DB2 error will begone on the next deploy. And yes, this whole post makes me yearn for a non-Java project started from scratch.

Apr 19

The Screwdriver and the Damage Done

iPod dismantled

When visiting the family last Christmas, I decided to avoid my iPod earbuds on the trip due to their deteriorated state (srsly, do they last more than three months without having their volume cut in half or the wire fraying?) Instead I took my Seinnhesier HD-595 for the plane/car ride for an obviously superior listening experience. I’ve done this before but this had an unexpected consequence: destruction of my iPod Touch’s audio plug.

On the plane’s return trip, the iPod would only play full stereo audio if pressure were applied to the headphone jack. But not left or right pressure like most jack problems, but pushing straight in. It seemed like an adjustment of the iPod’s internal jack would fix it easily enough. Problem was how could I easily open it? The manufacturing process tightly seals iPods to the point that they need a spudger (sharp plastic tool) to pop them open without causing aesthetic damage. One Amazon retailer won’t ship to Canada and I couldn’t find other online computer retailers carrying such items. My anti-eBay/Paypal policy prevented other channels.

So I just threw “fix iPod” onto my never-shrinking to-do list and held my iPod tight for use in the near future. With winter weather, that proved harder as the act left holes in my coat pockets along with the odd cramped hand (so business as usual – heyo, etc.) At work, I even devised a way to weigh down the earbud’s jack using a coffee mug. How these temporary solutions end up as three month-long habits are indicators of my lifelong love with procrastination.

Last week I decided to finally suck it up and fixed the damned thing or buy a new iPod already. I discovered it’s seemingly impossible to find a brick-and-mortar in Toronto that sells spudgers. The few College/Spadina stores I asked seemed offended that I would do such a repair myself without them to price gouge another witless customer. And the pretentious cunt as iRepair that just stood behind the counter staring at his Mac screen adjusting an iTunes playlist without even looking me in the eye while taking a few seconds between sentences while plainly repeating, “that’s how it’s done”… you can go fuck yourself.

Instead I found an eyeglass screwdriver in a kit given as a Christmas gift a few years ago. It ended up causing scratches after it took 10 minutes to pop open the iPod. There’s a decent iPhone jack repair tutorial available but bits smaller than those for eyeglasses work best for dismantling iPod components.

Adjusting the jack’s ground contact didn’t fix the problem, so I took off the circuit board where flash memory is soldered to get better access to secure the jack down. Since it was a two year old iPod Touch, I didn’t feel a need to be delicate in the process. Needless to say, a small circuit board piece broke off by the screw and after reassembly the iPod’s screen went to an iTunes & USB cable icon bootup loop that interrupted each iteration. And I couldn’t get to recovery mode by pressing home+power.

I basically bricked my iPod Touch because I didn’t bother buying the right tools.

Then I made the mistake of jokingly suggesting in a Facebook status update about getting a replacement iPhone, which spawned a 19 comments argument between three friends of Apple vs. Blackberry vs. Android. There’s still more than a year left of my terrible Canadian mobile phone contract; there will be no smartphone purchases. I’ll likely be a bitch by getting another iPod Touch that’ll require a $70 repair in another two years, simply because I like the WiFi+Safari+Facebook+Tweetdeck when I’m too lazy to get out of bed or away at a hotel.

Lessons learned? Get the right tools if you want to DIY it and don’t use huge indoor headphones for an iPod (big things, small holes).

Mar 27

Install Linux, Problem (Not) Solved

Ubuntu partition setup

An activity I don’t miss from my university days: setting up a Linux dev box. At work, we decided to get away from Red Hat Enterprise Linux (which would seriously take over two minutes to boot) and test out Ubuntu on a development box. However, the 9.10 installer was decidedly uncooperative. Firstly, it booted to a screen of garbled colours since the setup uses generic drivers and I had dual Dell 1907FPV monitors. The simple fix was to disconnect one of the monitors and install Nvidia’s proprietary drivers for a GeForce 6800 after installation had completed.

However, the real showstopper came at step 4 of 7 for setting up the partitions where the above disabled screen appeared. The only selectable option is to click “Forward”, which provides this dialog error message:

No root file system is defined. Please correct this from the partitioning menu.

Setup obviously wasn’t recognizing the hard drive and playing with BIOS settings didn’t help. This machine doesn’t exactly require crazy drivers since it has an Intel; 82945G Express Chipset (mobo) and ATA Maxtor 7L250S0 250gb (hd). The solution was to quit the installer, which then automatically boots into a mounted Ubuntu preview, and from the top menu select Application » Terminal, typing:

sudo apt-get remove dmraid

Running the installation from within Ubuntu now allowed setup to recognize my existing partitions for reconfiguration. The rest of the steps were relatively painless, aside from a file copy hiccup at 70% due to a DVD-R thumbprint (of course you can’t umount within the installation to eject the disc, clean, and insert back into to try again… so I had to start that whole install process again.)

When it came time to add a local NFS, inserting its entry to /etc/fstab and attempting the mount gave error message:

mount: wrong fs type, bad option, bad superblock on tcpserver:/tcphome, missing codepage or helper program, or other error (for several filesystems (e.g. nfs, cifs) you might need a /sbin/mount. helper program) In some cases useful info is found in syslog – try dmesg | tail or so

Nope, not enabled OOTB, so I had to run:

sudo apt-get install nfs-common

Using Synaptic Package Manager to retrieve all the software development tools was a well-done process, so it must be the year of the Linux desktop! Or not.

Jan 30

Relative Time in JavaScript

There are plenty of robust JavaScript libraries out there for handling timestamps, but I came across a situation where I needed a lightweight solution to represent dates returned from a JSON request that meaningfully indicate the proximity to an expiration. Something to the effect of, “Feb 3, 2010 (in 5 days)”, or, “Jan 1, 2010 (30 days ago)”. This implementation doesn’t handle language localization or customized messages like optionally breaking down to also include years or decades. But it does contain nested ugliness with magic numbers ahoy. I didn’t say it was elegant; just lightweight (see: took less than 10 minutes to create, even with clever pluralization of the time’s unit!)

 
/**
 * Get a string describing the difference between two timestamps.
 *
 * @var time Date object to retrieve relative description string of
 *
 * @return string indicating how far in the past or future the date is
 */
function getTimeDiff(time)
{
 
	var now = new Date();
	var diff = now.getTime() - time.getTime();
 
	var timeDiff = getTimeDiffDescription(diff, 'day', 86400000);
	if (!timeDiff) {
		timeDiff = getTimeDiffDescription(diff, 'hour', 3600000);
		if (!timeDiff) {
			timeDiff = getTimeDiffDescription(diff, 'minute', 60000);
			if (!timeDiff) {
				timeDiff = getTimeDiffDescription(diff, 'second', 60);
				if (!timeDiff) {
					timeDiff = 'just now';
				}
			}
		}
	}
 
	return timeDiff;
 
}
 
/**
 * Get a string describing the difference between two timestamps,
 * based on unit of time. Run sequentially starting from the greatest
 * unit and stopping once this function doesn't return null.
 *
 * @var diff current time (in millisec since 1970) minus time to compare to.
 *           e.g., (new DateTime()).getTime() - someOtherTime.getTime()
 * @var unit Time description to place in a string. e.g., 'hour'
 * @var timeDivisor Number to divide into milliseconds to get the value
 *                  described by the unit. e.g., 60*60*1000 for 'hour'
 *
 * @return A string representing the difference between the given time
 *         and now, if there is a difference for the given unit of time.
 *         null is returned if they're the same (e.g., provided time falls
 *         on today when unit = 'day')
 */
function getTimeDiffDescription(diff, unit, timeDivisor)
{
 
	var unitAmount = (diff / timeDivisor).toFixed(0);
	if (unitAmount > 0) {
		return unitAmount + ' ' + unit + (unitAmount == 1 ? '': 's') + ' ago';
	} else if (unitAmount < 0) {
		return 'in ' + Math.abs(unitAmount) + ' ' + unit + (unitAmount == 1 ? '' : 's');
	} else {
		return null;
	}
 
}
Dec 04

The Bitter Web Producer

  • How a Web design Goes Straight to Hell
  • Clients From Hell
  • Business Guys on Business Trips

Read these links and a proper catharsis should be met. But we’re a fussy bunch.

Oct 18

Disconnected Toronto Concert Listings

Just thought I’d highlight issues I have with concert listings in my dear city. I’m talking about advanced ticketing for bands that can pack medium-sized halls. The main problem I have with all of them is a lack of centralized event listings or even a common format where concert-goers can aggregate dates and details through RSS feeds or RESTful apps. Venues in this camp include El Mocambo, Lee’s Palace, Horseshow Tavern, Phoenix, Opera House, and The Mod Club (which has Flash listings that your browser can’t even search.) Sneaky Dee’s are in there too but they haven’t had a working events calendar since their latest event booker started in June. Promoters like Against the Grain and rootmeansquare also have separate listings that go across multiple venues.

Up to now, I’ve been tracking upcoming concerts by using local record stores, Rotate This and Soundscapes. They’re the best way for downtownizens to avoid Ticketmaster/LiveNation service fee money-grabs, instead getting tickets in-person with no scalping pretense. There are still issues with certain events falling through the cracks (no advanced ticketing?) and these listings not allowing custom sort criteria such as band name, date, price, or dated added to listings.

What I’d really like to see is for venues and promoters to use a feed or web service with full show details. It’s great when they show band set times, convenient when the openers are inexplicably terrible. With the event name, venue name, address, and list of artists with start times alongside links on where to buy tickets, all the necessary details are there for a potential customer. It’s a pipedream to get all these competitive profit-seeking interests to use one common service, so maybe just RSS can even the playing field.

Of course, the obvious part-solution to this whole problem would be if promoters would use Facebook . For aforementioned Sneaky Dee events held this summer, the only way I was able to find out about shows was through my friends’ joining Facebook events created by the venue’s event organizer. ATG’s group has also been on the ball (at times) by creating events for headlining artists and sending out invites to every member of their group. However, if all promoters pulled this, you’d be bombarded by dozens of invites everyday. It would be much better to narrow down invites based on genre (crowdsourcing killing art aside) but Facebook doesn’t have that capability unless each promoter created multiple groups. Then the overhead becomes overkill. The coolest filter I can think of is only allow invites for bands in a user’s Last.FM artist library but eh, that’s a web 2.0 mash-up where there wouldn’t be enough of a userbase to make it worthy. Last.FM does have an event recommendation system but it’s focused on the next week’s events that have likely sold out.

I believe Toronto is the second highest ranked city as far as Facebook registration number go (behind London), especially in the core audience of 18 to 35 year olds. Having centralized event pages with integration for Facebook, MySpace, and Last.FM would allow for advertising using free tools (no hosting, monthly registration, or web site development/maintenance fees) that take advantage of social networking for more pleasant concert organizing experiences.

But, of course:

Oct 16

Sony Ericsson K850i Contacts Sync

After having a Sony Ericsson K850i for almost 18 months (and under contract until June 2012 unless I buy an unlocked device), I thought it was about time to get all my contacts in order. Trying to find a seamless way to keep the phone, iPod Touch, and Hotmail contacts all synchronized wasn’t so easy. My main stumbling block was me being a stickler for a non-Gmail email address I’ve been using since 1997, but a solution must exist.

I had to install the Sony Ericsson PC Suite which promptly gave me the message:

Reading mobile phone’s contacts failed.

Few relevant Google results match my problem other than this site which claims you need to install drivers (which the PC Suite does.) The error message is also mentioned nowhere in Ericsson’s support pages.

A year ago, I had disabled phone modem-related drivers because every time it was connected via USB, Windows would popup five “new hardware installed” notifications. Every time. So I enabled those in Device Manager and also had to reinstall the PC Suite before my phone would be recognized for syncing.

You can use Google Sync for over-the-air Gmail contacts but I don’t have any data plan so I’m not touching that with Canadian per-kilobyte pricing. Over USB, Thunderbird isn’t an option; only MS Outlook. So my solution was to download a Hotmail .csv, import that into MS Outlook Express (like I’d pay for MS Office), then use the PC Suite to do the final sync.

Problem is I still had contacts on my SIM that the software can’t remove. The K850i has no method on the phone itself to batch delete contacts from the SIM. I had to do that manually or else there were redundant contacts for most people. Sony Ericsson’s OS already has latency issues, so it was unbelievably laggy on 165 contacts where I’d have to wait half a second between deletes for the list to (twice) refresh. Then the PC Suite offered no option to only sync contacts with (mobile, work, or home) phone numbers so I had to manually remove entries under the software’s Contacts tab which luckily has a table with phone numbers in view. So in the end, I found no elegant solution to sync contacts.

Oct 14

With Nothing to Offer: Aggregate

Social networking activity feeds are all the rage, where it’s now necessary to microblog every detail of our lives. I think it’s funny that Windows Live profiles started a new promotion for, “Your Online Updates Together in One Place”, which finally conceding that Microsoft is giving up on that game. Windows Live Spaces failed, so they now leech off other systems to hide how inactive users are with Microsoft’s services. I’ve already highlighted how I think Live profiles are jokes.

Now most timestamped updates you’ve made online can be placed in one activity feed. I have noticed other apps are following along the same lines when it comes to displaying user details. My profiles for Nine Inch Nails and Dredg both have iconized links to my third party accounts, with nin.com integrating YouTube videos and Flickr photos into its internal interface using each services’ tagging system. TweetDeck and other third party apps already split up Twitter threads alongside Facebook statuses. As third-party APIs open up, more sites will start combining shared information.

By mixing in text typed by me in this blog, Twitter, Facebook, Last.FM, film log, Goodreads, Windows Live Messenger statuses, and posts from the half-dozen forums I frequent (ir)regularly, you can probably piece together a rather disturbing stalker tracker. Is it banal? Most likely, but at least you’re now more likely to have alibi after getting mistakenly picked up by the po-po.

Oct 13

LiveDocx: Pleasant But Lacking

On a work project, one client wanted printable documents that weren’t in HTML. So we built RTF template in MS Word, formatted variable placeholders as <<VARIABLE_NAME>>, and in PHP read the whole file to run str_replace on those variable names. This didn’t work well because had to cut and paste RTF codes into separate files (sometimes up to 10) for one document when there were repeating sections. Any change requests to that document also meant breaking it all up again from scratch. Many times we came across the document not formatting correctly in all apps (MS Word, Wordpad, and OpenOffice on Windows or Mac.)

An alternate was found in LiveDocx with phpLiveDocx just recently added to the Zend Framework library. Instead .docx files are created in MS Word with MailMerge fields used for variable placeholders and loop start/end points defined by MS Word bookmarks. The phpLiveDocx code is dead simple too. We could also now export the completed document into .doc or .pdf while the whole service is free since it’s now part of the Zend Framework.

However I came across major limitations in these templates, specifically:

  • No nested loops – I wanted to group employees into their home country and have a separate table for each but the MergeBlock names created by the MS Word bookmark functionality are used as identifiers. I wanted it setup something like:

    1
    2
    3
    4
    5
    6
    
    country_start
    	[country_name]
    	employee_start
    		[employee_name] / [employee_visa_number] / [employee_visa_expiry]
    	employee_end
    country_end

    However the LiveDocx assign() functions ignore any inner loops and variables, so I would have to know beforehand what countries to include and manually created those tables separately (like having one block named “country_canada” and “country_usa”) which isn’t optimal when there are hundreds of countries worldwide.

  • No repeating blocks – Again, since the bookmarks are identifiers, I can’t reuse a block. So I couldn’t pull off even/odd table row background shading to improve readability or have a separate table for each country unless I manually created them separately. I ended up just making one flat table with the database SELECT query alphabetically sorting employees by their country. The redundant repeating country field decreases readability, which is the whole purpose of the printed document.
  • Poor template error feedback – This is probably the issue that ticked me off the most. The first time I built a template, the document generated would either being missing elements (like a table cell) or some elements would be aligned on the document in a nonsensical format. Only through trial-and-error was I able to diagnose and fix the layout issues I was having. The best tip I can give is if there is a problem, add more linebreaks. I found you can’t have two bookmarked blocks start/end on the same line. So for a table row, add two line breaks before adding in another block for LiveDocx to parse. Some of the exception messages thrown by the web service when your MergeMail field assignments aren’t properly called also make zero sense.

That’s really it. I’m impressed by the ease to integrate a web app into this service but its layout limitations leave a lot to be desired.

Sep 26

Yell at Bell

My home’s Internet connection was out for over 3 weeks. Incoming wall of text.

Aug 20
A massive thunderstorm goes through the GTA, with multiple tornadoes touching down. Our Internet connection with Teksavvy stops at 7:11PM. I get home after footy training (where I was standing in the middle of a UoT sports field with lightning all around me which was really not awesome) to find our 2wire modem’s DSL indicator is blinking red, meaning no signal. I figure it’s a temporary outage and go to bed.

Jun 06

Eye Over You

The proliferation of mobile devices and cost of GPS hardware implementations have raised interesting new methods for positioning end-points on the globe. IP address lookups are completely inaccurate (when in downtown Toronto near the lakeshore, one IP locator placed me near the 401, about 10km away.) I thought it was interesting that the iPod Touch OS doesn’t contain GPS (as its iPhone sibling), but instead uses Skyhook Wireless, a location lookup based on the (password secured or not) wireless router MACs within your range. Needless to say, it’ll only be accurate in dense urban areas. But almost pinpoint it is.

May 08

Like a Burmese Python

Kenny Powers Dance

I guess Microsoft’s sad attempt at playing catch-up on the social media game led them to start pushing Messenger status updates on their rarely-visited Windows Live user profiles in order to force an activity feed that nobody will follow. No biggie, except by default they’re published to anyone.

Older Posts »

Derek MacDonald


  • Photo Stream
  • Categories
    • Australia
    • Computing
    • Film & TV
    • Food
    • Gaming
    • General
    • Music
    • Sports
    • Visual Art
  • Search






  • Home
  • About
  • Doom II
  • Flog
  • Inspiration

© Copyright Derek MacDonald. All rights reserved.
Designed by FTL Wordpress Themes brought to you by Smashing Magazine

Back to Top