<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-11126983</id><updated>2026-10-22T21:12:32.498+11:00</updated><category term="three.js"/><title type='text'>Matt&#39;s Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>46</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-11126983.post-488232611808512035</id><published>2017-04-21T06:58:00.002+10:00</published><updated>2017-04-21T06:58:20.381+10:00</updated><title type='text'>@Nullable and @NotNull Annotations in IntelliJ</title><content type='html'>Every time I have to configure a new instance of IntelliJ I have to hunt for these settings to ensure that inspections catch possible NPEs to code annotated with&amp;nbsp;@NotNull, so I&#39;m &amp;nbsp;writing the steps here,&lt;br /&gt;
&lt;br /&gt;
The inspection is under &lt;b&gt;Java -&amp;gt; Probable bugs -&amp;gt; Constant conditions &amp;amp; exceptions&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4Cgnr7aumDqG7zlWYFi-83Fo1Dp18DvHNKqzV4GHbbKIqEx4U8M0HGAdA8LBcqlTGTHFSYAgwIFLU66KchGNUZiMfXAw6GQZBUXJI42f4MtOQgQLmMTnFHQna4J9HXpYFsbFQ0g/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;370&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4Cgnr7aumDqG7zlWYFi-83Fo1Dp18DvHNKqzV4GHbbKIqEx4U8M0HGAdA8LBcqlTGTHFSYAgwIFLU66KchGNUZiMfXAw6GQZBUXJI42f4MtOQgQLmMTnFHQna4J9HXpYFsbFQ0g/s640/Capture.PNG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
You will also probably need to add the javax validation annotations, which can be done by clicking &lt;b&gt;Configure annotations&lt;/b&gt;.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_xUhKT8QqDTqHVxnngEAd-ZlDa0aqjFujf2ehSd0wakqeqAylbZvAZWRzgObeT_R066oBlfDo3WpA7nnUDm_Z-IvrsCy8j64Dzhqj5iWaoH9HnzQyiynamJMzXNSGrIVlYgK4jg/s1600/Capture2.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_xUhKT8QqDTqHVxnngEAd-ZlDa0aqjFujf2ehSd0wakqeqAylbZvAZWRzgObeT_R066oBlfDo3WpA7nnUDm_Z-IvrsCy8j64Dzhqj5iWaoH9HnzQyiynamJMzXNSGrIVlYgK4jg/s320/Capture2.PNG&quot; width=&quot;202&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/488232611808512035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/488232611808512035' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/488232611808512035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/488232611808512035'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2017/04/nullable-and-notnull-annotations-in.html' title='@Nullable and @NotNull Annotations in IntelliJ'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4Cgnr7aumDqG7zlWYFi-83Fo1Dp18DvHNKqzV4GHbbKIqEx4U8M0HGAdA8LBcqlTGTHFSYAgwIFLU66KchGNUZiMfXAw6GQZBUXJI42f4MtOQgQLmMTnFHQna4J9HXpYFsbFQ0g/s72-c/Capture.PNG" height="72" width="72"/><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-53384531282701727</id><published>2017-04-12T08:29:00.003+10:00</published><updated>2017-04-12T08:31:10.120+10:00</updated><title type='text'>Is this code functional vomit?</title><content type='html'>Hi. My name is Matt, and I have pull-request-criticism-ophobia. I die a little inside every time someone tells me what I noob I have been when writing code.&lt;br /&gt;
&lt;br /&gt;
I am also a Java 8 developer, and have developed an interest in functional programing. One day I found the Optional class, and my life has been a living hell ever since. Here&#39;s why.&lt;br /&gt;
&lt;br /&gt;
I was tasked to write the following code:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;The method must convert an object of type ObjectA to an object of type ObjectD&lt;/li&gt;
&lt;li&gt;The conversion will be done via a number of service objects that will do the hard work of converting ObjectA to ObjectB, ObjectB to ObjectC, and ObjectC to ObjectD.&lt;/li&gt;
&lt;li&gt;These services do not throw exceptions, or throw only runtime exceptions.&lt;/li&gt;
&lt;li&gt;These services return a mixture of null or empty Optional objects to indicate that no conversion could take place.&lt;/li&gt;
&lt;li&gt;The method should return an empty Optional if the supplied ObjectA was null, or if any conversion returns null or an empty Optional.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
Because I have read no less than 3 blog posts on Monads (so many cute pictures of cats in boxes) and have spent several hours reading up on functional programming, I feel like I am a functional programming boss, and pump out the following code:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;public class MyClass {&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; @Autowired&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; private ConvertAToB convertAToB;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;&amp;nbsp; @Autowired&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; private ConvertBToC convertBToC;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;&amp;nbsp; @Autowired&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; private ConvertCToD convertCToD;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;&amp;nbsp; public Optional convertAToD(ObjectA objectA) {&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; return Optional.ofNullable(objectA)&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; .flatMap(convertAToB::convertToB)&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; .map(convertBToC::convertToC)&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; .map(convertCToD::convertToD);&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; }&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;}&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The code is clean, declarative and functional. I’ve neatly dealt with nulls and empty optionals without nested if blocks. I’m feeling pretty good at this point. Until I go and listen to a &lt;a href=&quot;https://virtualjug.com/vjug24-session-optional-the-mother-of-all-bikesheds-by-stuart-marks/&quot;&gt;talk by Stuart Marks&lt;/a&gt;, one of the guys who designed the Optional class, who very clearly says this:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Optional is intended to provide a limited mechanism for library method return types where there is a clear need to represent “no result,” and where using null for that is overwhelmingly likely to cause errors.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
He goes on to list a number of rules around the use of the Optional class:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Never, ever, use null for an Optional variable or return value.&lt;/li&gt;
&lt;li&gt;Never use Optional.get() unless you can prove that the Optional is present.&lt;/li&gt;
&lt;li&gt;Prefer alternatives to Optional.isPresent() and Optional.get().&lt;/li&gt;
&lt;li&gt;It’s generally a bad idea to create an Optional for the specific purpose of chaining methods from it to get a value.&lt;/li&gt;
&lt;li&gt;If an Optional chain has a nested Optional chain, or has an intermediate result of Optional, it’s probably too complex.&lt;/li&gt;
&lt;li&gt;Avoid using Optional in fields, method parameters, and collections.&lt;/li&gt;
&lt;li&gt;Avoid using identity-sensitive operations on Optionals.&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
My heart sinks. It seems like my clever method chaining with map and flatMap just violated the intended purpose of the Optional class. Given my fear of criticism in pull requests, I quickly refactor the code to only use Optional as a return type and remove all method chaining:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;public class MyClass {&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; @Autowired&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; private ConvertAToB convertAToB;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;&amp;nbsp; @Autowired&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; private ConvertBToC convertBToC;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;&amp;nbsp; @Autowired&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; private ConvertCToD convertCToD;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;&amp;nbsp; public Optional convertAToD(ObjectA objectA) {&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; if (objectA != null) {&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; Optional objectB = convertAToB.convertToB(objectA);&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; if (objectB.isPresent()) {&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ObjectC objectC = convertBToC.convertToC(objectB.get());&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (objectC != null) {&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ObjectD objectD = convertCToD.convertToD(objectC);&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return Optional.ofNullable(objectD);&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; }&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; &amp;nbsp; return Optional.empty();&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&amp;nbsp; }&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;}&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Great. I’m pretty sure I’m no longer violating the intended use of the Optional class here. It is used as a return type and nothing more, and I am no longer chaining methods to get a value.&lt;br /&gt;
&lt;br /&gt;
But, try as I might, I just can’t see this refactoring as better code.&lt;br /&gt;
&lt;br /&gt;
Help me internet. Is Optional map and flatMap chaining an example of clean code, or is it functional vomit that abuses the limited scope of the Optional class?</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/53384531282701727/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/53384531282701727' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/53384531282701727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/53384531282701727'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2017/04/hi.html' title='Is this code functional vomit?'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-2942100343379621898</id><published>2015-08-17T10:11:00.002+10:00</published><updated>2015-08-17T10:18:56.525+10:00</updated><title type='text'>Configuring Wildfly for HTTPS in a post Poodle world</title><content type='html'>If you have ever run into “ssl_error_no_cypher_overlap” errors trying to configure Wildfly to use HTTP then you have probably cursed the lack of decent documentation for configuring Wildfly now that browsers have disabled a lot of insecure SSL cyphers. This is how I got around the problem.&lt;br /&gt;
&lt;br /&gt;
First you need a self signed key. This can be created with the command:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;keytool -genkey -alias mycert -keyalg RSA -sigalg SHA256withRSA -keystore my.jks -storepass secret&amp;nbsp; -keypass secret -validity 9999&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Then you need to configure Wildfly to accept a list of known cyphers. Mozilla has a nice list of cypher codes for high security, compatibility etc at &lt;a href=&quot;https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility&quot;&gt;https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The problem is that this list has the OpenSSL key names, and Wildfly needs the RFC names. So you need to map one to the other using the table at &lt;a href=&quot;https://testssl.sh/openssl-rfc.mappping.html&quot;&gt;https://testssl.sh/openssl-rfc.mappping.html&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
What I ended up with was this list, defined in a enabled-cipher-suites attribute. This list is the Modern Compatibility list. This is added to the &amp;lt;subsystem xmlns=&quot;urn:jboss:domain:undertow:2.0&quot;&amp;gt; element.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&amp;lt;https-listener name=&quot;https&quot; socket-binding=&quot;https&quot; security-realm=&quot;UndertowRealm&quot; enabled-cipher-suites=&quot;TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,    TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_DSS_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA&quot; /&amp;gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
You then need to configure the self signed key with this. It goes under the &amp;lt;management&amp;gt;&amp;lt;security-realms&amp;gt; element.&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;&amp;lt;security-realm name=&quot;UndertowRealm&quot;&amp;gt;
                &amp;lt;server-identities&amp;gt;
                    &amp;lt;ssl&amp;gt;
                        &amp;lt;keystore path=&quot;my.jks&quot; relative-to=&quot;jboss.server.config.dir&quot; keystore-password=&quot;secret&quot; alias=&quot;mycert&quot; key-password=&quot;secret&quot;/&amp;gt;
                    &amp;lt;/ssl&amp;gt;
                &amp;lt;/server-identities&amp;gt;
            &amp;lt;/security-realm&amp;gt;
&lt;/b&gt;</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/2942100343379621898/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/2942100343379621898' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/2942100343379621898'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/2942100343379621898'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2015/08/configuring-wildfly-for-https-in-post.html' title='Configuring Wildfly for HTTPS in a post Poodle world'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-2595020736609610159</id><published>2015-07-15T07:09:00.001+10:00</published><updated>2015-07-15T07:42:49.229+10:00</updated><title type='text'>Authenticating via Kerberos with Keycloak and Windows 2008 Active Directory</title><content type='html'>&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
The following instructions show you how to configure Keycloak with Windows AD in order to use Kerberos authentication.&lt;/div&gt;
&lt;h2 style=&quot;clear: both; text-align: left;&quot;&gt;
Assumptions&lt;/h2&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;The Kerberos realm is VIRTUAL.LOCAL&lt;/li&gt;
&lt;li&gt;The hostname used to access Keycloak is virtual.local. This just means we are running Keycloak on the domain controller. In production virtual.local will be replaced with something like keycloak.dev.virtual.local or something like that, giving you a SPN of&amp;nbsp;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;HTTP/keycloak.dev.virtual.local@VIRTUAL.LOCAL&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 style=&quot;clear: both; text-align: left;&quot;&gt;
Configuration&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Create a windows domain account called Keycloak.&lt;/li&gt;
&lt;li&gt;Run the following command to assign a SPN to the user and generate a keytab file:&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;ktpass -out keycloak.keytab -princ HTTP/virtual.local@VIRTUAL.LOCAL -mapUser Keycloak@VIRTUAL.LOCAL -pass password1! -kvno 0 -ptype KRB5_NT_PRINCIPAL -crypto RC4-HMAC-NT&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Verify the SPN has been assigned to the user with the command:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;setspn -l Keycloak&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Configure the LDAP settings in Keycloak like this. Since we are running Keycloak on the domain controller, we reference LDAP via the local loopback address. Obviously this would change in most production environments.&lt;/span&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW4TmyAKXaSWnvIQEiiIohcnYDCTOaBZ5AvZOz89xDWIHQUzwz_0BNTGZs1ZZ_mKDzoPY9Yx-cU4LCkZ0nuppdyEJk8j2cAF2-t06eAGrDi8tDQUlcwHGmeqrMD1mrkkqXBQxjgQ/s1600/Screen+Shot+2015-07-14+at+3.06.44+pm.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW4TmyAKXaSWnvIQEiiIohcnYDCTOaBZ5AvZOz89xDWIHQUzwz_0BNTGZs1ZZ_mKDzoPY9Yx-cU4LCkZ0nuppdyEJk8j2cAF2-t06eAGrDi8tDQUlcwHGmeqrMD1mrkkqXBQxjgQ/s400/Screen+Shot+2015-07-14+at+3.06.44+pm.png&quot; width=&quot;391&quot; /&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Configure the Kerberos integration like this:&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv95AI0XOUg7TJsY9Ool86lYdlpTmozo0xzhOsewF776RKE92QefALVATMSO4pMKF3DJkjZXVx6b0gS91dlGag3NTaiT29LTpUx0qcrVg6mLzmBbv8alwX2qGptzL5YQqP-xREcA/s1600/Screen+Shot+2015-07-14+at+3.07.27+pm.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;202&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv95AI0XOUg7TJsY9Ool86lYdlpTmozo0xzhOsewF776RKE92QefALVATMSO4pMKF3DJkjZXVx6b0gS91dlGag3NTaiT29LTpUx0qcrVg6mLzmBbv8alwX2qGptzL5YQqP-xREcA/s400/Screen+Shot+2015-07-14+at+3.07.27+pm.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2 style=&quot;clear: both; text-align: left;&quot;&gt;
Configuring Java&lt;/h2&gt;
&lt;div&gt;
You will need to install the&amp;nbsp;Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for your version of Java. Download them for Java 8 at&amp;nbsp;&lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html&quot;&gt;http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html&lt;/a&gt;, or Java 7 at&amp;nbsp;&lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html&quot;&gt;http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Notes&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
Don&#39;t use port names in the SPN. By default Keycloak will run under port 8080, but this must not be added to the SPN, even though SPNs can contain port details.&lt;br /&gt;
&lt;br /&gt;
To make use of Kerberos authentication from a Windows client, the Keycloak server has to be in a Internet Zone that has &lt;b&gt;User Authentication -&amp;gt; Logon -&amp;gt; Automatic logon with current user name and password&lt;/b&gt; enabled. Chrome and IE both respect this settings.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrAOoWdh7Nd9Bi2qcoimCGCzCR35doSorxUhdASj9NQCytsPrJZVEQp16_u8RuKCA60uz5alEBf2iElu1D6gLSzgOiritVqy16-_vRpzvKOEGPC3w_vx2nL6S6vF99UN0yrqbCnQ/s1600/Screen+Shot+2015-07-14+at+5.59.50+pm.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrAOoWdh7Nd9Bi2qcoimCGCzCR35doSorxUhdASj9NQCytsPrJZVEQp16_u8RuKCA60uz5alEBf2iElu1D6gLSzgOiritVqy16-_vRpzvKOEGPC3w_vx2nL6S6vF99UN0yrqbCnQ/s320/Screen+Shot+2015-07-14+at+5.59.50+pm.png&quot; width=&quot;280&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
To enable Kerberos authentication in Firefox, you need to add the Keycloak domain to the&amp;nbsp;network.negotiate-auth.trusted-uris setting in about:config.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6lpszNKVnIEPu_pIFS0G3rGY5bzzBHyP-ib0nM-y1_cQdiKFc13YCbcluP2C8mOK_7qZ3emDCc9MXecfkxlTbMprJR54IVsLCt0MVEwHq708d4YfsAnt4Q2fBKvjAivdVTqbQwg/s1600/Screen+Shot+2015-07-15+at+7.14.07+am.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;102&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6lpszNKVnIEPu_pIFS0G3rGY5bzzBHyP-ib0nM-y1_cQdiKFc13YCbcluP2C8mOK_7qZ3emDCc9MXecfkxlTbMprJR54IVsLCt0MVEwHq708d4YfsAnt4Q2fBKvjAivdVTqbQwg/s400/Screen+Shot+2015-07-15+at+7.14.07+am.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;a href=&quot;http://sourceforge.net/p/spnego/discussion/1003768/thread/960ba7ad/&quot;&gt;http://sourceforge.net/p/spnego/discussion/1003768/thread/960ba7ad/&lt;/a&gt; has some useful information that can be used to debug exceptions.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2 style=&quot;clear: both; text-align: left;&quot;&gt;
Testing&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
This page can be used to quickly test the ability for a client to log in.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Get the keycloak.js file from the Keycloak JavaScript Adapter download.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Get the keycloak.json file from Keycloak itself.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQCMOo05r18pvtNxPH-C5lm0if1pEIZquAUtJFJ-EO_h4pf7RY1u4rc8Et6ZLpoBezjsQIgE3P2b-8l7e65zmS8qmudPJRb0yDt8ST3m0O412WORh_7YTvRJwBksSXTdbZTIDkbg/s1600/Screen+Shot+2015-07-15+at+7.37.12+am.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;112&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQCMOo05r18pvtNxPH-C5lm0if1pEIZquAUtJFJ-EO_h4pf7RY1u4rc8Et6ZLpoBezjsQIgE3P2b-8l7e65zmS8qmudPJRb0yDt8ST3m0O412WORh_7YTvRJwBksSXTdbZTIDkbg/s400/Screen+Shot+2015-07-15+at+7.37.12+am.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;html&gt;&lt;/html&gt;&lt;/div&gt;
&lt;div&gt;
&lt;body&gt;&lt;/body&gt;&lt;/div&gt;
&lt;div&gt;
&lt;script src=&quot;keycloak.js&quot;&gt;&lt;/script&gt;&lt;/div&gt;
&lt;div&gt;
&amp;lt;html&amp;gt;&lt;br/&gt;

&amp;lt;body&amp;gt;&lt;br/&gt;

&amp;lt;script src=&quot;keycloak.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br/&gt;

&amp;lt;script&amp;gt;&lt;br/&gt;

 var keycloak = new Keycloak(&#39;keycloak.json&#39;);&lt;br/&gt;

 keycloak.init({ onLoad: &#39;login-required&#39; }).success(function(authenticated) {&lt;br/&gt;

  keycloak.loadUserProfile().success(function(profile) {&lt;br/&gt;

   alert(JSON.stringify(profile));&lt;br/&gt;

  });&lt;br/&gt;

 });&lt;br/&gt;

&amp;lt;/script&amp;gt;&lt;br/&gt;

&amp;lt;/body&amp;gt;&lt;br/&gt;

&amp;lt;/html&amp;gt;&lt;br/&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Some notes on Java 8&lt;/h2&gt;
&lt;div&gt;
Builds of Java 8 above 1.8.0_31 have a bug that will throw the &lt;b&gt;Defective token detected (&lt;wbr&gt;&lt;/wbr&gt;Mechanism level: GSSHeader did not find the right tag)&lt;/b&gt; exception. See&amp;nbsp;&lt;a href=&quot;http://sourceforge.net/p/spnego/discussion/1003769/thread/700b6941/#cb84&quot;&gt;http://sourceforge.net/p/spnego/discussion/1003769/thread/700b6941/#cb84&lt;/a&gt; for details.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/2595020736609610159/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/2595020736609610159' title='22 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/2595020736609610159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/2595020736609610159'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2015/07/authenticating-via-kerberos-with.html' title='Authenticating via Kerberos with Keycloak and Windows 2008 Active Directory'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW4TmyAKXaSWnvIQEiiIohcnYDCTOaBZ5AvZOz89xDWIHQUzwz_0BNTGZs1ZZ_mKDzoPY9Yx-cU4LCkZ0nuppdyEJk8j2cAF2-t06eAGrDi8tDQUlcwHGmeqrMD1mrkkqXBQxjgQ/s72-c/Screen+Shot+2015-07-14+at+3.06.44+pm.png" height="72" width="72"/><thr:total>22</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-3944731122235596007</id><published>2015-04-09T08:13:00.002+10:00</published><updated>2015-04-09T08:14:04.493+10:00</updated><title type='text'>Remember me not - avoiding the Australian metadata dragnet with Tor and Asus</title><content type='html'>So it is official. As an internet user in the great country of Australia my actions online are now tracked and recorded by the government. And that doesn&#39;t sit so well with me.&lt;br /&gt;
&lt;br /&gt;
But rather than complain, I decided to take action and install an &lt;a href=&quot;http://www.asus.com/au/Networking/RTN66U/&quot;&gt;Asus RT-N66U&lt;/a&gt; router as the gateway on my home network. The router had generally positive reviews online, but I was mostly interested in the fact that it supported third party firmware, like the popular ones provided by a developer calling himself&amp;nbsp;&lt;a href=&quot;https://github.com/RMerl/asuswrt-merlin/wiki&quot;&gt;Merlin&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
One of the big benefits provided by third party firmware releases is that you get early access to some cool new features. One such feature that caught my eye was the introduction of Tor into the router.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiMKYis5gIKRjUUtuioVGAPtbl3XnvLdmhtgbdU9idMxS-8krt33VUxpm7-3NiSMYVHQrrUx3zjC32GhoZ7ejJFIHDj0IptGx6YDxCkw71cwnNHtX89bdzFkFZXX5is-wEttIKeA/s1600/tor+asus.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiMKYis5gIKRjUUtuioVGAPtbl3XnvLdmhtgbdU9idMxS-8krt33VUxpm7-3NiSMYVHQrrUx3zjC32GhoZ7ejJFIHDj0IptGx6YDxCkw71cwnNHtX89bdzFkFZXX5is-wEttIKeA/s1600/tor+asus.png&quot; height=&quot;285&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
I have used Tor sporadically in the past. While I have to commend the Tor developers for making it easy to install Tor and browse anonymously, the reality is that running an additional piece of software was kind of a pain. It was a mental jump to go from &quot;always online&quot; to &quot;first make sure you have opened the Tor software, wait for it to connect, then go online&quot;, especially when the process was different depending on whether you are on a desktop PC or a tablet.&lt;br /&gt;
&lt;br /&gt;
The end result was that I usually spend a few hours on Tor only to eventually put it in the &quot;too inconvenient&quot; basket and give up.&lt;br /&gt;
&lt;br /&gt;
Which is why having Tor enabled at the router level is so nice. It&#39;s a one off setup, after which you never have to think about Tor again.&lt;br /&gt;
&lt;br /&gt;
So how well does Tor on the N66U work?&lt;br /&gt;
&lt;br /&gt;
First of all, the configuration is a breeze. Well, a breeze for someone who is knowledgeable enough to be flashing their home router with custom firmware. The process is not going to appeal to those who don&#39;t know what a router is, but anyone with a bit of technical knowledge, or a nerdy friend, can be up and running without too much trouble.&lt;br /&gt;
&lt;br /&gt;
As you can see by the screenshot above, enabling Tor is a matter of selecting &quot;Enable&quot; in a drop down list, and deciding on whether to protect the entire network, or just a few select devices.&lt;br /&gt;
&lt;br /&gt;
Once Tor is enabled, there is nothing more that needs to be done on the devices that you have selected. There is no software to be installed, and no process required to enable Tor.&lt;br /&gt;
&lt;br /&gt;
This convenience comes at a cost though. Tor is only as secure as those applications whose data it is transferring, and one of the benefits of the Tor bundle is a browser that has disabled a number of plugins that are known to leak identifiable information.&lt;br /&gt;
&lt;br /&gt;
However, my goal is to frustrate the government metadata dragnet that has been cast over this country&#39;s internet rather than achieve anonymity. Now that the average Australian citizen has to treat their ISP as a Man-In-The-Middle attacker, I&#39;m willing to trade a little less anonymity for the convenience of having the packets that make up my web browsing obscured in a way that makes the government&#39;s logging efforts mostly meaningless.&lt;br /&gt;
&lt;br /&gt;
Performance wise, there is a noticeable lag as you open web pages, and complex pages (i.e. 90% of the pages you&#39;ll view online) take noticeably longer to load. You&#39;ll also notice that the browser continues to show pages as still loading long after the page has been displayed to you. This is most likely because background scripts loading analytics and advertising scripts are still being download after the main content of the page has been displayed. Initially it is a little disconcerting to see pages taking so long to completely load, but you quickly learn to ignore the spinning icon on the browser tab and jump in once the page is rendered.&lt;br /&gt;
&lt;br /&gt;
Overall, while this performance lag is noticeable, I never felt that it prevented me from doing what I needed to do online.&lt;br /&gt;
&lt;br /&gt;
The biggest downside to using Tor is the number of Capchas you&#39;ll need to fill out to visit every day sites. Google is especially bad, depending on the Tor exit node you happen to be assigned.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6QnCUCFeDByeGGpLmd3M6GrH7Hd_aYna20zYTOsXKVIFM-KqQLTcWBLaYzVDkmpaSYiKYRvORdJ27Tq6qR_6Y6X3WYBrv6U92cLobQpTitWyFuYh0cIdxD_0y-jqCCoAN4f4zHQ/s1600/google+capcha.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6QnCUCFeDByeGGpLmd3M6GrH7Hd_aYna20zYTOsXKVIFM-KqQLTcWBLaYzVDkmpaSYiKYRvORdJ27Tq6qR_6Y6X3WYBrv6U92cLobQpTitWyFuYh0cIdxD_0y-jqCCoAN4f4zHQ/s1600/google+capcha.png&quot; height=&quot;286&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrVQyjsFucKFFJ22XK-itoB51zRqrE-1xjsRAQB7P2-ze_nTYRbB6zqSsKCZYrPiTyD6lm1YtcKXlvVHXBJ9f9asPt8GyaMk1SSzotxbGNumzDYGO26hfThEIW48Imkb80mOi5sQ/s1600/cloudflare.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrVQyjsFucKFFJ22XK-itoB51zRqrE-1xjsRAQB7P2-ze_nTYRbB6zqSsKCZYrPiTyD6lm1YtcKXlvVHXBJ9f9asPt8GyaMk1SSzotxbGNumzDYGO26hfThEIW48Imkb80mOi5sQ/s1600/cloudflare.png&quot; height=&quot;153&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
You may find that Google&#39;s over zealous bot protection renders the search box on your mobile device useless. Apparently there is no facility on the native Android search widget to prove that you are in fact a human and not a bot scraping Google&#39;s search results.&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
You&#39;ll also notice that the ads you are served are now probably in another language. Given that I have clicked on maybe 4 ads in my entire life, this is amusing, and not particularly troublesome.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6XZFmX2orjv70rpQue5JXvAPAho2MljO_QKyynA21afEt3F0kfvN23SfXJqQLJDMtY2O582c4-Q4gXz1Iv_0KS37z3Sh40yFQctSBfFXro3d6LoerL8l39Lf6LK7Dw0Ut7IUv2g/s1600/newscomau.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6XZFmX2orjv70rpQue5JXvAPAho2MljO_QKyynA21afEt3F0kfvN23SfXJqQLJDMtY2O582c4-Q4gXz1Iv_0KS37z3Sh40yFQctSBfFXro3d6LoerL8l39Lf6LK7Dw0Ut7IUv2g/s1600/newscomau.png&quot; height=&quot;258&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
I even managed to stream videos at 780p. 1080p was asking just a little to much, but if I&#39;m being honest, I don&#39;t see much difference between 780p and 1080p anyway.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
There are a few things that I didn&#39;t try to do over Tor.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
The first was streaming Geolocked content (i.e. Netflix). I have a dedicated media PC that uses a VPN (the VPN is also configured through the the N66U) rather than Tor. I imagine that it would be quite difficult to use Tor with something like Netflix, given that you don&#39;t have a lot of control as to where your traffic exits the Tor network. One minute you may appear to be in the US, the next in Europe.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
I don&#39;t torrent, &lt;a href=&quot;https://blog.torproject.org/blog/bittorrent-over-tor-isnt-good-idea&quot;&gt;but no one should be using Tor for torrents anyway&lt;/a&gt;.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
I also don&#39;t use applications like Skype. Actually,&lt;a href=&quot;http://arstechnica.com/information-technology/2015/04/review-anonabox-or-invizbox-which-tor-router-better-anonymizes-online-life&quot;&gt; Skype uses UDP, and won&#39;t be routed over Tor anyway&lt;/a&gt;.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
Even though Tor on the N66U is still in beta, configuration is relatively easy, and I never found myself having to reset the router or perform any kind of troubleshooting. Performance is adequate for day to day browsing, and the ability to only route traffic from specific devices over Tor means that your more bandwidth sensitive devices (like your media PC) are not affected.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
Overall, I am quite impressed with how Asus and Merlin have integrated Tor into the N66U. A Tor enabled router is a great way to effortlessly opt out of the Governments metadata dragnet.&lt;/div&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/3944731122235596007/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/3944731122235596007' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/3944731122235596007'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/3944731122235596007'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2015/04/remember-me-not-avoiding-australian.html' title='Remember me not - avoiding the Australian metadata dragnet with Tor and Asus'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiMKYis5gIKRjUUtuioVGAPtbl3XnvLdmhtgbdU9idMxS-8krt33VUxpm7-3NiSMYVHQrrUx3zjC32GhoZ7ejJFIHDj0IptGx6YDxCkw71cwnNHtX89bdzFkFZXX5is-wEttIKeA/s72-c/tor+asus.png" height="72" width="72"/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-6378313984139645129</id><published>2015-03-08T10:38:00.000+11:00</published><updated>2015-03-08T10:39:16.761+11:00</updated><title type='text'>Fixing OpenVPN &quot;Authenticate/Decrypt packet error: cipher final failed&quot;</title><content type='html'>When connecting to a VPN I was constant getting the error&lt;br /&gt;
&lt;br /&gt;
Mar &amp;nbsp;8 09:29:27 openvpn[1696]: Authenticate/Decrypt packet error: cipher final failed&lt;br /&gt;
&lt;br /&gt;
I had imported the supplied ovpn file and had followed all the other configuration steps, so this was quite frustrating. Then I saw this in the logs:&lt;br /&gt;
&lt;br /&gt;
Mar &amp;nbsp;8 09:31:07 openvpn[1790]: WARNING: &#39;cipher&#39; is used inconsistently, local=&#39;cipher BF-CBC&#39;, remote=&#39;cipher AES-256-CBC&#39;&lt;br /&gt;
&lt;br /&gt;
Changing my client to use &quot;cipher AES-256-CBC&quot; instead of the default (which apparently was cipher BF-CBC) fixed the issue.</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/6378313984139645129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/6378313984139645129' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/6378313984139645129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/6378313984139645129'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2015/03/fixing-openvpn-authenticatedecrypt.html' title='Fixing OpenVPN &quot;Authenticate/Decrypt packet error: cipher final failed&quot;'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-6517501831757697887</id><published>2014-12-07T09:17:00.002+11:00</published><updated>2014-12-09T07:13:38.097+11:00</updated><title type='text'>A XSS filter for Java EE web apps</title><content type='html'>&lt;div style=&quot;background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border: none; box-shadow: none !important; color: #222222; font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif; font-size: 15px; line-height: 21.75px; margin-bottom: 15px !important; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;
Cross Site Scripting, or XSS, is a fairly common vector used to attack web sites. It involves user generated code being redisplayed by a website with all the privileges and security rights that a browser assigns to code originating from the current host. If the user code is something like &amp;lt;script&amp;gt;doEvil();&amp;lt;/script&amp;gt;, then you have a problem.&lt;br /&gt;
&lt;br /&gt;
OWASP is an organisation that provides guidance on web security, and they have a page that provides a suggested method for avoiding XSS in JavaEE web app. You can read this document at &lt;a href=&quot;https://www.owasp.org/index.php/How_to_add_validation_logic_to_HttpServletRequest&quot;&gt;https://www.owasp.org/index.php/How_to_add_validation_logic_to_HttpServletRequest&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The library being demonstrated here is based off the ideas presented in that article, but fleshed out to be more flexible and easy to deploy. We call this library the (unimaginatively named) Parameter Validation Filter, or PVF.&lt;br /&gt;
&lt;br /&gt;
PVF is implemented as a Servlet filter that intercepts requests to web pages, runs submitted parameters through a configurable sequence of validation rules, and either sanitises the parameters before they are sent through to the web application, or returns a HTTP error code if validation errors were detected.&lt;br /&gt;
&lt;br /&gt;
We have made the following assumptions when developing this library:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Client side validation will prevent legitimate users from submitting invalid data.&lt;/li&gt;
&lt;li&gt;The PVF library should prevent further processing if invalid data is submitted in the majority of cases.&lt;/li&gt;
&lt;li&gt;Occasionally it might be appropriate to sanitise submitted data, but any sanitisation should be trivial (like the removal of whitespace).&lt;/li&gt;
&lt;/ul&gt;
To make use of the PVF library, you’ll need to add it to your project. This artifact is currently in the Sonatype staging repo, so you&#39;ll need to add that repo to your Maven config. See &lt;a href=&quot;http://stackoverflow.com/questions/13945757/how-do-you-import-a-maven-dependency-from-sonatype-org&quot;&gt;http://stackoverflow.com/questions/13945757/how-do-you-import-a-maven-dependency-from-sonatype-org&lt;/a&gt; &amp;nbsp;for details.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt;&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;com.matthewcasperson&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;parameter_validation_filter&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;LATEST&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/pre&gt;
&lt;span style=&quot;background-color: white; color: #222222; font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif; font-size: 15px; line-height: 21.75px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;background-color: white; color: #222222; font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif; font-size: 15px; line-height: 21.75px;&quot;&gt;The filter then needs to be added to the web.xml file with the following settings. You may want to configure the url-pattern to match the pages that you actually want to protect.&lt;/span&gt;
&lt;br /&gt;
&lt;span style=&quot;background-color: white; color: #222222; font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif; font-size: 15px; line-height: 21.75px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;pre&gt;&amp;lt;filter&amp;gt;
  &amp;lt;filter-name&amp;gt;ParameterValidationFilter&amp;lt;/filter-name&amp;gt;
  &amp;lt;filter-class&amp;gt;com.matthewcasperson.validation.filter.ParameterValidationFilter&amp;lt;/filter-class&amp;gt;
  &amp;lt;init-param&amp;gt;
    &amp;lt;param-name&amp;gt;configFile&amp;lt;/param-name&amp;gt;
    &amp;lt;param-value&amp;gt;/WEB-INF/xml/pvf.xml&amp;lt;/param-value&amp;gt;
  &amp;lt;/init-param&amp;gt;
&amp;lt;/filter&amp;gt;
&amp;lt;filter-mapping&amp;gt;
  &amp;lt;filter-name&amp;gt;ParameterValidationFilter&amp;lt;/filter-name&amp;gt;
  &amp;lt;url-pattern&amp;gt;*.jsp&amp;lt;/url-pattern&amp;gt;
&amp;lt;/filter-mapping&amp;gt;
&lt;/pre&gt;
&lt;span style=&quot;background-color: white; color: #222222; font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif; font-size: 15px; line-height: 21.75px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;background-color: white; color: #222222; font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif; font-size: 15px; line-height: 21.75px;&quot;&gt;Finally you need to create a file called WEB-INF/xml/pvf.xml. This file defines the custom validation rules applied to the parameters being sent to your web applications.&lt;/span&gt;
&lt;br /&gt;
&lt;span style=&quot;background-color: white; color: #222222; font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif; font-size: 15px; line-height: 21.75px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;pre&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;yes&quot;?&amp;gt;
&amp;lt;!-- ParameterValidationChainDatabase is always the document element --&amp;gt;
&amp;lt;ParameterValidationChainDatabase&amp;gt;
   &amp;lt;!--
        Enforcing mode needs to be set to true to return a HTTP error code if validation fails.
        If set to false, validation errors are logged but ignored.
    --&amp;gt;
    &amp;lt;EnforcingMode&amp;gt;true&amp;lt;/EnforcingMode&amp;gt;

&amp;lt;!-- We always have a single ParameterValidationChains element under the parent --&amp;gt;

    &amp;lt;ParameterValidationChains&amp;gt;

        &amp;lt;!-- Each chain of validation rules is contained in a ParameterValidationDefinition element --&amp;gt;
        &amp;lt;!-- 
            This chain apply some global validation rules. If anyone supplies encoded or params with HTML
            characters, it will fail.
         --&amp;gt;
        &amp;lt;ParameterValidationDefinition&amp;gt;
            &amp;lt;!-- This is the list of validation classes that should be applied to matching parameters --&amp;gt;
            &amp;lt;ParameterValidationRuleList&amp;gt;
                &amp;lt;ParameterValidationRule&amp;gt;
                    &amp;lt;!-- This is the fully qualified name of the class used to apply the validation rule --&amp;gt;
                    &amp;lt;!-- All input fields are to be trimmed of excess whitespace --&amp;gt;
                    &amp;lt;validationRuleName&amp;gt;com.matthewcasperson.validation.ruleimpl.TrimTextValidationRule&amp;lt;/validationRuleName&amp;gt;
                &amp;lt;/ParameterValidationRule&amp;gt;
                &amp;lt;ParameterValidationRule&amp;gt;
                    &amp;lt;!-- No parameters are expected to already be encoded --&amp;gt;
                    &amp;lt;validationRuleName&amp;gt;com.matthewcasperson.validation.ruleimpl.FailIfNotCanonicalizedValidationRule&amp;lt;/validationRuleName&amp;gt;
                &amp;lt;/ParameterValidationRule&amp;gt;
                &amp;lt;ParameterValidationRule&amp;gt;
                    &amp;lt;!-- No parameters are expected to contain html --&amp;gt;
                    &amp;lt;validationRuleName&amp;gt;com.matthewcasperson.validation.ruleimpl.FailIfContainsHTMLValidationRule&amp;lt;/validationRuleName&amp;gt;
                &amp;lt;/ParameterValidationRule&amp;gt;
            &amp;lt;/ParameterValidationRuleList&amp;gt;
            &amp;lt;!-- This is a regex that defines which parameteres will be validated by the classes above --&amp;gt;
            &amp;lt;paramNamePatternString&amp;gt;.*&amp;lt;/paramNamePatternString&amp;gt;
            &amp;lt;!-- This is a regex that defines which URLs will be validated by the classes above --&amp;gt;
            &amp;lt;requestURIPatternString&amp;gt;.*&amp;lt;/requestURIPatternString&amp;gt;
            &amp;lt;!--
                 Setting this to false means the paramNamePatternString has to match the param name.
                 Setting it to true would mean that paramNamePatternString would have to *not* match the param name.
             --&amp;gt;          
            &amp;lt;paramNamePatternNegated&amp;gt;false&amp;lt;/paramNamePatternNegated&amp;gt;
            &amp;lt;!--
                 Setting this to false means the requestURIPatternString has to match the uri.
                 Setting it to true would mean that requestURIPatternString would have to *not* match the uri name.
             --&amp;gt;
            &amp;lt;requestURIPatternNegated&amp;gt;false&amp;lt;/requestURIPatternNegated&amp;gt;
        &amp;lt;/ParameterValidationDefinition&amp;gt;        
    &amp;lt;/ParameterValidationChains&amp;gt;
&amp;lt;/ParameterValidationChainDatabase&amp;gt;
&lt;/pre&gt;
&lt;br /&gt;
The XML has been commented to make it easier to understand, but there are a few interesting elements:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;paramNamePatternString, which has been configured to enable the validation chain to match all parameters&lt;/li&gt;
&lt;li&gt;requestURIPatternString, which has been configured to enable the chain to match all URIs&lt;/li&gt;
&lt;li&gt;The three elements called validationRuleName, which reference the full class name of the validation rules that will be applied to each parameter passed into our web application&lt;/li&gt;
&lt;/ul&gt;
Although this is a simple example, the three validation rules that have been implemented (TrimTextValidationRule, FailIfNotCanonicalizedValidationRule and FailIfContainsHTMLValidationRule) are quite effective at preventing a malicious user from submitting parameters that contain XSS code.&lt;br /&gt;
&lt;br /&gt;
The first rule, TrimTextValidationRule, simply strips away any whitespace on either side of the parameter. This uses the trim() function any developer should be familiar with.&lt;br /&gt;
&lt;br /&gt;
The second rule, FailIfNotCanonicalizedValidationRule, will prevent further processing if the supplied parameter has already been encoded. No legitimate user will have a need to supply text like %3Cscript%3EdoEvil()%3B%3C%2Fscript%3E, so any time encoded text is found we simply return with a HTTP 400 error code. This rule makes use of the &lt;a href=&quot;https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API&quot;&gt;ESAPI&lt;/a&gt; library supplied by OWASP.&lt;br /&gt;
&lt;br /&gt;
Like the second rule, the third rule will prevent further processing if the supplied parameter has any special HTML characters. If you would like your customers to be able to pass through characters like &amp;amp;, this rule is too broad. However, it is almost always valid to block special HTML characters.&lt;br /&gt;
&lt;br /&gt;
If you want to see how effective this simple validation chain is, check out the live demo at &lt;a href=&quot;http://pvftest-matthewcasperson.rhcloud.com/&quot;&gt;http://pvftest-matthewcasperson.rhcloud.com/&lt;/a&gt;. You may want to take a look at &lt;a href=&quot;https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet&quot;&gt;https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet&lt;/a&gt; to find some XSS patterns that are often used to bypass XSS filters.&lt;br /&gt;
&lt;br /&gt;
Moving forward we will be looking to implement more targeted validation rules, especially those that can’t be easily implemented as regex matches (like making sure a date if after today, or that a number is between two values etc).&lt;br /&gt;
&lt;br /&gt;
If you have any suggestions, or find any bugs, feel free to fork the code from our &lt;a href=&quot;https://github.com/mcasperson/ParameterValidationFilter&quot;&gt;GitHub repo&lt;/a&gt; . We do hope to get some public feedback in order to make this library as robust as it can be.</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/6517501831757697887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/6517501831757697887' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/6517501831757697887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/6517501831757697887'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2014/12/a-xss-filter-for-java-ee-web-apps.html' title='A XSS filter for Java EE web apps'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-1606691328387094123</id><published>2014-07-19T15:50:00.003+10:00</published><updated>2014-07-19T16:04:17.933+10:00</updated><title type='text'>Three Daily Things</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.threedailythings&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVVhqqYot0YJcSUM0cJ9iwTM5BER38-JYh21PMGJD0twVsauarZOIPG0ml2Bz0Ja5dixAZYUer-DqU7b3T8TU2J7lH5mf94g_dJRxCm_lGCW3067eyCqeOIFG7yakiS8Z1Bp1GnA/s1600/playstore.png&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/three-daily-things/jecgjccbadfkhgggbhconpgmoanhgend&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4sb17r1aPKC4W4J4osz2VZEn_vzCUutqDU5pXo4FCUePPG3k76gS265X3vDeaevUETPdjTkvmYKVhBLIP8uAvDED0LP-CiL0FpmzK_Rcxuz2v5H4AqlWnbArSw7pfHOO2naArrQ/s1600/chromestore.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAIytO-fL2GINhVJ6IxE4NE6eVqbgpOCaU-MNKpmMupNU7FskHvr2pHEjRLypWNgayn285KJcQAnfPQ7E9RiR1yP5qcmSkzHoUl03dSjBSA_Skfhnp0yiNLGDL0lEbNXDsgEG7Qg/s1600/chromestorelogo.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; display: inline !important; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAIytO-fL2GINhVJ6IxE4NE6eVqbgpOCaU-MNKpmMupNU7FskHvr2pHEjRLypWNgayn285KJcQAnfPQ7E9RiR1yP5qcmSkzHoUl03dSjBSA_Skfhnp0yiNLGDL0lEbNXDsgEG7Qg/s1600/chromestorelogo.png&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://threedailythings.com/&quot;&gt;Three Daily Things&lt;/a&gt; is a new, free website that I have put together based on a motivation app that I have had great personal success with.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The reason why I wrote this app is best explained with a story about myself.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Like most people, I want to be fit an healthy. To achieve this, I sign up to my local gym. But it doesn&#39;t take long for the unknowns to start rattling around in my brain. How many reps and set should I do? Should I do cardio before or after resistance training? Should I be taking supplements? Is it best to work out in the morning or afternoon? Which exercises are best?&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
All these unknowns start to weigh heavily in my mind. I begin to wonder if I am wasting my time. I skip a few sessions, and before I know it I haven&#39;t visited the gym in weeks.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
So many beneficial aspirations in my life have followed this path. I like the idea, I try it out, I get overwhelmed by the unknowns and eventually I give up.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
So I asked myself, what was it that I really wanted out of my gym membership? The answer is right there in the first sentence: to be fit and healthy. And I realize that I can achieve this goal without actually knowing the best ratio of sets to reps, the most effective exercises and without taking supplements. All I need to do is walk through the gym door. Simply being in the gym day after day and doing some sort of exercise achieves my goal of being fit and healthy.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In a nutshell, my problem is that in seeking perfection I often end up with nothing. The solution was to convince myself that continuing to focus on these beneficial activities, even if I do them imperfectly, will eventually get me the result I am looking for.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
That is why I created Three Daily Things. It is a way to remind myself that any time I spend working on the three things that I feel would most improve my life is worthwhile.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The app is deliberately simple. You list the three things that you feel would most improve your life, and when you spend any amount of time working on them, you give yourself a tick. The report allows you to quickly gauge how often you have made time (any time) for these three things over the last week.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
For such a simple idea, my personal results were quite incredible. Prior to writing this app, I would estimate that I would have spent time on my three most important things maybe six or seven times a week, giving me a weekly score of around 30%. Using the app daily, my weekly score usually sits between 60% and 80%. That means I have easily doubled my focus on those three things that I know are improving my life.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
This started out as a personal motivational tool, but it has worked so well that I decided to make the web site free to everyone in the hopes that others would find Three Daily Things as simple and effective as I have.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/1606691328387094123/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/1606691328387094123' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/1606691328387094123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/1606691328387094123'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2014/07/three-daily-things.html' title='Three Daily Things'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVVhqqYot0YJcSUM0cJ9iwTM5BER38-JYh21PMGJD0twVsauarZOIPG0ml2Bz0Ja5dixAZYUer-DqU7b3T8TU2J7lH5mf94g_dJRxCm_lGCW3067eyCqeOIFG7yakiS8Z1Bp1GnA/s72-c/playstore.png" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-5683089113044775654</id><published>2014-06-13T15:47:00.004+10:00</published><updated>2014-06-13T15:47:43.967+10:00</updated><title type='text'>RHEL 7 VirtualBox Guest Additions Patched</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8QekRQnOaIPG5Kqxlo5X5wg96H1CC0spLQ_iux7YypSveuDoyDi0RTNkKbuKMdhyIDbcHmRqmVOiU78B8s8eMH6Sd8A9A_OMSGARSFLSWDBTG8H1f9IJ1RD-dabAEA8tf5QZw8A/s1600/rhel_7_3x3_sticker_12236947_0514mm_preview.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8QekRQnOaIPG5Kqxlo5X5wg96H1CC0spLQ_iux7YypSveuDoyDi0RTNkKbuKMdhyIDbcHmRqmVOiU78B8s8eMH6Sd8A9A_OMSGARSFLSWDBTG8H1f9IJ1RD-dabAEA8tf5QZw8A/s1600/rhel_7_3x3_sticker_12236947_0514mm_preview.png&quot; height=&quot;200&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
If you have tried to used RHEL 7 in VirtualBox, and run into the issue with the guest additions not compiling (see &lt;a href=&quot;https://www.virtualbox.org/ticket/12638&quot;&gt;https://www.virtualbox.org/ticket/12638&lt;/a&gt; for details), you can download &lt;a href=&quot;https://dl.dropboxusercontent.com/u/1526979/VirtualBoxGuestAditions-4.3.12-RHEL7.tar.bz2&quot;&gt;this tar file&lt;/a&gt;, extract it, and run&lt;br /&gt;
&lt;br /&gt;
sudo ./install.sh&lt;br /&gt;
&lt;br /&gt;
The tar file is just the guest additions pre patched to work with RHEL 7.</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/5683089113044775654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/5683089113044775654' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/5683089113044775654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/5683089113044775654'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2014/06/rhel-7-virtualbox-guest-additions.html' title='RHEL 7 VirtualBox Guest Additions Patched'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8QekRQnOaIPG5Kqxlo5X5wg96H1CC0spLQ_iux7YypSveuDoyDi0RTNkKbuKMdhyIDbcHmRqmVOiU78B8s8eMH6Sd8A9A_OMSGARSFLSWDBTG8H1f9IJ1RD-dabAEA8tf5QZw8A/s72-c/rhel_7_3x3_sticker_12236947_0514mm_preview.png" height="72" width="72"/><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-4626654077827810957</id><published>2014-01-24T11:57:00.000+11:00</published><updated>2014-01-24T11:57:26.240+11:00</updated><title type='text'>Scroll to the bottom of log files in web pages</title><content type='html'>We use supervisord on our systems, which has a handy browser based &#39;tail -f&#39; feature. The problem is that the end of the log file appears off the bottom of the screen, and the browser won&#39;t scroll to the bottom automatically. This bookmarklet will keep the end of the page in view.&lt;br /&gt;
&lt;br /&gt;
javascript:scroll=function(){setTimeout(function(){window.scrollTo(0,document.body.scrollHeight);scroll();},100);};scroll();</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/4626654077827810957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/4626654077827810957' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/4626654077827810957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/4626654077827810957'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2014/01/scroll-to-bottom-of-log-files-in-web.html' title='Scroll to the bottom of log files in web pages'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-3981912978686670020</id><published>2014-01-11T08:46:00.000+11:00</published><updated>2014-01-11T14:38:09.490+11:00</updated><title type='text'>If you are a knowledge gatekeeper, the game is changing</title><content type='html'>&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&quot;We take comfort from Charles
Darwin&#39;s observation that it&#39;s not the strongest species that
survives, nor the most intelligent, but the ones most responsive to
change. We just need to be adaptable.&quot; - Gary Pruitt, then the
CEO of McClatchy Newspapers and now CEO of the Associated Press
(&lt;a href=&quot;http://www.slate.com/blogs/future_tense/2012/11/12/google_ad_revenue_tops_entire_us_print_media_industry_chart.html&quot;&gt;http://www.slate.com/blogs/future_tense/2012/11/12/google_ad_revenue_tops_entire_us_print_media_industry_chart.html&lt;/a&gt;).&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
I recently spent some time looking into
the stats for web content we were responsible for and noticed two
things. The first was that the majority of visitors were viewing
chunked versions of our books, where each individual web page aligned
roughly with an entry in the table of contents. The second was that
most visitors were spending only a couple of minutes reading the
content in any given session.&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
This observation aligned very closely
with my own information gathering process. Once or twice a year I&#39;ll
make time to sit down and read a technical book from start to finish.
This is usually because I am studying for a test or need to
familiarise myself with a new technology. This compares to the dozens
of web searches I do every single day to find answers to various
issues that I run into as part of my daily job.&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
My productivity depends in large part
on my ability to find answers to specific and usually isolated
problems. I imagine a lot of information workers are in the same
boat. 
&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
Right now the process of finding
answers is to do a search in Google and then skim over the resulting
links to try and find an answer. Or in other words, Googling.&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
The fact that Googling involves viewing
the original content is very important to those that create, collate
and profit from factual digital content (think wikis, knowledgebases,
books, articles, blogs etc), because it means that they are the
gatekeepers to their digital kingdoms. The answers people seek can be
presented, restricted and sold in any way that best suits the
knowledge gatekeepers.&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
That is about to come to an end
(&lt;a href=&quot;http://slashdot.org/topic/bi/ibm-investing-1-billion-in-watson-business-unit/&quot;&gt;http://slashdot.org/topic/bi/ibm-investing-1-billion-in-watson-business-unit/&lt;/a&gt;):&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
If everything goes according to IBM’s plan, Watson will help kick off what CEO Ginni Rometty refers to as a third era in computing. The 19th century saw the rise of a “tabulating” era: the birth of machines designed to count. In the latter half of the 20th century, developers and scientists initiated the “programmable” era—resulting in PCs, mobile devices, and the Internet. The third (potential) era is “cognitive,” in which computers become adept at understanding and solving, in a very human way, some of society’s largest problems. &lt;/div&gt;
&lt;/blockquote&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
Knowledge is about to become
commoditized. Since most people want answers to questions, this third
era of computing is going to demote knowledge gatekeepers to funnels
into systems like Watson. At best your original content will be
presented as a footnote against the answer provided by Watson. At
worst, no will will care where the answer came from, only that it is
accurate.&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
Today it takes teams of highly skilled
employees to create, curate, manage and navigate digital knowledge
repositories designed to support vertical markets. Stick a support
contract in front of that and you have the makings of a billion
dollar business. In the not too distant future, some kid in a dorm
room is going to be able to scrape every forum, wiki, book, bug
report, chat log and code base that relates to your business or
product, funnel it into a system like Watson, and produce a “Ask
Watson” style web page that renders your once valuable digital
knowledge repositories obsolete.&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
And if you think that your content is
so engrained, valuable or unique as to be immune from disruption from
this third era of computing, I have shares in Encyclopaedia
Britannica, print based news media and bricks and mortar book shops
that I would like to sell you.&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
Adaptation is the key to surviving the
transition from valuable knowledge gatekeeper to anonymous
information source. Because make no mistake, change is upon us right
now, and history is littered with the corpses of business that missed
the signs.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/3981912978686670020/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/3981912978686670020' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/3981912978686670020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/3981912978686670020'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2014/01/if-you-are-knowledge-gatekeeper-ibm-is.html' title='If you are a knowledge gatekeeper, the game is changing'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-3663824262560100558</id><published>2014-01-05T10:23:00.003+11:00</published><updated>2014-01-05T16:56:30.350+11:00</updated><title type='text'>Your grandkids are going to laugh at the notion of &quot;searching the web&quot;</title><content type='html'>&lt;h2&gt;
&lt;/h2&gt;
In the next 5 - 10 years our good friend and faithful digital companion, the search bar, is going to ride off into the sunset.&lt;br /&gt;
&lt;br /&gt;
Googling has forever changed how we find answers to almost anything we could ever want to know, because the answer is most likely there buried in the first 10 links brought up in response to anything you type into the search bar.&lt;br /&gt;
&lt;br /&gt;
When you think about it though, having to trawl through those 10 links is actually not a great way to answer a question. If you were to ask your mechanic what was making that noise in your engine, you want the answer, not rambling descriptions and discussions on engine design and maintenance. But that is exactly what you get if you were to ask Google the same question: pages of forum posts when you only want the last one that actually has the answer; a blog post where that engine noise is coincidentally mentioned in the comments; or a 10 minute YouTube video where the information you want is 9 minutes in.&lt;br /&gt;
&lt;br /&gt;
That is all about to change. In the not too distant future you will type your query into a question bar, and the result will be a plain English answer. No more &quot;this search returned 10000000000000 results&quot;; no more clicking through web pages to find the information you are looking for; and, if Google isn&#39;t careful, potentially no more Googling.&lt;br /&gt;
&lt;br /&gt;
We have seen a glimpse of this move from searching to asking with &lt;a href=&quot;http://www.wolframalpha.com/&quot;&gt;Wolfram Alpha&lt;/a&gt;. You enter some search criteria, Alpha identifies real world entities or concepts in the search query, and then displays a bunch of cross referenced information. It&#39;s a great tool if you are looking for something that is easily identifiable, like the manufacturer of your car, and possibly even your car&#39;s model. But Alpha is not going to tell you what that sound in your engine could be.&lt;br /&gt;
&lt;br /&gt;
That&#39;s not to say that Wolfram is giving up. &lt;a href=&quot;http://blog.stephenwolfram.com/2013/11/something-very-big-is-coming-our-most-important-technology-project-yet/&quot;&gt;He has made some vague reference to a new knowledge based programming language:&lt;/a&gt;&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
We call it the Wolfram Language because it is a language.&amp;nbsp;But it’s a new
 and different kind of language.&amp;nbsp;It’s a general-purpose knowledge-based 
language.&amp;nbsp;That covers all forms of computing, in a new way.&amp;nbsp;&lt;/blockquote&gt;
Apple has made some bold moves away from searching with Siri. Granted, Siri is often just a speech to text converter for plain old web searches, but it is not hard to imagine Siri connecting the dots between the questions you asked about &quot;that noise in my engine&quot; and post number 13 in a forum conversion that mentions a fan belt with the same ease with which she connects you request to set an alarm with the settings in your iPhone alarm clock app.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.smh.com.au/digital-life/digital-life-news/in-the-future-internet-search-will-give-us-super-powers-20131228-300ei.html&quot;&gt;Microsoft is also making a move towards asking questions&lt;/a&gt;:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&quot;Our vision of search is &#39;just ask&#39;. Search is the ultimate human interface. It should be able to cope with any input.&quot;&lt;/blockquote&gt;
And then there is IBM, &lt;a href=&quot;http://en.wikipedia.org/wiki/Watson_%28computer%29#Jeopardy.21&quot;&gt;which bested some of the brightest minds ever to grace the game show Jeopardy with their system Watson&lt;/a&gt;. Watson is probably the best public example of a system that can answer general questions with plain English responses, &lt;a href=&quot;http://gigaom.com/2013/11/14/have-at-it-programmers-ibm-makes-watson-available-via-api/&quot;&gt;and that power is going to be available to developers&lt;/a&gt;:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Developers who want to incorporate Watson’s ability to understand 
natural language and provide answers need only have their applications 
make a REST API call to IBM’s new Watson Developers Cloud. “It doesn’t 
require that you understand anything about machine learning other than 
the need to provide training data,” Rob High, IBM’s CTO for Watson, said
 in a recent interview about the new platform. &lt;/blockquote&gt;
&lt;span id=&quot;goog_1723606886&quot;&gt;&lt;/span&gt;&lt;span id=&quot;goog_1723606887&quot;&gt;&lt;/span&gt;Google has been fairly quiet about this post web search world, probably because it is of no benefit to them to suggest that searching is going anywhere, &lt;a href=&quot;http://en.wikipedia.org/wiki/Freebase&quot;&gt;but their acquisitions of data sources like Freebase&lt;/a&gt;,&amp;nbsp;the sidebar that displays relevant information about any entities in your search query and Google Now do indicate that they are looking to move beyond just plain old web searches too.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtI6hqlqEw2suLXa70cIHUynkvfi5S1iZH5Kdyrs66vfECx8tw81X7QQU_7InxgV-7865-d4zM2CAeqOLvRRR4MXBIE0kq-1MAN9tzq8XTWV172k3KQroHNV1BSjURgtb-RAynvw/s1600/Untitled2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtI6hqlqEw2suLXa70cIHUynkvfi5S1iZH5Kdyrs66vfECx8tw81X7QQU_7InxgV-7865-d4zM2CAeqOLvRRR4MXBIE0kq-1MAN9tzq8XTWV172k3KQroHNV1BSjURgtb-RAynvw/s320/Untitled2.png&quot; width=&quot;268&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
So when my grandkids ask me what the internet was like when I was their age, I will tell them of the great information hunts I used to go on, carefully crafting my search queries and then staking the answer through pages of unrelated and often unhelpful information. And they will laugh.</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/3663824262560100558/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/3663824262560100558' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/3663824262560100558'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/3663824262560100558'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2014/01/your-grandkids-are-going-to-laugh-at.html' title='Your grandkids are going to laugh at the notion of &quot;searching the web&quot;'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtI6hqlqEw2suLXa70cIHUynkvfi5S1iZH5Kdyrs66vfECx8tw81X7QQU_7InxgV-7865-d4zM2CAeqOLvRRR4MXBIE0kq-1MAN9tzq8XTWV172k3KQroHNV1BSjURgtb-RAynvw/s72-c/Untitled2.png" height="72" width="72"/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-6575157660309730249</id><published>2013-11-15T16:38:00.001+11:00</published><updated>2013-11-15T16:39:40.834+11:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="three.js"/><title type='text'>Three.js Tutorials</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWtTJWZeOFoHFrkh_NY5Xb3cW9ILbW51eiwgWGZnuHngaxaZclJc-tz-AUCgyrOsTUF8rdeB-SwVvBaT5I0VutXl8QbhWYgmIeaEuOEvtaj_ZIMWKafxmdksDqEFJd_dca7HNuag/s1600/physijs.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;300&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWtTJWZeOFoHFrkh_NY5Xb3cW9ILbW51eiwgWGZnuHngaxaZclJc-tz-AUCgyrOsTUF8rdeB-SwVvBaT5I0VutXl8QbhWYgmIeaEuOEvtaj_ZIMWKafxmdksDqEFJd_dca7HNuag/s400/physijs.jpg&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
A tutorial series looking at the JavaScript 3D engine &lt;a href=&quot;http://threejs.org/&quot;&gt;three.js&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://matthewcasperson.blogspot.com.au/2013/11/threejs-1-getting-started_10.html&quot;&gt;Three.js - 1 - Getting Started Part One&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://matthewcasperson.blogspot.com.au/2013/11/threejs-2-getting-started-part-2.html&quot;&gt;Three.js - 2 - Getting Started Part Two&lt;/a&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;/ul&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/6575157660309730249/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/6575157660309730249' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/6575157660309730249'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/6575157660309730249'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2013/11/threejs-tutorials.html' title='Three.js Tutorials'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWtTJWZeOFoHFrkh_NY5Xb3cW9ILbW51eiwgWGZnuHngaxaZclJc-tz-AUCgyrOsTUF8rdeB-SwVvBaT5I0VutXl8QbhWYgmIeaEuOEvtaj_ZIMWKafxmdksDqEFJd_dca7HNuag/s72-c/physijs.jpg" height="72" width="72"/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-3308481725039092464</id><published>2013-11-15T16:19:00.000+11:00</published><updated>2013-11-15T16:39:28.590+11:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="three.js"/><title type='text'>Three.js - 2 - Getting Started Part 2</title><content type='html'>&lt;h1&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja-DOZvHvex3TyuaEkXajO7v3RilUCLlWXrxmV60KgxTAE05x3kh2nKQpb53HkZi8XDAh_RK_rDi1Kmk1S12IcJRmdtQD9Vw6ny0xjqR6TwIk2NQdU9bCI7XPNfvv-JlejonxH3Q/s1600/screenshot.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;209&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja-DOZvHvex3TyuaEkXajO7v3RilUCLlWXrxmV60KgxTAE05x3kh2nKQpb53HkZi8XDAh_RK_rDi1Kmk1S12IcJRmdtQD9Vw6ny0xjqR6TwIk2NQdU9bCI7XPNfvv-JlejonxH3Q/s320/screenshot.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Three.js - 2 - Getting Started Part Two&lt;/h1&gt;
&lt;div&gt;
&lt;b&gt;&lt;a href=&quot;http://matthewcasperson.blogspot.com.au/2013/11/threejs-tutorials.html&quot;&gt;THREE.JS TUTORIALS&lt;/a&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;strong&gt;&lt;a href=&quot;https://googledrive.com/host/0B6cpJDiEKC62RHh4TVhlc1BpVWM&quot;&gt;VIEW THE DEMO&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;a href=&quot;https://googledrive.com/host/0B6cpJDiEKC62Vy1xdzJUcS1nZjQ&quot;&gt;DOWNLOAD THE SOURCE CODE&lt;/a&gt;&lt;/b&gt;&lt;/div&gt;
&lt;h2&gt;
Introduction&lt;/h2&gt;
&lt;div&gt;
In the last tutorial we got a very basic three.js application up and running. This tutorial will continue on from that, adding a stats counter and dealing with window resizing.&lt;/div&gt;
&lt;h2&gt;
Stats Counter&lt;/h2&gt;
&lt;div&gt;
A number of the example three.js applications have a neat little stats counter showing the number of frames per second, and displaying a graph of the frame rate over the last few seconds. This information is invaluable when testing the performance of your 3D application.
  &lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;
The JavaScript library responsible for this stats counter is not actually part of three.js. It is a separate library that can be found on GitHub at
   &lt;a href=&quot;https://github.com/mrdoob/stats.js/&quot;&gt;
   https://github.com/mrdoob/stats.js/&lt;/a&gt;. The file that we need to include in our project can be found at &lt;a href=&quot;https://github.com/mrdoob/stats.js/blob/master/build/stats.min.jshttps://github.com/mrdoob/stats.js/blob/master/build/stats.min.js&quot;&gt;https://github.com/mrdoob/stats.js/blob/master/build/stats.min.jshttps://github.com/mrdoob/stats.js/blob/master/build/stats.min.js&lt;/a&gt;. This JavaScript file is included like the others in the 
   &lt;strong&gt;&amp;lt;head&amp;gt;&lt;/strong&gt; element.
  &lt;/div&gt;
&lt;br /&gt;
&lt;pre&gt; &amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;Three.js - 2 - Getting Started 2&amp;lt;/title&amp;gt;
  &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;css/style.css&quot;&amp;gt;
  &amp;lt;script type=&quot;application/javascript&quot; src=&quot;javascript/stats.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script type=&quot;application/javascript&quot; src=&quot;javascript/three.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script type=&quot;application/javascript&quot; src=&quot;javascript/threejs-2.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
 &amp;lt;/head&amp;gt;  
  &lt;/pre&gt;
&lt;div&gt;
If you look at the code above, you&#39;ll notice that we have also included a link to a CSS file. The DOM element created by the stats.js library has the ID of
   &lt;strong&gt;stats&lt;/strong&gt;. Knowing this, we can create a CSS rule to position the stats counter in the top left hand corner of the page. 
  &lt;/div&gt;
&lt;br /&gt;
&lt;pre&gt;#stats { position: absolute; top:0; left: 0 }  
  &lt;/pre&gt;
&lt;div&gt;
With the stats.js library loaded and the DOM element styled, we now create the stats counter and add it to the HTML page in JavaScript. The following lines are added to the end of the 
   &lt;strong&gt;init()&lt;/strong&gt; function. Note that we are appending the stats element directly to the body of the HTML page, just like we did with the renderer in the last tutorial. Because we specified absolute positioning in the style that is applied to the stats element, it will not interfere with the position of the renderer.
  &lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt; stats = new Stats();
 document.body.appendChild( stats.domElement );  
  &lt;/pre&gt;
&lt;div&gt;
The final step is to update the stats counter every time a frame is rendered. This is done with the following code added to the end of the 
   &lt;strong&gt;animate()&lt;/strong&gt; function.
  &lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt; stats.update();    &lt;/pre&gt;
&lt;h2&gt;
Window Resizing&lt;/h2&gt;
&lt;div&gt;
Take a moment to run the example from the last tutorial, and after it has been loaded, resize the window. Notice that the 3D cube does not change its position relative to the top left hand corner of the screen. Obviously this is not ideal.
  &lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;
Fixing this requires the camera&#39;s aspect ratio and projection matrix, and the renderer&#39;s size, to be updated if the window is resized. We can specify a function to be run in the event that the window is resized by adding an event listener to the window&#39;s resize event.
  &lt;/div&gt;
&lt;br /&gt;
&lt;pre&gt; window.addEventListener( &#39;resize&#39;, onWindowResize, false );  
  &lt;/pre&gt;
&lt;div&gt;
The &lt;strong&gt;onWindowResize()&lt;/strong&gt; function is where we update the camera&#39;s aspect ratio and projection matrix, and the renderer&#39;s size.
  &lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt;function onWindowResize() {
 camera.aspect = window.innerWidth / window.innerHeight;
 camera.updateProjectionMatrix();
 renderer.setSize( window.innerWidth, window.innerHeight );
}  
  &lt;/pre&gt;
&lt;div&gt;
When running the example supplied with this tutorial, it is possible to resize the window and still have the cube placed in the middle of the screen.
  &lt;/div&gt;
&lt;h2&gt;
Tips&lt;/h2&gt;
&lt;div&gt;
If you click on the stats counter, you will switch between two views. The first displays the frames per second, while the second displays the milliseconds per frame.
  &lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/3308481725039092464/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/3308481725039092464' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/3308481725039092464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/3308481725039092464'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2013/11/threejs-2-getting-started-part-2.html' title='Three.js - 2 - Getting Started Part 2'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja-DOZvHvex3TyuaEkXajO7v3RilUCLlWXrxmV60KgxTAE05x3kh2nKQpb53HkZi8XDAh_RK_rDi1Kmk1S12IcJRmdtQD9Vw6ny0xjqR6TwIk2NQdU9bCI7XPNfvv-JlejonxH3Q/s72-c/screenshot.png" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-7492521406696803433</id><published>2013-11-14T14:36:00.002+11:00</published><updated>2014-01-11T09:11:19.995+11:00</updated><title type='text'>MinHash for dummies</title><content type='html'>Duplicate document detection is becoming increasingly important for businesses that have huge collections of email, web pages and documents with multiple copies that may or may not be kept up to date.&lt;br /&gt;
&lt;br /&gt;
MinHash is a fairly simple algorithm that from all my Googling has been explained very poorly in blogs or in the kind of mathematical terms that I forgot long ago. So in this article I will attempt to explain how MinHash works at a practical code level.&lt;br /&gt;
&lt;br /&gt;
Before I start, please take a look at&amp;nbsp;&lt;a href=&quot;http://infolab.stanford.edu/~ullman/mmds/ch3.pdf&quot;&gt;http://infolab.stanford.edu/~ullman/mmds/ch3.pdf&lt;/a&gt;. That document goes into a lot of theory, and was ultimately where my understanding on MinHash came from. Unfortunately it approaches the algorithm from a theoretical standpoint, but if I gloss over some aspect of the MinHash algorithm here, you will almost certainly find a fuller explanation in the PDF.&lt;br /&gt;
&lt;br /&gt;
I&#39;ll also be using pseudo Java in these examples instead of traditional math. This means when I use terms like Set, I am referring to the group of classes that implement a Set in Java. Of course a set in Java shares a lot of properties with a set in maths, but keep in mind that this is written from a developers point of view.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
What does it mean when we talk about document similarity? &amp;nbsp;&lt;/h2&gt;
&lt;div&gt;
The whole point of MinHash is to efficiently answer the question &quot;How similar is this document to that document&quot;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Before we can answer that question, we need to look at a more simple example of two sets of strings and determine how similar they are.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Here are our two sets:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Set&lt;string&gt; a = new Set([&quot;chair&quot;, &quot;desk&quot;, &quot;rug&quot;, &quot;keyboard&quot;, &quot;mouse&quot;]);&lt;/string&gt;&lt;/div&gt;
&lt;div&gt;
Set&lt;string&gt; b = new Set([&quot;chair&quot;, &quot;rug&quot;, &quot;keyboard&quot;]);&lt;/string&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
A measure of how similar these two sets are is known as the Jaccard&amp;nbsp;Coefficient. It is calculated as &lt;b&gt;number of common elements / (total number of elements - number of common elements)&lt;/b&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In our case we have 3 common elements: &quot;chair&quot;, &quot;rug&quot; and &quot;keyboard&quot;. We have a total of 8 elements. So the Jaccard Coefficient &amp;nbsp;= 3 / (8 - 3) = 0.6, or 60%.&amp;nbsp;&lt;a href=&quot;http://www.planetcalc.com/1664/&quot;&gt;http://www.planetcalc.com/1664/&lt;/a&gt;&amp;nbsp;is an online calculator that you can play around with to determine the similarity of two sets.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Documents as sets&lt;/h2&gt;
&lt;div&gt;
While a document can be though of as a giant set of words, we don&#39;t just break down a document into individual words, place them in a set and calculate the similarity, because that looses the importance of the order of the words. The sets&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
Set&lt;string&gt; a = new Set([&quot;I&quot;, &quot;went&quot;, &quot;to&quot;, &quot;work&quot;, &quot;today&quot;]);&lt;/string&gt;&lt;/div&gt;
&lt;div&gt;
Set&lt;string&gt; b = new Set([&quot;today&quot;, &quot;I&quot;, &quot;went&quot;, &quot;to&quot;, &quot;work&quot;]);&lt;/string&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
would be considered 100% similar, even though they clearly are not.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Instead we break a document down into what are known as shingles. Each shingle contains a set number of words, and a document is broken down into &lt;b&gt;total words - single length + 1&amp;nbsp;&lt;/b&gt;number of shingles. So if a document contained a single sentence of &quot;The quick brown fox jumps over the lazy dog&quot;, that would be broken down into the following 5 word long shingles :&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;The quick brown fox jumps&lt;/li&gt;
&lt;li&gt;quick brown fox jumps over&lt;/li&gt;
&lt;li&gt;brown fox jumps over the&lt;/li&gt;
&lt;li&gt;fox jumps over the lazy&lt;/li&gt;
&lt;li&gt;jumps over the lazy dog&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt;
So our document as a set looks like:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Set&lt;string&gt; a = new Set([&quot;The quick brown fox jumps&quot;, &quot;quick brown fox jumps over&quot;, &quot;brown fox jumps over the&quot;, &quot;fox jumps over the lazy&quot;, &quot;jumps over the lazy dog&quot;]);&lt;/string&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
These sets of shingles can then be compared for similarity using the Jaccard Coefficient.&lt;/div&gt;
&lt;h2&gt;
Optimizing the process&lt;/h2&gt;
&lt;div&gt;
So we now have a way to compare two documents for similarity, but it is not an efficient process. To find similar documents to document A in a directory of 10000 documents, we need compare each pair individually. This is obviously not going to scale well.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
What we can do to reduce some cycles is compare sets of randomly selected shingles from two documents. So for a document that is 10000 words long, we break it down into 9996 shingles, and randomly select say 200 of those to represent the document. If the second document is 20000 words long, we boil that down from 19996 shingles to another set of 200 randomly selected shingles.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Now instead of matching 9996 shingles to&amp;nbsp;19996&amp;nbsp;other shingles, we are are comparing 200 shingles to another 200 shingles. We have reduced our workload at the expense of accuracy, but this is still good enough for most scenarios.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: #cc0000;&quot;&gt;NOTE&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: #cc0000;&quot;&gt;So just how much accuracy did we sacrifice by comparing only a small subset of randomly selected shingles? I actually don&#39;t know, but I assume it sits on a bell curve where the number of random selections improves the accuracy to a point where more random selections won&#39;t provide any meaningful increase in accuracy. If you know the math behind this, please leave a comment.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: #cc0000;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: #cc0000;&quot;&gt;I chose 200 random selections after reading&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://blog.cluster-text.com/tag/minhash/&quot;&gt;http://blog.cluster-text.com/tag/minhash/&lt;/a&gt;.&lt;span style=&quot;color: #cc0000;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;h2&gt;
Computing and saving the random shingle selections&lt;/h2&gt;
&lt;div&gt;
So this is where MinHash comes in.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We could save the 200 randomly selected shingles, but storing variable length strings kinda sucks. It complicates database design and comparing strings is slow.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The clever part about MinHash is that it allows us to save integers instead of strings, and also takes the hassle out of randomly selecting shingles. It works like this.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
1. Break down the document a set of shingles.&lt;/div&gt;
&lt;div&gt;
2. Calculate the hash value for every shingle.&lt;/div&gt;
&lt;div&gt;
3. Store the minimum hash value found in step 2.&lt;/div&gt;
&lt;div&gt;
4. Repeat steps 2 and 3 with different hash algorithms 199 more times to get a total of 200 min hash values.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
At this point instead of 200 randomly selected shingles, we have 200 integer hash values. This is effectively a random selection of shingles, because a good hash algorithm is supposed to generate a number that is as likely to be large as it is likely to be small. This kind of distribution of hash codes is what hashing is all about, so selecting the smallest hash is, for all intents and purposes, a random selection of a shingle.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Um, 199 more hash algorithms? WTF!&lt;/h2&gt;
&lt;div&gt;
So you see the String.hashCode() method, but where do these other 199 hash codes come from? This is the step that most explanations on the MinHash algorithm conveniently leave out.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The short answer is that you XOR the value returned by String.hashCode() with 199 random numbers to generate the 199 other hash code values. Just make sure that you are using the &lt;i&gt;same&lt;/i&gt; 199 random numbers across all the documents.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
If you want some more detail on why this works, see&amp;nbsp;&lt;a href=&quot;http://stackoverflow.com/a/19711615/157605&quot;&gt;http://stackoverflow.com/a/19711615/157605&lt;/a&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Great, but I still have to compare every document to every other document. Can this be optimized?&lt;/h2&gt;
&lt;div&gt;
This is where the second neat trick of MinHash comes in.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Locality sensitive hashing (LSH) involves generating a hash code such that similar items will tend to get similar hash codes. This is the opposite of what .hashCode() does.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
LSH allows you to precompute a hash code that is then quickly and easily compared to another precomputed LSH hash code to determine if two objects should be compared in more detail or quickly discarded.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We don&#39;t actually calculate a LSH hash code as such, but the idea of a boiling down a complex object (like our collection of minhash codes) into something that is quickly and easily compared with other complex objects is still applicable.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
What we do is take the collection of minhash values, and categorize them into bands and row. For the sake of simplicity we&#39;ll assume each document has 20 minhash values. We will break them down into 5 bands with 4 rows each. Also for the sake of simplicity we&#39;ll assume that .hashCode() (and any number XORed with it) results in a number between 0 and 9.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
So conceptually the minhash values from 5 documents in the first of the 5 bands and its rows looks like this.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;table style=&quot;text-align: center;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Band One&lt;/td&gt;&lt;td&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;MinHash Algorithm One&lt;/td&gt;
&lt;td&gt;MinHash Algorithm Two&lt;/td&gt;
&lt;td&gt;MinHash Algorithm Three&lt;/td&gt;
&lt;td&gt;MinHash Algorithm Four&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document One&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document Two&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document Three&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document Four&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
What we are looking for are rows within a band that are the same between documents. Document One and Document Three in this case both have rows with values 1, 3, 6, 0. This tells us that these two documents should be compared for their similarity.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Although it is not shown here, you should look through Bands Two - Five looking for documents that share rows. Any documents that share rows in any bands should&amp;nbsp;be compared for their similarity.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
This is useful when you have a document, and you want to know which other documents to compare to it for similarity. I&#39;ll assume that the minhash values are stored in SQL, so to get out candidates for comparison you would write:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
SELECT DocName&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
FROM Documents&lt;/div&gt;
&lt;div&gt;
WHERE (&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;div&gt;
BandOneMinHashAlgorithmOne = 1 AND&lt;/div&gt;
&lt;div&gt;
BandOneMinHashAlgorithmTwo = 3 AND&lt;/div&gt;
&lt;div&gt;
BandOneMinHashAlgorithmThree = 6 AND&lt;/div&gt;
&lt;div&gt;
BandOneMinHashAlgorithmFour = 0&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
) OR&lt;br /&gt;
(&lt;br /&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;div&gt;
BandTwoMinHashAlgorithmOne = # AND&lt;/div&gt;
&lt;div&gt;
BandTwoMinHashAlgorithmTwo = # AND&lt;/div&gt;
&lt;div&gt;
BandTwoMinHashAlgorithmThree = # AND&lt;/div&gt;
&lt;div&gt;
BandTwoMinHashAlgorithmFour = #&lt;/div&gt;
&lt;/div&gt;
) OR (&lt;br /&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;div&gt;
BandThreeMinHashAlgorithmOne = # AND&lt;/div&gt;
&lt;div&gt;
BandThreeMinHashAlgorithmTwo = # AND&lt;/div&gt;
&lt;div&gt;
BandThreeMinHashAlgorithmThree = # AND&lt;/div&gt;
&lt;div&gt;
BandThreeMinHashAlgorithmFour = #&lt;/div&gt;
&lt;/div&gt;
) OR (&lt;br /&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;div&gt;
BandFourMinHashAlgorithmOne = # AND&lt;/div&gt;
&lt;div&gt;
BandFourMinHashAlgorithmTwo = # AND&lt;/div&gt;
&lt;div&gt;
BandFourMinHashAlgorithmThree = # AND&lt;/div&gt;
&lt;div&gt;
BandFourMinHashAlgorithmFour = #&lt;/div&gt;
&lt;/div&gt;
) OR (&lt;br /&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;div&gt;
BandFiveMinHashAlgorithmOne = # AND&lt;/div&gt;
&lt;div&gt;
BandFiveMinHashAlgorithmTwo = # AND&lt;/div&gt;
&lt;div&gt;
BandFiveMinHashAlgorithmThree = # AND&lt;/div&gt;
&lt;div&gt;
BandFiveMinHashAlgorithmFour = #&lt;/div&gt;
&lt;/div&gt;
)&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
This explanation completely glosses over why this works and what combination of bands and rows you should construct when looking for similar documents. If you want to get into that detail, take a look at&amp;nbsp;&lt;a href=&quot;http://infolab.stanford.edu/~ullman/mmds/ch3.pdf&quot;&gt;http://infolab.stanford.edu/~ullman/mmds/ch3.pdf&lt;/a&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/7492521406696803433/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/7492521406696803433' title='384 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/7492521406696803433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/7492521406696803433'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2013/11/minhash-for-dummies.html' title='MinHash for dummies'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>384</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-1294654440287889949</id><published>2013-11-10T16:02:00.003+11:00</published><updated>2013-11-15T16:43:12.301+11:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="three.js"/><title type='text'>Three.js 1 - Getting Started</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiwJ0oxs9h9VNUohvg7Rl3jStwziG6-w61GnPOfCo9OBOIPi8gDud6BUf6y03qrXPPUBaLnHfwqKV9WqmBWHRjX-58iqFTJvR9BeiqHIq8A7CBvcFPBQLYuM4tx0kQASi0R0X2rA/s1600/screenshot.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiwJ0oxs9h9VNUohvg7Rl3jStwziG6-w61GnPOfCo9OBOIPi8gDud6BUf6y03qrXPPUBaLnHfwqKV9WqmBWHRjX-58iqFTJvR9BeiqHIq8A7CBvcFPBQLYuM4tx0kQASi0R0X2rA/s320/screenshot.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;
Three.js - 1 - Getting Started&lt;/h2&gt;
&lt;b&gt;&lt;a href=&quot;http://matthewcasperson.blogspot.com.au/2013/11/threejs-tutorials.html&quot;&gt;THREE.JS TUTORIALS&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;a href=&quot;https://googledrive.com/host/0B6cpJDiEKC62T1BjSUJzaWxXdWM/example.html&quot;&gt;VIEW THE DEMO&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;a href=&quot;https://googledrive.com/host/0B6cpJDiEKC62OUhNbkR6VllwMlE&quot;&gt;DOWNLOAD THE SOURCE CODE&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;
&lt;h3&gt;
Introduction&lt;/h3&gt;
After Apple&#39;s war on Flash, relying on plugins in your web pages is just not a viable option. Flash (and maybe, thanks to Netflix, Silverlight) were the last of a dying breed of browser plugins. In their place are a host of new technologies being standardised as part of HTML5.&lt;br /&gt;
&lt;br /&gt;
WebGL is one of these technologies, and is supported by all major browsers. Microsoft is even getting on board with IE 11. Combine that with the ever increasing performance of JavaScript engines and it is now possible to create some impressive 3D applications in a web browser without the aid of plugins.&lt;br /&gt;
&lt;br /&gt;
Three.js is one of the more powerful and mature JavaScript 3D engines available. This article will show you how to get a simple 3D application running with three.js.&lt;br /&gt;
&lt;h3&gt;
What to download&lt;/h3&gt;
Using three.js requires only a single JavaScript file called three.min.js. You can grab the latest version of file from &lt;a href=&quot;https://github.com/mrdoob/three.js/&quot;&gt;GitHub&lt;/a&gt;.&lt;br /&gt;
&lt;h3&gt;
The HTML page&lt;/h3&gt;
A three.js application is a DOM element that can be appended as a child of an element in your HTML page. This means that you can embed a 3D application in a web page like it was an image or video file. This gives you a great deal of flexibility when presenting your 3D application.&lt;br /&gt;
&lt;br /&gt;
However, for this example we will create a very basic HTML page and add the three.js application as the only child. While basic, this configuration is quite suitable for full screen games or simulations.&lt;br /&gt;
&lt;br /&gt;
As you can see, this is a very simple HTML page. The only code of importance here are the referenced to the two JavaScript files: the three.js library (three.min.js), and the JavaScript file that will hold our application (threejs-1.js).&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt;&amp;lt;html&amp;gt;
 &amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;Three.js - 1 - Getting Started&amp;lt;/title&amp;gt;
  &amp;lt;script type=&amp;quot;application/javascript&amp;quot; src=&amp;quot;javascript/three.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script type=&amp;quot;application/javascript&amp;quot; src=&amp;quot;javascript/threejs-1.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
 &amp;lt;/head&amp;gt;
 &amp;lt;body&amp;gt;
  
 &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/span&gt;  &lt;/pre&gt;
&lt;h3&gt;
The Components of a 3D Application&lt;/h3&gt;
There are 4 important classes in three.js that make up a basic 3D application: the camera, the scene, the meshes, the geometries and the materials.&lt;br /&gt;
&lt;h4&gt;
The Camera&lt;/h4&gt;
This is exactly what it sounds like. The 3D world created by three.js is viewed as if through the lens of a video camera. In fact the camera has methods like .setLens() to set properties like focal length and frame size, which relate to a camera&#39;s field of view.&lt;br /&gt;
&lt;h4&gt;
The Scene&lt;/h4&gt;
The scene is the world that you will populate with 3D objects, lights and cameras.&lt;br /&gt;
&lt;h4&gt;
The Meshes&lt;/h4&gt;
A mesh defines the individual charictaristics &amp;nbsp;of a 3D object. These include things like the position, rotation and scale of the object. A mesh will reference a geometry to define the shape of the 3D object, and a material to define the appearance of the 3D object.&lt;br /&gt;
&lt;br /&gt;
What is important to note about a mesh is that they can share geometries and materials. For example, to render a forest with a thousand 3D trees, you would need a thousand meshes, but potentially only 3 or 4 geometries (to represent the different types of trees) and 1 material.&lt;br /&gt;
&lt;h4&gt;
The Geometries&lt;/h4&gt;
The shape of a 3D object is defined by a geometry. Geometries define the various vertices and faces that make up a 3D model. Simple applications can make use of the build in geometry classes that describe a sphere, cube, cylinder and other common shapes. More complex 3D applications will load geometries created in 3D modelling applications.&lt;br /&gt;
&lt;h4&gt;
The Materials&lt;/h4&gt;
While a geometry defines the shape of a 3D object, the material defines the appearance. Materials can be a simple as a plain colour, or they can be complex shaders with reflections or animated appearances.&lt;br /&gt;
&lt;h4&gt;
Putting it all Together&lt;/h4&gt;
We start by assigning a function to the window.onload event. This is good practice when writing any JavaScript that manipulates the HTML DOM, as it is possible to start playing with the DOM before it is completely loaded. If you use jQuery, the ready() function is even better as it will allow your code to manipulate the DOM without having to wait for images to download. Since our page has no images, and we are not using jQuery, the window.onload event will work just fine.&lt;br /&gt;
&lt;br /&gt;
The function assigned to the window.onload event will call two additional functions: init() and animate().&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt;window.onload = function(){
 init();
 animate();
};  &lt;/span&gt;
&lt;/pre&gt;
&lt;br /&gt;
The init() function is where we setup the components that make up the 3D application.&lt;br /&gt;
&lt;br /&gt;
We create the camera, which is an instance of the PerspectiveCamera class. The 4 parameters supplied to the constructor are&lt;br /&gt;
&lt;br /&gt;
fov - Camera frustum vertical field of view.&lt;br /&gt;
aspect - Camera frustum aspect ratio.&lt;br /&gt;
near - Camera frustum near plane.&lt;br /&gt;
far - Camera frustum far plane.&lt;br /&gt;
We then set the position of the camera down the z axis. This will allow us to position a 3D object at the origin and be able to view it.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt;camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
 camera.position.z = 1000;    &lt;/span&gt;
&lt;/pre&gt;
&lt;br /&gt;
Next we create some geometry. Three.js provides a number of geometry classes that define a number of common shapes. Here we use the CubeGeometry class to create the geometry for a cube. The supplied parameters define the size of the cube along the three axes.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt; geometry = new THREE.CubeGeometry( 200, 200, 200 ); &lt;/span&gt;  
&lt;/pre&gt;
&lt;br /&gt;
In order for the 3D object to be visible, it needs a material. The MeshBasicMaterial class allows us to create a simple material without the use of any external image files. The MeshBasicMaterial constructor takes an object whose properties define the appearance of the material. In this case we have set the colour to red, and specified that the material should display as a wireframe.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt; material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: true } ); &lt;/span&gt; 
&lt;/pre&gt;
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt;  &lt;/span&gt;&lt;br /&gt;
The mesh uses the geometry and material to create the 3D object that we will see on the screen.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt;mesh = new THREE.Mesh( geometry, material );  &lt;/span&gt; 
&lt;/pre&gt;
&lt;br /&gt;
Now that we have a 3D object to view, we need to create the scene that will hold it. The mesh is then added to the scene.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt; scene = new THREE.Scene();
 scene.add( mesh );&lt;/span&gt;
&lt;/pre&gt;
&lt;br /&gt;
Three.js can use a number of technologies to draw the 3D application within the browser. Each is provided by a renderer class. These different renderers are provided because not all browsers support all the HTML technologies that are available to libraries like three.js. For example, Internet Explorer won&#39;t support WebGL until version 11.&lt;br /&gt;
&lt;br /&gt;
We have used the WebGL renderer here. This code will fail if it is run on any version of IE before 11, and future tutorials will provide workarounds for older browsers. For now though make sure you run the demo on Firefox or Chrome.&lt;br /&gt;
&lt;br /&gt;
Because our application will take up the full browser window, we set the size of the renderer to the size of the browser window.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt; renderer = new THREE.WebGLRenderer();
 renderer.setSize( window.innerWidth, window.innerHeight );   &lt;/span&gt;
&lt;/pre&gt;
&lt;br /&gt;
The final step is to attach the renderer to the HTML DOM. In this case the renderer is attached directly as a child of the HTML body.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt; document.body.appendChild( renderer.domElement );  &lt;/span&gt; 
&lt;/pre&gt;
&lt;br /&gt;
At this point we have created all the objects required by the 3D application. The next step is to animate the scene.&lt;br /&gt;
&lt;br /&gt;
Just like a flip book, animation of 3D applications quickly taking snapshots of the 3D objects and displaying the snapshots on the screen.&lt;br /&gt;
&lt;br /&gt;
HTML pages used to be fairly static, and only updated in response to some kind of user interaction (like clicking a button or submitting a form). This model doesn&#39;t work well for applications like the one we are creating, which expect to be able to update the browser page with each frame, independently of any explicit user interaction.&lt;br /&gt;
&lt;br /&gt;
Modern browsers support this kind of application through the requestAnimationFrame function. This function takes another function as a parameter, and will call the supplied function when the browser page is available to be refreshed. The requestAnimationFrame function is not supported by all browsers, but three.js includes a shim (or workaround) for older browsers, saving us from having to deal with these incompatibilities.&lt;br /&gt;
&lt;br /&gt;
So the animate() function is first called when the window is loaded, and is then scheduled to be recursively called each time we render a frame.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt; requestAnimationFrame( animate );  &lt;/span&gt; 
&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;br /&gt;
So we have something to look at, the cube is rotated slightly with each frame.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt; mesh.rotation.x += 0.01;
 mesh.rotation.y += 0.02;  &lt;/span&gt;&lt;span style=&quot;color: #0b5394;&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;br /&gt;
Finally, the renderer takes the scene and the camera, and renderers the current frame to the screen.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;span style=&quot;color: #cc0000;&quot;&gt; renderer.render( scene, camera ); &amp;nbsp;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt; 
&lt;/pre&gt;
&lt;h3&gt;
Final Words&lt;/h3&gt;
The end result of this tutorial has been to display a single spinning 3D cube in a web page. While this is quite a trivial example of the three.js library, it does cover all the basic elements of a 3D application, and provides a useful starting point for building more complex 3D applications.</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/1294654440287889949/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/1294654440287889949' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/1294654440287889949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/1294654440287889949'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2013/11/threejs-1-getting-started_10.html' title='Three.js 1 - Getting Started'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiwJ0oxs9h9VNUohvg7Rl3jStwziG6-w61GnPOfCo9OBOIPi8gDud6BUf6y03qrXPPUBaLnHfwqKV9WqmBWHRjX-58iqFTJvR9BeiqHIq8A7CBvcFPBQLYuM4tx0kQASi0R0X2rA/s72-c/screenshot.png" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-6692249626783863972</id><published>2013-09-23T07:47:00.000+10:00</published><updated>2013-09-23T07:48:20.058+10:00</updated><title type='text'>Stopping emails in the new Gmail tabs from showing up in new mail counts</title><content type='html'>I love the new tabbed interface in Gmail. To be blunt, email clients have done a very poor job of dealing with the increasing volume and variety of email that people get nowadays, and it is nice to see Gmail implementing simple and effective solutions.&lt;br /&gt;
&lt;br /&gt;
The one problem I have with the new interface is that email that was simply going to spam is now being categorised and filed under these tabs, and their existence is contributing to the number I see in my Chrome Gmail monitoring extension. It became impossible to know when I have mail that I was actually interested in, because the little icon in my browser was always showing 100+ new emails.&lt;br /&gt;
&lt;br /&gt;
I initially tried to setup a filter, but nothing I could type into the new filter dialog would allow me to set emails sent to the categories to be marked as read.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKwv12la0CUekMCuKB5KKsqrUreCCqPsl92ehMHl7AKfsdFywH4BAKnYlnrCUvdgPR76mw_WDg3GEQfcHM73fMnL1n3hq46F6pdExSz69IU6Tafj5QsMl7510M5loqEX72mCv97Q/s1600/snapshot7.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;271&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKwv12la0CUekMCuKB5KKsqrUreCCqPsl92ehMHl7AKfsdFywH4BAKnYlnrCUvdgPR76mw_WDg3GEQfcHM73fMnL1n3hq46F6pdExSz69IU6Tafj5QsMl7510M5loqEX72mCv97Q/s320/snapshot7.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Nothing you can type in here will let you create a filter based on the categories.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
But I soon tweaked that you can create a filter from a search, and you can search based on categories.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNSk36dHaXyoU5AKJfCAe5gmbyYpoB7yJlsg4YS1cOswOa3CxA7dLlqsCtrODhgf0g6DeyV-Aqochodrb_ExKKlHW3UAH4_wIvRb-NNh521Le94jc_sqExumXGat37orTK_q28Mg/s1600/snapshot8.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;152&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNSk36dHaXyoU5AKJfCAe5gmbyYpoB7yJlsg4YS1cOswOa3CxA7dLlqsCtrODhgf0g6DeyV-Aqochodrb_ExKKlHW3UAH4_wIvRb-NNh521Le94jc_sqExumXGat37orTK_q28Mg/s320/snapshot8.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Do a search with the &lt;b&gt;categories:&lt;/b&gt;&amp;nbsp;prefix.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi72D18eTUydCHn_0fHq6-mmNiUHx49mJhnM9kXKh3d8dndJoS2LxHH_9UXQP5Wph9f3eJsWFtDYcEUBl1s7ne6K2QG6OJlj6_sVkw0EB0yCtYfC3DeMYVJVYw0Met6JwT27Darsw/s1600/snapshot9.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;317&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi72D18eTUydCHn_0fHq6-mmNiUHx49mJhnM9kXKh3d8dndJoS2LxHH_9UXQP5Wph9f3eJsWFtDYcEUBl1s7ne6K2QG6OJlj6_sVkw0EB0yCtYfC3DeMYVJVYw0Met6JwT27Darsw/s320/snapshot9.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Drop the search menu down again, and the &lt;b&gt;Create filter with this search&lt;/b&gt;&amp;nbsp;link is now active!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
And with that, any email sent to one of the new tabs can be marked as read, leaving your mail monitoring extensions showing only the actual inbox count.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVIAFPF8zhaenfeUmdh-zvYiArWS-XulicvZjd_p7Tb9Q-3rmCRzehAQ0xiOHpqZ-IqEA02jC62cuWEgANcsJ-6DP1spTJuUhrWRK6I2lRsSvwa4LTAROxXqb2se1y6eQZRIeHTw/s1600/snapshot10.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;157&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVIAFPF8zhaenfeUmdh-zvYiArWS-XulicvZjd_p7Tb9Q-3rmCRzehAQ0xiOHpqZ-IqEA02jC62cuWEgANcsJ-6DP1spTJuUhrWRK6I2lRsSvwa4LTAROxXqb2se1y6eQZRIeHTw/s320/snapshot10.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/6692249626783863972/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/6692249626783863972' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/6692249626783863972'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/6692249626783863972'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2013/09/stopping-emails-in-new-gmail-tabs-from.html' title='Stopping emails in the new Gmail tabs from showing up in new mail counts'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKwv12la0CUekMCuKB5KKsqrUreCCqPsl92ehMHl7AKfsdFywH4BAKnYlnrCUvdgPR76mw_WDg3GEQfcHM73fMnL1n3hq46F6pdExSz69IU6Tafj5QsMl7510M5loqEX72mCv97Q/s72-c/snapshot7.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-4708244999420351144</id><published>2013-07-05T16:05:00.000+10:00</published><updated>2013-08-23T15:54:06.063+10:00</updated><title type='text'>Exporting from MySQL to H2</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9XaHq5HBL8stX1dcXRLlrGJ7TriGxo_7YR3aA2xdBYmWi5h3VgmR_hh9HxRYm4zr8kgXwed5o50Yv_7seEEueBqUymp5FStmpcruzpt6lNP2Yb2-4Jb6wRI5j7O0iqgVHYjtAKQ/s274/h2-logo-2.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9XaHq5HBL8stX1dcXRLlrGJ7TriGxo_7YR3aA2xdBYmWi5h3VgmR_hh9HxRYm4zr8kgXwed5o50Yv_7seEEueBqUymp5FStmpcruzpt6lNP2Yb2-4Jb6wRI5j7O0iqgVHYjtAKQ/s274/h2-logo-2.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;When it comes to testing, you can&#39;t beat the convenience of an embedded database. It can be created, populated and cleaned up without any dependencies on outside infrastructure, making it ideal for self contained test suites.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;However, there is a very good chance that you don&#39;t use an embedded database in your production environment. In my case, I have a MySQL database in production, and want to use H2 in testing.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Getting data from MySQL into H2 is not as straight forward as you might think. You might have some luck with &lt;a href=&quot;http://dbcopyplugin.sourceforge.net/&quot;&gt;CopyDB&lt;/a&gt;, 

&lt;a href=&quot;http://scriptella.javaforge.com/?&quot;&gt;Scriptella&lt;/a&gt;,
or &lt;a href=&quot;http://opendbcopy.sourceforge.net/&quot;&gt;OpenDBCopy&lt;/a&gt;. But in the end I simply used mysqldump and a couple of hacks to get data from MySQL to H2.&lt;/span&gt;&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Dump the data&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Dump the data from MySQL with the following command:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;mysqldump --compatible=ansi,no_table_options,no_field_options,no_key_options --hex-blob --skip-opt -uroot -p Database &amp;gt; importansi.sql&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;This will strip out most of the MySQL specific syntax and give you something that you can clean up for H2 to import.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Fix up single quotes&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;MySQL will escape a single quote with a backslash, like &lt;b&gt;\&#39;&lt;/b&gt;. H2 expects to have single quotes escaped with another single quote, like &lt;b&gt;&#39;&#39;&lt;/b&gt;. &lt;a href=&quot;http://bugs.mysql.com/bug.php?id=65941&quot;&gt;There has been a request &lt;/a&gt;to have this kind of escaping included as an option in MySQL, but it has not been implemented. So you&#39;ll have to do a find and replace for yourself.&lt;/span&gt;&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Fix up hex numbers&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;a href=&quot;http://stackoverflow.com/questions/11627248/cannot-transfer-blob-from-mysql-to-h2&quot;&gt;H2 doesn&#39;t import hex numbers properly&lt;/a&gt;. To fix that up, you&#39;ll need to find any hex numbers and replace them with strings.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;If you have a text editor that can find and replace with regular expressions, this can be done by finding &lt;b&gt;0x([A-F0-9]*)&lt;/b&gt; and replacing it with &lt;b&gt;&#39;$1&#39;&lt;/b&gt;.&lt;/span&gt;&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Fix up bits&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The default value for a bit in MySQL will be represented as &lt;b&gt;b&#39;0&#39;&lt;/b&gt;. Replace this with a plain &lt;b&gt;0&lt;/b&gt; for H2.&lt;/span&gt;&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Don&#39;t include ranges in keys&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;MySQL has the ability to define a range on a key, like &lt;b&gt;KEY &quot;ATextFieldKey&quot; (&quot;&lt;/b&gt;&lt;b&gt;&lt;b&gt;ATextField&lt;/b&gt;&quot;(255))&lt;/b&gt;. This doesn&#39;t work in H2, so replace it with &lt;b&gt;KEY &quot;ATextFieldKey&quot; (&quot;&lt;/b&gt;&lt;b&gt;&lt;b&gt;ATextField&lt;/b&gt;&quot;)&lt;/b&gt;.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Remove character sets&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Remove any character set information on a field like &lt;b&gt;CHARACTER SET latin1&lt;/b&gt;. &lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Remove COLLATE settings&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Remove any collate information on a field like&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;b&gt;COLLATE utf8_unicode_ci&lt;/b&gt;.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Remove indexes on BLOBS, CLOBS and TEXT fields&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;H2 does not support indexes on these fields. Queries on these fields will be slower, but you should be able to take that into account in your tests.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Make all index names unique&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;MySQL only requires that an index name be unique within a table. H2 will require that it be unique within the database. So change any duplicated index names before importing into H2.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Use the MySQL compatibility mode&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Even though we have dumped the MySQL database using the ANSI compatibility mode, we still need to enable the &lt;a href=&quot;http://www.h2database.com/html/features.html#compatibility&quot;&gt;MySQL compatibility mode in H2&lt;/a&gt;. This is done through a connection URL that looks like &lt;b&gt;jdbc:h2:~/test;MODE=MySQL&lt;/b&gt;.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Reorder the table creation sequence&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;MySQL will dump tables in alphabetical order. When it imports a database dumps, some commands in the comments instruct MySQL to ignore relationships, allowing MySQL to create tables with foreign keys to tables that have not yet been created.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;H2 does not have the same ability to ignore relationships (I tried &lt;/span&gt;&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;a href=&quot;http://www.h2database.com/html/grammar.html#set_referential_integrity&quot;&gt;SET REFERENTIAL_INTEGRITY FALSE&lt;/a&gt;, but it didn&#39;t work), and so you&#39;ll have to do some copying and pasting to make sure that any table that is pointed to in a relationship exists before the relationship is created.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;If you have circular dependencies, you&#39;ll have to remove the foreign key creation from the CREATE TABLE command, and create the &lt;/span&gt;&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;foreign keys only after both tables have been created.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;For those that want to automate this process, fork the repo hold &lt;a href=&quot;https://github.com/pressgang-ccms/PressGangCCMSDeployable/blob/master/src/com/jboss/pressgang/ccms/MySqlH2Conversion.groovy&quot;&gt;this script&lt;/a&gt;. It has saved us quite a few hours worth of work.&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/4708244999420351144/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/4708244999420351144' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/4708244999420351144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/4708244999420351144'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2013/07/exporting-from-mysql-to-h2.html' title='Exporting from MySQL to H2'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9XaHq5HBL8stX1dcXRLlrGJ7TriGxo_7YR3aA2xdBYmWi5h3VgmR_hh9HxRYm4zr8kgXwed5o50Yv_7seEEueBqUymp5FStmpcruzpt6lNP2Yb2-4Jb6wRI5j7O0iqgVHYjtAKQ/s72-c/h2-logo-2.png" height="72" width="72"/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-4242558231804568795</id><published>2013-07-03T15:16:00.001+10:00</published><updated>2013-11-06T16:27:47.450+11:00</updated><title type='text'>Setting up a MariaDB Galera cluster in Centos 6</title><content type='html'>&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
&amp;nbsp;Synopsis&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;One of the big draw cards of MariaDB over MySQL is open source support for clustering. While the open source version of MySQL allows for replication amongst a number of read only nodes, MariaDB has true multi-master replication.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;If you have read the documentation at https://kb.askmonty.org/en/getting-started-with-mariadb-galera-cluster/, you could be forgiven for believing that setting up a cluster is a trivial process. Unfortunately those instructions leave out a few vital details that will prevent your cluster from working. These are the steps I used to get a MariaDB Galera Cluster running in Centos 6.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Configure the YUM repo&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Go to https://downloads.mariadb.org/mariadb/repositories/ and select Centos as the OS, and 5.5 as the version. Galera (the clustering component of MariaDB) is only available with version 5.5 at this time.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;I have the 64 bit version of Centos, so the resulting file looks like this:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;# MariaDB 5.5 CentOS repository list - created 2013-07-03 04:40 UTC&lt;br /&gt;# http://mariadb.org/mariadb/repositories/&lt;br /&gt;[mariadb]&lt;br /&gt;name = MariaDB&lt;br /&gt;baseurl = http://yum.mariadb.org/5.5/centos6-amd64&lt;br /&gt;gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB&lt;br /&gt;gpgcheck=1&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The file for the 32 bit version of Centos looks like:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;# MariaDB 5.5 CentOS repository list - created 2013-07-03 04:46 UTC&lt;br /&gt;# http://mariadb.org/mariadb/repositories/&lt;br /&gt;[mariadb]&lt;br /&gt;name = MariaDB&lt;br /&gt;baseurl = http://yum.mariadb.org/5.5/centos6-x86&lt;br /&gt;gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB&lt;br /&gt;gpgcheck=1&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;You&#39;ll want to save this file into &lt;b&gt;/etc/yum.repo.d/MariaDB.repo&lt;/b&gt;.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Install MariaDB&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;MariaDB is a drop in replacement for MySQL. If you have installed the desktop packages for Centos, chances are you have MySQL installed already. To uninstall it, run the following command:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;&lt;br /&gt;yum erase *mysql*&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Installing MariaDB Galera is done with the following command:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;yum install MariaDB-Galera-server MariaDB-client setroubleshoot&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;You will notice that we are also installing the SELinux Troubleshooter. This will come in handy later.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Initial Database Configuraion&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;We need to start the MariaDB service. Because it is a drop in replacement for MySQL, you use the same command to start the server as you would to start MySQL.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;service mysql start&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Upgrading the databases fixes an issue where MySQL Workbench will simply say &quot;fetching...&quot; and will never list the tables available in the database.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;mysql_upgrade&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;It is good practice to secure the new installation. MariaDB provides a script that will run you through the process.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;mysql_secure_installation&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;We also need to setup some passwords. To do that we load the mysql command line client, and log into our database.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;mysql -hlocalhost -uroot&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The following commands will set passwords on the root account, both when accessed locally, and when accessed from a remote system. Obviously you can change the password in these commands to something a little more secure, but for this tutorial we&#39;ll use &#39;password&#39;.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;GRANT ALL PRIVILEGES ON *.* TO &#39;root&#39;@&#39;localhost&#39; IDENTIFIED BY &#39;password&#39; WITH GRANT OPTION;&lt;br /&gt;&lt;br /&gt;GRANT ALL PRIVILEGES ON *.* TO &#39;root&#39;@&#39;%&#39; IDENTIFIED BY &#39;password&#39; WITH GRANT OPTION;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;FLUSH PRIVILEGES;&lt;br /&gt;&lt;br /&gt;exit&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The last step is to shutdown MariaDB&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;service mysql stop&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Open the network ports&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;A MariaDB cluster requires ports 3306 and 4567 to be open. If you use rsync to do the state transfers, you&#39;ll also need port 4444 open. You can do by running the command:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;system-config-firewall-tui&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;In the other ports section, you need to open up 3306, 4444 and 4567, all with the tcp protocol.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEie9d7cMPhYIzfFCPNpQyW-6pGr1aUUEWMhuUsB6VCshNy6YI9t8imGodyhKtn0T5Uwprt2GZ__kSQ5FMP4xL7Q3q2xTp4iq3tMiUbNoC-2eTSvrafbfuGjNDZCcKBiAirYLG7xvw/s661/firewall.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;224&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEie9d7cMPhYIzfFCPNpQyW-6pGr1aUUEWMhuUsB6VCshNy6YI9t8imGodyhKtn0T5Uwprt2GZ__kSQ5FMP4xL7Q3q2xTp4iq3tMiUbNoC-2eTSvrafbfuGjNDZCcKBiAirYLG7xvw/s320/firewall.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Fix SELinux Issues&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The quick way to fix this is to disable SELinux with the command:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;setenforce 0&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;If you want to leave SELinux enabled, you&#39;ll need to restart Centos to allow the SELinux Troubleshooter we installed earlier to pick up on any violations.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;When you attempt to run MariaDB in the steps below, SELinux Troubleshooter will report three errors.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1OijlTE-yoQ2LCGl_6PSY3MFMiktDYxFau2tkMCgNL_tQ5IECodg5lNhS03uq6A2EOqmXUzWb495GcJ_GGigA_BAZD_3WvkZHznbvgM7J4KCVEo9HEn0jEDbP93tdYv1WFn06EA/s754/selinux1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;101&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1OijlTE-yoQ2LCGl_6PSY3MFMiktDYxFau2tkMCgNL_tQ5IECodg5lNhS03uq6A2EOqmXUzWb495GcJ_GGigA_BAZD_3WvkZHznbvgM7J4KCVEo9HEn0jEDbP93tdYv1WFn06EA/s320/selinux1.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBcAiYJ8pP_ViER12QcEYRbO3ADDW8ptqVdLm02rMpSlMfy7oCB2J7XSdC1Udwkm7h49DKWOMEDE9GQxHwrr4OX-_Z4D5n0LpiqNpcbQZnyXWFNQhI7eLugpztpU4eEMYO7ll-Xg/s752/selinux2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;101&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBcAiYJ8pP_ViER12QcEYRbO3ADDW8ptqVdLm02rMpSlMfy7oCB2J7XSdC1Udwkm7h49DKWOMEDE9GQxHwrr4OX-_Z4D5n0LpiqNpcbQZnyXWFNQhI7eLugpztpU4eEMYO7ll-Xg/s320/selinux2.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4sdu6WDiptIiAZKRc21YFu6-F5e0fLbv1Vh8EC4ESmjL-VvGYi5Bf5lWGdNcstJGS5CjjygPHt9yyu2Ez_8gI3i0RwfNX83s281hpfIe9RJVc_tk0I-dZrlZ1s6mJTEXoKkHyOA/s754/selinux6.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;101&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4sdu6WDiptIiAZKRc21YFu6-F5e0fLbv1Vh8EC4ESmjL-VvGYi5Bf5lWGdNcstJGS5CjjygPHt9yyu2Ez_8gI3i0RwfNX83s281hpfIe9RJVc_tk0I-dZrlZ1s6mJTEXoKkHyOA/s320/selinux6.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Clicking the &lt;b&gt;Details&lt;/b&gt; button on each of these alerts provides the commands you need to run to allow MariaDB to run with SELinux enabled.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibscahIl96lbj2mqxbkNGCyATLgEY3QnHo100soEO4bm0FaQQqo03ZnSSQ1uNsBD_N3uHIHhFZF4VbqqwamfBUcmuOj5fyJuVObBkS_kB9KfZhDswMqn1HcUJl3RgcewBRu91GJw/s605/selinux3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;280&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibscahIl96lbj2mqxbkNGCyATLgEY3QnHo100soEO4bm0FaQQqo03ZnSSQ1uNsBD_N3uHIHhFZF4VbqqwamfBUcmuOj5fyJuVObBkS_kB9KfZhDswMqn1HcUJl3RgcewBRu91GJw/s320/selinux3.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGpM7pdEuH0H5Xoajk0FKdi0ekArkathyphenhyphen_ou-WAlMvjQuOq-enL3wx2qgNxqIsNORhIeiWMTxC9xCS6fCCXQjnFjBk9skMuT_3CUJ9AEDdUeIKRKbRn0OEZUzufTUbXNptdRaTVg/s604/selinux4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;280&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGpM7pdEuH0H5Xoajk0FKdi0ekArkathyphenhyphen_ou-WAlMvjQuOq-enL3wx2qgNxqIsNORhIeiWMTxC9xCS6fCCXQjnFjBk9skMuT_3CUJ9AEDdUeIKRKbRn0OEZUzufTUbXNptdRaTVg/s320/selinux4.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfhF950CimtZ-b-Pgmgf9Bf5b8lWtF3Xp3LN4CvfoS5d6B1lpi2Z7Tin4FE5jYXHwEFWRShQPLfE5clmVHR6Zuppvuel4Oel1iC_a2BDik5zRGZk8dhFLXmieSBRi5LIQg6zwszg/s602/selinux5.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;280&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfhF950CimtZ-b-Pgmgf9Bf5b8lWtF3Xp3LN4CvfoS5d6B1lpi2Z7Tin4FE5jYXHwEFWRShQPLfE5clmVHR6Zuppvuel4Oel1iC_a2BDik5zRGZk8dhFLXmieSBRi5LIQg6zwszg/s320/selinux5.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Start the cluster&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;MariaDB requires 3 nodes to effectively provide fault tolerance. We&#39;ll get two running, but the process is the same for any additional nodes.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Start the first server with the command&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;mysqld --wsrep_cluster_address=gcomm:// --user=mysql --wsrep_provider=/usr/lib64/galera/libgalera_smm.so --wsrep_sst_auth=root:password&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The &lt;b&gt;--wsrep_cluster_address=gcomm://&lt;/b&gt; option defines this as the initial node in the cluster. &lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The &lt;b&gt;--user=mysql&lt;/b&gt; option tells MariaDB to start under the mysql user&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The &lt;b&gt;--wsrep_provider=/usr/lib64/galera/libgalera_smm.so&lt;/b&gt; option tells MariaDB where the Galera library is.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The&lt;b&gt; --wsrep_sst_auth=root:password&lt;/b&gt; option provides the credentials that MariaDB requires when syncing the initial state of the cluster to newly joined nodes. Change the password to reflect the password you set earlier.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;span style=&quot;color: red;&quot;&gt;Remember that if you have SELinux enabled, it is at this point that the SELinux Troubleshooter should be notifying you of issues. If not, make sure you have restarted Centos to allow the troubleshooter to catch violations.&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Joining the cluster&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;With the first node up, run this command on the second node to join it:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;mysqld --wsrep_cluster_address=gcomm://192.168.0.1 --user=mysql --wsrep_provider=/usr/lib64/galera/libgalera_smm.so&amp;nbsp; --wsrep_sst_auth=root:password&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The only change here is the &lt;b&gt;--wsrep_cluster_address=gcomm://192.168.0.1&lt;/b&gt; option. We have assumed that the first node has an IP address of 192.168.0.1, and have requested that this second node connect to the first node.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: #274e13; font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;NOTE&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: #274e13; font-family: Arial, Helvetica, sans-serif;&quot;&gt;One way to avoid editing my.cnf in order to remove gcomm:// is to start all cluster nodes with the following URL: gcomm://node1,node2:port2,node3?pc.wait_prim=no&amp;amp;...&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: #274e13; font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: #274e13; font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: #274e13; font-family: Arial, Helvetica, sans-serif;&quot;&gt;(note the &#39;pc.wait_prim=no&#39; option which makes the node to wait for primary component indefinitely.) Then bootstrap the primary component by running&amp;nbsp;&lt;b&gt;SET GLOBAL wsrep_provider_options=&quot;pc.bootstrap=1&quot;;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #274e13; font-family: Arial, Helvetica, sans-serif;&quot;&gt;&amp;nbsp;on any one of the nodes.&lt;/span&gt;&lt;br /&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Testing the cluster&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;At this point you should be able to make changes to the database tables on one server and see them reflected on the second server. Your cluster is now up and running!&lt;/span&gt;&lt;br /&gt;
&lt;h2&gt;
&lt;/h2&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;
Making the changes permanent&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;All the configuration has been defined in command line options, which means that if one of the servers went down, it would not reconnect to the cluster.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;To save the settings we supplied on the command line, edit the &lt;b&gt;/etc/my.cnf&lt;/b&gt; file, and add the following lines to the end of it. This configuration allows the first node to reconnect to the second.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;[mariadb]&lt;br /&gt;&lt;br /&gt;wsrep_cluster_address=gcomm://192.168.0.2&lt;br /&gt;&lt;br /&gt;wsrep_provider=/usr/lib64/galera/libgalera_smm.so&lt;br /&gt;&lt;br /&gt;wsrep_sst_auth=root:password&lt;br /&gt;&lt;br /&gt;wsrep_node_address=192.168.0.1&lt;br /&gt;&lt;br /&gt;log-error=/var/log/mysql.log&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The &lt;b&gt;wsrep_node_address&lt;/b&gt; option defines the IP address of the local host. This is optional, but when I ran MariaDB on my VM, it reported that it could not automatically determine the IP address, which is why it is hard coded in this option.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The&lt;b&gt; log-error&lt;/b&gt; option defines the log file that MariaDB will write to.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;All the other options are exactly the same as what was supplied on the command line.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;With this configuration in place you can kill the mysql instance that was started from the command line. The second node is still up, and so the cluster is still up. The first node can then be restarted with the command:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;service mysql start&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;This will start MariaDB and reconnect it to the second node, and thus to the cluster.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;We can then follow the same process for the second node. Of course you&#39;ll need to change the IP addresses from those listed above. The configuration to add to the &lt;b&gt;/etc/my.cnf&lt;/b&gt; file for the second node looks like:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;[mariadb]&lt;br /&gt;&lt;br /&gt;wsrep_cluster_address=gcomm://192.168.0.1&lt;br /&gt;&lt;br /&gt;wsrep_provider=/usr/lib64/galera/libgalera_smm.so&lt;br /&gt;&lt;br /&gt;wsrep_sst_auth=root:password&lt;br /&gt;&lt;br /&gt;wsrep_node_address=192.168.0.2&lt;br /&gt;&lt;br /&gt;log-error=/var/log/mysql.log&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;All we have done is changes the node it is to connect to, and the IP address of the local host.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The mysql instance started from the command line for the second node can then be killed and restarted as a service. It will then rejoin the first node.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Restarting the cluster&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;As long as one node is up, the cluster will remain active, and the node that is down will have something to connect to when it starts up. But what happens when all nodes go down?&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;At that point you&#39;ll have to restart the cluster on the node that has the version of the database you want to use. So if node 1 went down, and then a day later node 2 went down, it is most likely that node 2 will have the copy of the database you want to keep, and so node 2 will be used to restart the cluster.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Restarting the cluster is best done from the command line. Since all our settings are now stored in the /etc/my.cnf file, restarting the node as the initial node in the cluster can be done with the following command:&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;mysqld --wsrep_cluster_address=gcomm:// --user=mysql&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Just like before, an empty address in the &lt;/span&gt;&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;wsrep_cluster_address&lt;/b&gt; option defines this node as the initial node in the cluster.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The node one can then be started as a service, and it will connect to node 2, and the cluster will be running once again.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;You probably don&#39;t want to have a terminal window open with node 2 running, so once node 1 is running you can kill node 2, and restart it as a service. Node 2 will connect to node 1, and the cluster is back in action.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Troubleshooting&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
&lt;h3&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Name Resolution &lt;/span&gt;&lt;/h3&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;If the node trying to connect to an establish cluster is reporting the error:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;130307 15:14:52 [Warning] IP address &#39;192.168.0.1&#39; could not be resolved: Name or service not known&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;And the node it is trying to connect to is displaying the error:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;&#39;wsrep_sst_mysqldump --user &#39;root&#39; --password &#39;password&#39; --host &#39;192.168.0.2&#39; --port &#39;3306&#39; --local-port &#39;3306&#39; --socket &#39;/var/lib/mysql/mysql.sock&#39; --gtid &#39;4c754641-e45a-11e2-0800-425dfc14f8f4:390&#39;&#39; failed: 1 (Operation not permitted)&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;The try adding the following option to the nodes configuration files:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;skip-name-resolve&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Be aware that by doing this, MariaDB will no longer use credentials with host names. That means you&#39;ll have to configure the password on root@127.0.0.1 instead of root@localhost.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Credentials&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;If the node trying to connect to an establish cluster is reporting the error:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;WSREP: gcs/src/gcs_group.c:gcs_group_handle_join_msg():719: Will never receive state. Need to abort.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;And the node it is trying to connect to is displaying the error:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt; [ERROR] WSREP: Try 1/3: &#39;wsrep_sst_mysqldump --user &#39;root&#39; --password &#39;password&#39; --host &#39;192.168.0.1&#39; --port &#39;3306&#39; --local-port &#39;3306&#39; --socket &#39;/var/lib/mysql/mysql.sock&#39; --gtid &#39;4c754641-e45a-11e2-0800-425dfc14f8f4:420&#39;&#39; failed: 1 (Operation not permitted)&lt;br /&gt;ERROR 1045 (28000): Access denied for user &#39;root&#39;@&#39;192.168.0.2&#39; (using password: YES)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Make sure the account on the node trying to connect to the cluster is correct. The established node that is being connected to is trying to send the database state to the connecting node, and is failing to do so because of an authentication error.&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;/span&gt;

&lt;br /&gt;
&lt;h3&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;Rsync&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;If you see the error on the established node of&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;rsync: failed to connect to 192.168.0.2: No route to host (113)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;It means that the default state transfer method is Rsync, not mysqldump. To use mysqldump, add the setting&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;b&gt;&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;wsrep_sst_method=&lt;/span&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;mysqldump&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Or you can open firewall port 4444.&lt;/span&gt;&lt;br /&gt;
&lt;h3&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Rsync Wan&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;If you see the error&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;b&gt;sh: wsrep_sst_rsync_wan: command not found&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;in the log files of the node trying to connect to a cluster, run&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;b&gt;cd /usr/bin&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;b&gt;ln -s wsrep_sst_rsync wsrep_sst_rsync_wan&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h3&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;File Access&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;If you get the error &lt;b&gt;File &#39;/var/lib/mysql/aria_log_control&#39; not found&lt;/b&gt;&amp;nbsp;make sure that the file is owned my the user mysql. I ran into this issue running mysql_install_db as root, which meant that the file was owned by root.&lt;/span&gt;&lt;/div&gt;
&lt;h2&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Tips and Tricks&lt;/span&gt;&lt;/h2&gt;
&lt;h3&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;Initial Setup &lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;You can find out how large your SQL database is with the command:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;b&gt;SELECT table_schema,
&lt;br /&gt;&amp;nbsp; sum(data_length) / 1024 / 1024 &quot;data&quot;,
&lt;br /&gt;&amp;nbsp; sum(index_length) / 1024 / 1024 &quot;index&quot;,
&lt;br /&gt;&amp;nbsp; sum( data_length + index_length ) / 1024 / 1024 &quot;total&quot;
&lt;br /&gt;FROM information_schema.TABLES
&lt;br /&gt;GROUP BY table_schema;&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;This value includes all the indexes, and represents the amount of data MariaDB will transfer to an empty node when it joins the cluster.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;To save yourself some time, initialise the nodes that will join the cluster by dumping the SQL data into a file with the command:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;b&gt;mysqldump -u root -ppassword --all-databases &amp;gt; all-database.sql &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;And then restoring it on the node to be connected with&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;b&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;mysql -u root -ppassword &amp;lt; &lt;/span&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;all-database.sql&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Because the backup will not contain the indexes, and can be compressed, you&#39;ll find that you&#39;ll be able to get a remote node up to a relatively recent initial state far more quickly than attempting to sync the entire database through the MariaDB state transfer. In my case a 5GB database could be backed up into a 600 MB compressed SQL file.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;It doesn&#39;t matter if the database changes between when you backed it up and when the node connects, because the state transfer will take care of that for you. &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;h3&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Save Bandwidth&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Use the setting&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;wsrep_sst_method=rsync_wan&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt;to save some bandwidth for state transfers over a WAN connection.&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;font-family: Arial,Helvetica,sans-serif;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/4242558231804568795/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/4242558231804568795' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/4242558231804568795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/4242558231804568795'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2013/07/setting-up-galera-cluster-in-centos-6.html' title='Setting up a MariaDB Galera cluster in Centos 6'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEie9d7cMPhYIzfFCPNpQyW-6pGr1aUUEWMhuUsB6VCshNy6YI9t8imGodyhKtn0T5Uwprt2GZ__kSQ5FMP4xL7Q3q2xTp4iq3tMiUbNoC-2eTSvrafbfuGjNDZCcKBiAirYLG7xvw/s72-c/firewall.png" height="72" width="72"/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-7652213720391332566</id><published>2013-06-24T07:45:00.000+10:00</published><updated>2013-06-24T07:46:25.301+10:00</updated><title type='text'>Emscripten - the saviour we have been waiting for?</title><content type='html'>Turn the clock back a few years and listen to the promise of this new language called Java. &quot;Write once, run anywhere&quot; was going to change the way developers wrote applications. It didn&#39;t matter what OS, browser or device you were using, Java was going to be there.&lt;br /&gt;
&lt;br /&gt;
It didn&#39;t quite work out that way. Most mobile devices don&#39;t run the garden variety of Java (Android certainly relies on Java, but much to Oracle&#39;s disgust, it is not their Java), I haven&#39;t seen an applet based website in years, and even desktop Java applications are hard to come by if you are not a developer.&lt;br /&gt;
&lt;br /&gt;
It was JavaScript that won the battle for the ubiquitous development platform. Speed and functionality still varies between devices, but if you want to write code that at least has a chance of running everywhere, JavaScript is your best choice.&lt;br /&gt;
&lt;br /&gt;
The problem with JavaScript is that it lacks a lot of the libraries that other languages have had years to build up. This void is something that emscripten hopes to fill by giving developers the opportunity to compile their favourite C/C++ apps into JavaScript.&lt;br /&gt;
&lt;br /&gt;
I recently used emscripten to compile two XSLT libraries into JavaScript. XSLT on the client is quite limited by a number of longstanding and low priority bugs (&lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=69799&quot;&gt;https://bugzilla.mozilla.org/show_bug.cgi?id=69799&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href=&quot;https://code.google.com/p/chromium/issues/detail?id=8441&quot;&gt;https://code.google.com/p/chromium/issues/detail?id=8441&lt;/a&gt;&amp;nbsp;combine to make any serious XSLT transformations impossible with JavaScript), so I had high hopes that I emscripten would allow me to run either xsltproc or xalan on the client.&lt;br /&gt;
&lt;br /&gt;
The good news is that I got both application compiled and running in the browser. The code for xalan can be found at&amp;nbsp;&lt;a href=&quot;https://github.com/mcasperson/xalan.js&quot;&gt;https://github.com/mcasperson/xalan.js&lt;/a&gt;&amp;nbsp;and xsltproc can be found at&amp;nbsp;&lt;a href=&quot;https://github.com/mcasperson/xsltproc.js&quot;&gt;https://github.com/mcasperson/xsltproc.js&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The process for getting both to compile was quite manual and time consuming. You are building in clean room environment, which means you have to manage all dependencies beyond the few provided by emscripten (like libc or sdl). I imagine the standard libraries provided by emscripten will grow, but at this point it is up to you to know what your application depends on and include it in your build environment.&lt;br /&gt;
&lt;br /&gt;
I also found that optimization was a case of start with everything and work back until you find what options break the application you are compiling. Neither xsltproc nor xalan would work with the recommended setting of -O2, and removing the optimizations one by one, recompiling and testing was &lt;b&gt;very&lt;/b&gt;&amp;nbsp;time consuming.&lt;br /&gt;
&lt;br /&gt;
And unfortunately the end result was not all that useful: both applications resulted in 20 MB+ JavaScript files; a&amp;nbsp;bug in chrome (&lt;a href=&quot;https://code.google.com/p/chromium/issues/detail?id=252492&quot;&gt;https://code.google.com/p/chromium/issues/detail?id=252492&lt;/a&gt;) prevented xsltproc from running in a web worker; Firefox was slower than Chrome when running xsltproc by a factor of aruond 6; and xalan took upwards of 90 seconds to transform a simple XML document (compared to the less than 1 second for the native build).&lt;br /&gt;
&lt;br /&gt;
As cool as emscripten is (take a loot at &lt;a href=&quot;http://vps2.etotheipiplusone.com:30176/redmine/projects/emscripten-qt/wiki/Demos&quot;&gt;some KDE applications running in the browser&lt;/a&gt;), it seems that it will be some time before it is possible to take any existing C++ application and have it compiled reliably into JavaScript and run consistently in the major browsers.</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/7652213720391332566/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/7652213720391332566' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/7652213720391332566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/7652213720391332566'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2013/06/emscripten-saviour-we-have-been-waiting.html' title='Emscripten - the saviour we have been waiting for?'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-8551394929795354779</id><published>2013-03-21T07:57:00.001+11:00</published><updated>2013-03-21T07:57:27.641+11:00</updated><title type='text'>The day I left &quot;mainstream&quot; news forever</title><content type='html'>news.com.au (NCA) is a news website run by News Limited (part of the same group of companies&lt;a href=&quot;http://en.wikipedia.org/wiki/News_Corporation_scandal&quot;&gt; now well known for hacking famous voice mails&lt;/a&gt;), and was my last tie to the world of traditional news.&lt;br /&gt;
&lt;br /&gt;
It was a little guilty pleasure. Their focus was solely on catchy headlines and simple pieces of reporting that worked well for those like me looking for a five minute break. And their penchant for attaching bikini shots to any story even remotely related to the human body was also quite entertaining.&lt;br /&gt;
&lt;br /&gt;
Then one day NCA decided to fill the space that bordered the central main content column with ads. That in itself was not unlike a lot of big web sites. But they also chose to open the ad when you clicked anywhere in that border, even if you had scrolled down well past the actual ad content and the border&amp;nbsp; just displayed white space. It was a devious tactic, as I found that I usually switched back to a browser window not by clicking the window&#39;s title bar, but by clicking in some white space in the page that was being displayed.&lt;br /&gt;
&lt;br /&gt;
After being caught out more than once with a flashy pop up ad, I decided to remove NCA from my browsing vocabulary. And with that, I&#39;d like to welcome news.google.com (NGC) into my life. Yes Rupert, you are actively pushing people into the arms of your arch nemesis.&lt;br /&gt;
&lt;br /&gt;
It was something of an adjustment. NGC presented headlines that I had to think about. There were no boob shots. I couldn&#39;t read the first two paragraphs of the stories and safely ignore the remaining text as SEO filler. And I feel so much better for it.&lt;br /&gt;
&lt;br /&gt;
So it is with no great sadness that I say goodbye to traditional media, and fully embrace the brave new world of content aggregation and tick&#39;n&#39;flick news consumption. NCA, you shall not be missed.</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/8551394929795354779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/8551394929795354779' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/8551394929795354779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/8551394929795354779'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2013/03/the-day-i-left-mainstream-news-forever.html' title='The day I left &quot;mainstream&quot; news forever'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-3739186020837888143</id><published>2013-01-03T11:22:00.000+11:00</published><updated>2013-01-03T11:22:32.334+11:00</updated><title type='text'>Ubuntu phone - the first truly unified platform?</title><content type='html'>The &lt;a href=&quot;http://arstechnica.com/gadgets/2013/01/canonical-unveils-ubuntu-phone-os-that-doubles-as-a-full-pc/?utm_source=feedburner&amp;amp;utm_medium=feed&amp;amp;utm_campaign=Feed%3A+arstechnica%2Findex+(Ars+Technica+-+All+content)&quot;&gt;Ubuntu phone OS&lt;/a&gt; has been announced, replacing the old &quot;Ubuntu for Android&quot; initiative that seems to have been all but&amp;nbsp;abandoned. Frankly &lt;a href=&quot;http://matthewcasperson.blogspot.com.au/2012/02/after-second-thought-this-is-why-ubuntu.html&quot;&gt;I&#39;m not surprised that Ubuntu for Android didn&#39;t gain much traction&lt;/a&gt;, as the concept had a number of limitations that made it a burden instead of adding value. But the Ubuntu phone OS is another story.&lt;br /&gt;
&lt;br /&gt;
From the information made available in the Canonical press release, it seems that the Ubuntu phone OS is a fully fledged version of Ubuntu that can be installed directly onto a smartphone. It is a single set of apps and single configuration that presents a tailored interface depending on whether your phone is docked or standalone.&lt;br /&gt;
&lt;br /&gt;
It&#39;s no stretch to say that the current computing experience provided across desktops, laptops, tables and smartphones is disjointed at best. What you do on a particular device is still determined more by the OS it is running and the configuration that you have taken the time to (re)implement than the practicalities of the device itself. Try to do you everyday tasks on a portable device (even with a dock) and you&#39;ll quickly find that compatibility between common file formats is spotty, access to&amp;nbsp;corporate networks is difficult, &amp;nbsp;most of the apps you use for work can not be installed on your portable devices, and even synchronising common settings like bookmarks and passwords is tiresome.&lt;br /&gt;
&lt;br /&gt;
The real value of a platform like the Ubuntu phone OS is not so much that it allows you to have one device, but that it allows you to have one configuration. Whether you are on the go or at your desk, everything is consistent: the data you access; the apps you use; the toolbars, icon placements, keyboard shortcuts, templates, favourites and shortcuts you have configured in your apps and the commands you run are the same.&lt;br /&gt;
&lt;br /&gt;
I&#39;m not saying that all your favourite Linux apps will be a pleasure to use on a smartphone interface. Just because you can install LibreOffice, GIMP or Eclipse doesn&#39;t mean you&#39;ll be able to use them without a keyboard, mouse and a large screen. But the issue here is more that it is simply not practical to type up a document, edit some graphics or do some development on a smartphone rather than the fact that the word processing app on your phone is not compatible with your document&#39;s format, that it can not access the network drive that contains the image you want to edit, or that a compiler simply does not exist.&lt;br /&gt;
&lt;br /&gt;
Finally we are getting to a point where the physical limitations of the device you are using become the primary factor in deciding what you want to do on the device. This has been one of the selling points behind Windows 8, although I don&#39;t expect to see Windows 8 (not phone or RT) running on a smartphone any time soon.&lt;br /&gt;
&lt;br /&gt;
For the Ubuntu phone OS to be a roaring success it does need to do a couple of things:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Provide images for current phone hardware. Canonical needs to exploit the legion of fans it has and the phones and tablets they already own to get a grassroots movement started. Some kind of a website where you can register interest in the Ubuntu phone OS and the mobile hardware you already have would be a great start.&lt;/li&gt;
&lt;li&gt;Allow Android apps to run in Ubuntu. Bluestacks proved that it can be done on Windows, and &lt;a href=&quot;http://arstechnica.com/gadgets/2009/05/canonical-developers-aim-to-make-android-apps-run-on-ubuntu/&quot;&gt;Canonical was working on something similar&lt;/a&gt; (although the effort appears to have been abandoned).&lt;/li&gt;
&lt;li&gt;Promote touch friendly apps in the Ubuntu marketplace.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Canonical has a real opportunity with the Ubuntu phone OS to provide a&amp;nbsp;genuinely&amp;nbsp;unified experience, and I for one look forward to seeing it in action.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/3739186020837888143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/3739186020837888143' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/3739186020837888143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/3739186020837888143'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2013/01/ubuntu-phone-first-truly-unified.html' title='Ubuntu phone - the first truly unified platform?'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-8320691093789007834</id><published>2012-12-02T10:21:00.000+11:00</published><updated>2012-12-02T10:23:32.452+11:00</updated><title type='text'>Windows 8 isn&#39;t so bad, but the damage has been done</title><content type='html'>I have used Windows 8 for a couple of weeks now on a desktop PC, and I have to say that I don&#39;t know what all the fuss is about. The start menu has been been replaced with a start screen, but once you get past the fact that it now takes up the whole screen, the functionality is pretty much the same: you organise your apps, search for them, and launch them. The charm bar is a little awkward on a multi-monitor setup, but I only use it to shutdown the PC, so it&#39;s not a big problem. The selection of Windows 8 apps is uninspiring to say the least, but since so many of these apps are just pretty wrappers around web pages that work fine with a large monitor, keyboard and mouse, I don&#39;t really feel like I am missing out on anything.&lt;br /&gt;
&lt;br /&gt;
The real problem for Microsoft is not that Windows 8 is bad at what it does, but that the rise of Apple and Android have dispelled the notion that Microsoft is the PC.&lt;br /&gt;
&lt;br /&gt;
For the majority of people, there was no distinction between the PC and Windows. Macs were in people&#39;s peripheral vision, Linux was unheard of, and the concept of not using Windows on a PC was like the idea of not using a steering wheel in a car.&lt;br /&gt;
&lt;br /&gt;
Then came the iPhone, followed by the rise of the tablets. And suddenly everyday people were breaking the link between Microsoft products and &quot;computing&quot;. Email no longer means Outlook. Web browsing no longer means IE. Word processing no longer means Word. People now realise that they could use a computer in an enjoyable and productive way without relying on Microsoft applications and operating systems. So much so that it gave rise to the idea of &quot;&lt;a href=&quot;http://en.wikipedia.org/wiki/Bring_your_own_device&quot;&gt;bring your own device&lt;/a&gt;&quot;.&lt;br /&gt;
&lt;br /&gt;
Microsoft held the mind share of its users to a large degree because people didn&#39;t realise there was an alternative, or had no reason to switch even if they were aware that alternatives existed. Apple (with Android riding shotgun) not only made people aware of an alternative, but also made people want to switch.&lt;br /&gt;
&lt;br /&gt;
And with that, the Microsoft spell was broken.&lt;br /&gt;
&lt;br /&gt;
The downside to having a software ecosystem so intricately entwined is that once the dominoes start falling, they don&#39;t stop. If you have no need for IE and Office, you have no need for Windows (Windows RT especially). If you have no need for Outlook, you have no need for Windows Server. If you can bring your own device to work, you have no need for a desktop.&lt;br /&gt;
&lt;br /&gt;
I&#39;m not suggesting that Microsoft is going anywhere. But it will have to work hard to reverse the increasing notion that its products are something that you are forced to use, as opposed to the products that you want to use.</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/8320691093789007834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/8320691093789007834' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/8320691093789007834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/8320691093789007834'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2012/12/windows-8-isnt-so-bad-but-damage-has.html' title='Windows 8 isn&#39;t so bad, but the damage has been done'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-4907211206978702396</id><published>2012-10-19T11:22:00.002+11:00</published><updated>2012-10-19T11:24:12.114+11:00</updated><title type='text'>Increasing Ubuntu Wubi disk image</title><content type='html'>If you have tried Ubuntu using &lt;a href=&quot;http://www.ubuntu.com/download/desktop/windows-installer&quot;&gt;Wubi&lt;/a&gt;, you may have noticed that the maximum disk size you can specify is 30GB. &lt;a href=&quot;http://ubuntuforums.org/showthread.php?t=1625371&amp;amp;highlight=resize2fs%20wubi&quot;&gt;There are a number of ways around this limitation&lt;/a&gt;, but the easiest is to resize the disk within Windows using the resize2fs application that is bundled with the Wubi executable.&lt;br /&gt;
&lt;br /&gt;
Open up the &lt;b&gt;wubi.exe&lt;/b&gt; file with &lt;a href=&quot;http://www.7-zip.org/&quot;&gt;7ZIP&lt;/a&gt;, and extract the &lt;b&gt;resize2fs.exe&lt;/b&gt; and &lt;b&gt;cygwin1.dll&lt;/b&gt; files from the&amp;nbsp;&lt;b&gt;bin&lt;/b&gt;&amp;nbsp;directory&amp;nbsp;to a convenient directory. Then run the command&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;resize2fs.exe -f d:\ubuntu\disks\root.disk 70G&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Substitute&amp;nbsp;d:\ubuntu\disks\root.disk with the location of your disk, and change the 70G to the size that you want.</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/4907211206978702396/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/4907211206978702396' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/4907211206978702396'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/4907211206978702396'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2012/10/increasing-ubuntu-wubi-disk-image.html' title='Increasing Ubuntu Wubi disk image'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11126983.post-8356071117610765120</id><published>2012-10-11T17:06:00.004+11:00</published><updated>2012-12-05T09:37:56.174+11:00</updated><title type='text'>Free yourself...</title><content type='html'>&lt;a href=&quot;http://en.wikipedia.org/wiki/John_Gage&quot;&gt;The network is the computer&lt;/a&gt;. It was a visionary&amp;nbsp;statement, but one that is yet to really play out.&lt;br /&gt;
&lt;br /&gt;
If the network is the computer, then the computer has been a slow and unreliable device that can only be used inside metropolitan areas. It wasn&#39;t that long ago that dial up was the standard way to connect to the internet, and even now high speed mobile internet access is spotty at best. You can&#39;t even expect to get a reasonable connections at major inner city hotels. I remember spending 10 minutes trying to establish a VPN and download my emails from a business oriented hotel.&lt;br /&gt;
&lt;br /&gt;
However, things are getting better. 4G is finally offering the kind of bandwidth and latency that you need to run real time network applications. ADSL and cable internet access is now common place, and rollouts of high speed fibre networks are slowly spreading across various countries (unless you happen to live in South Korea, which has led the way for high speed internet access).&lt;br /&gt;
&lt;br /&gt;
The incremental improvements in internet access have been matched by an ever expanding variety of computing devices. It is not uncommon for a household to have a Windows or Mac desktop, an iPad and an Android phone. These devices still tend to fulfill different roles, but what if the network was the computer, and these devices simply became different ways to access the same information?&lt;br /&gt;
&lt;br /&gt;
Of course, with browser based email clients and productivity suites, this is already possible to some extent. But web based apps still have a long way to go to match the functionality and compatibility of the desktop applications they seek to replace.&lt;br /&gt;
&lt;br /&gt;
The holy grail for me has been to have a desktop that I can access anywhere and from any device. In practical terms this means having a remote desktop accessible from a browser.&lt;br /&gt;
&lt;br /&gt;
My requirements were simple. First, the whole setup had to cost less that $500 a year. Since this would be a work expense that could be claimed on tax, this would amount to about $1 per day, which was a comfortable price point. Second, I needed enough computing resources to do my daily work. This meant at least 4GB of memory and enough CPU cycles to run around half a dozen applications&amp;nbsp;simultaneously. Finally, I needed to be able to easily access the desktop through a browser using standard HTML5 technologies.&lt;br /&gt;
&lt;br /&gt;
Surprisingly&amp;nbsp;few VPS (Virtual Private Server) providers could provide the resources I was looking for with the budget I had allowed. I could have maybe come in under budget with Amazon EC2, as long as I shut everything down at the end of the day and was willing to use their spot pricing system. But that seems like a lot of work. Most other VPS providers would charge at least $100 a month for a system with more than 2GB, which seems&amp;nbsp;ridiculous to me given how cheap RAM is these days.&lt;br /&gt;
&lt;br /&gt;
After much frustration I finally found &lt;a href=&quot;http://www.a2hosting.com/refer/37649&quot;&gt;A2 Hosting&lt;/a&gt;. They provided Linux based VPS systems with 4GB of memory for just under $500 a year (as long as you use &lt;a href=&quot;http://www.a2hosting.com/coupon&quot;&gt;their coupon&lt;/a&gt;&amp;nbsp;to get 5% off).&lt;br /&gt;
&lt;br /&gt;
Getting the VPS setup was a breeze. It was pretty much point and click as far as getting the Centos 6 OS up and running, and from there you have root access via SSH.&lt;br /&gt;
&lt;br /&gt;
I installed the Gnome desktop, and then installed VNC using the instructions provided on the &lt;a href=&quot;http://wiki.centos.org/HowTos/VNC-Server&quot;&gt;Centos web site&lt;/a&gt;. Once that was running, I installed &lt;a href=&quot;http://kanaka.github.com/noVNC/&quot;&gt;NoVNC&lt;/a&gt;&amp;nbsp;and set it up as a service using &lt;a href=&quot;http://supervisord.org/&quot;&gt;supervisord&lt;/a&gt;. And that was it - a fully fledged Linux desktop that was accessible via a web browser.&lt;br /&gt;
&lt;br /&gt;
Performance is quite good. The tasks I run on a daily basis are at least as fast as they were on my Lenovo T500 laptop, if not slightly faster. I could have used another 2GB of memory, but unfortunately 4GB is the limit on VPS services provided by A2.&lt;br /&gt;
&lt;br /&gt;
Visually there is some lag when accessing the desktop through VNC. You can &lt;a href=&quot;http://superuser.com/questions/484790/how-to-use-8-bit-mode-with-novnc&quot;&gt;drop the color depth down&lt;/a&gt;&amp;nbsp;to increase the&amp;nbsp;responsiveness over slow connections, but there is a limit to what you can do to reduce latency on a server on the other side of the planet.&lt;br /&gt;
&lt;br /&gt;
I also had to hack up a small Javascript bookmarklet to remove the HTML elements from the standard NoVNC login page in order to get true full screen desktop experience (the code is included below). NoVNC is designed to work with custom pages though, so if you are really keen you can just make your own NoVNC portal.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;javascript:(function(){var overflow=&quot;hidden&quot;; var display=&quot;none&quot;; document.body.style.overflow = overflow; var hideElements = [&quot;noVNC-control-bar&quot;, &quot;noVNC_status_bar&quot;, &quot;noVNC_screen_pad&quot;, &quot;noVNC_logo&quot;]; for (i = 0; i &amp;lt; hideElements.length; ++i) document.getElementById(hideElements[i]).style.display = display;})()&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Unfortunately NoVNC doesn&#39;t work very well on Android devices. I had to install a virtual keyboard in Gnome to get any access to the system, since the native Android keyboard displayed by the NoVNC portal didn&#39;t seem to work.&lt;br /&gt;
&lt;br /&gt;
Thankfully it is possible to connect to the remote desktop using a standard VNC client, and Android / iOS have plenty of VNC clients available. However, because A2 uses a non-standard SSH port, you will need some workaround to tunnel your VNC connection over SSH. The following command will establish a VNC connection to a non-standard SSH port (1234 in this example).&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;VNC_VIA_CMD=&#39;/usr/bin/ssh -p 1234 -f -L &quot;$L&quot;:&quot;$H&quot;:&quot;$R&quot; &quot;$G&quot; sleep 20&#39; vncviewer -via &lt;a href=&quot;mailto:matthew@199.195.116.200&quot;&gt;user@1&lt;/a&gt;23.123.123.123 localhost:2&lt;/b&gt;&lt;br /&gt;
On Android I used ConnectBot to establish a connect and forward port 5902, which allowed me to connect to the remote desktop with RealVNC (which is like $7 from the Play Store).&lt;br /&gt;
&lt;br /&gt;
Overall I&#39;d consider the experiment to be a success. I can log into my desktop from pretty much any device and pick up where I left off. It sounds simple, but being able to do some work at home on your desktop and then pick up &lt;i&gt;exactly&lt;/i&gt; where you left off when you get into work and fire up your laptop is a huge time saver. It has also allowed me to squeeze some more life out of an old desktop PC, because it no longer does anything other than open a web browser or VNC client.&lt;br /&gt;
&lt;br /&gt;
Looking forward, I can see that prices for VPS services will only come down while performance goes up. There is no need to worry about upgrading my own hardware to keep pace, and I have the piece of mind that the hardware is being maintained by dedicated professionals.&lt;br /&gt;
&lt;br /&gt;
So if you are looking to rid yourself of the bond that an OS configuration on a specific device has over you, check out the A2 VPS services. If you are willing to work around a slightly laggy interface (and even that might not be an issue if you live closer to the A2 installation than I do), you can free yourself from the burden of being tied to a specific computer.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;UPDATE&lt;/b&gt;&lt;br /&gt;
I found that &lt;a href=&quot;http://myhosting.com/virtual-server-hosting/custom-vps.aspx&quot;&gt;MyHosting &lt;/a&gt;offers an 8GB Linux VPS for under $500 a year (as long as you use a &lt;a href=&quot;http://www.retailmenot.com/view/myhosting.com&quot;&gt;discount code&lt;/a&gt;). I could really have used that extra 4GB... Maybe next year I will swap.</content><link rel='replies' type='application/atom+xml' href='http://matthewcasperson.blogspot.com/feeds/8356071117610765120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11126983/8356071117610765120' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/8356071117610765120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11126983/posts/default/8356071117610765120'/><link rel='alternate' type='text/html' href='http://matthewcasperson.blogspot.com/2012/10/free-yourself.html' title='Free yourself...'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/06833539405189788748</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry></feed>