<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="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" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-4187587304957081274</atom:id><lastBuildDate>Mon, 19 Oct 2026 00:31:11 +0000</lastBuildDate><category>Programming</category><category>dismantling</category><category>iphone</category><category>linux</category><category>projects</category><category>landfill</category><category>NetbeansPlatform</category><category>CNC</category><category>Nintendo DS</category><category>UGS</category><category>osx</category><category>vim</category><category>3D printing</category><category>MediaTomb</category><category>PS3</category><category>conference</category><category>dsants</category><category>self-hosting</category><category>Attenuator</category><category>Audio</category><category>GLUT</category><category>LoggerDS</category><category>privacy</category><category>restoration</category><category>video</category><category>woodworking</category><title>Will&#39;s Blog</title><description>linux, programming, audio, homebrew, video, open source</description><link>http://blog.willwinder.com/</link><managingEditor>noreply@blogger.com (Will Winder)</managingEditor><generator>Blogger</generator><openSearch:totalResults>74</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-6207832229030197304</guid><pubDate>Wed, 22 Jul 2026 14:33:13 +0000</pubDate><atom:updated>2026-07-22T10:47:57.379-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">linux</category><category domain="http://www.blogger.com/atom/ns#">projects</category><title>Setting up a VLAN</title><description>&lt;p&gt;&amp;nbsp;&lt;/p&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/AVvXsEhK0Ww2USiWdm9gvueZVfDyN8CsAFgSyI5xjpiYZlS_FLUj6JL3i8UoBDPlGxxAiy5v2b2VelRBDyRju9QeThBkdt9CMPlxPCyVMdU4uXEaf0oyh3-uXLzMuk-ofeKSsklDnPVi45bFqMbfL7y_IAmxteEuJRBST9pllNPIH9g0t3EJ1EyiJI482mEX_aZN/s4080/PXL_20260722_144022388.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;3072&quot; data-original-width=&quot;4080&quot; height=&quot;482&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK0Ww2USiWdm9gvueZVfDyN8CsAFgSyI5xjpiYZlS_FLUj6JL3i8UoBDPlGxxAiy5v2b2VelRBDyRju9QeThBkdt9CMPlxPCyVMdU4uXEaf0oyh3-uXLzMuk-ofeKSsklDnPVi45bFqMbfL7y_IAmxteEuJRBST9pllNPIH9g0t3EJ1EyiJI482mEX_aZN/w640-h482/PXL_20260722_144022388.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;What follows is documentation for my home network VLAN configuration. An LLM was used to help with terminology. My focus is generally focused on the intent and sometimes that is misaligned with the common technical terms used to communicate some particular implementation. The machine is great at making that type of translation.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Why do you want a VLAN?&amp;nbsp;&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&amp;nbsp;Unless you&#39;re well versed with layer 2 networking, setting up a VLAN network is quite a tedious, so there had better be a good reason to set this thing up. I have a few reasons:&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Security:&lt;/b&gt;&amp;nbsp;I&#39;m interested in setting up various cheap IOT devices for home automation, but I don&#39;t trust them. Putting them on a separate VLAN minimizes how much damage can be done by a rogue device.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Priority:&lt;/b&gt;&amp;nbsp;I also have IP Cameras generating a lot of traffic. I&#39;d like
 to see how much traffic and have a way to lower its priority.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;More security&lt;/b&gt;: Any employer-managed device goes on a completely isolated network.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Fun:&lt;/b&gt;&amp;nbsp;What can I say, I&#39;ve always wanted to set this up. I worked at a &lt;a href=&quot;http://www.iol.unh.edu&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;networking lab&lt;/a&gt; for years and know how this stuff works in theory and am long overdue to see how it works in practice.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;&amp;nbsp;Internet&lt;/h2&gt;&lt;p&gt;Beginning from the Internet Service Provider (ISP), my public internet is delivered as light through a fiber optic cable. In order to avoid any potential issues with my ISPs troubleshooting flowchart, this entry point is completely standard and uses the provided hardware with minimal configuration: the provided Optical Network Terminal (ONT) to convert light over fiber signals to electricity over copper Ethernet wire, and the provided router with minimal configuration.&lt;/p&gt;&lt;p&gt;&lt;/p&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/AVvXsEgUpiE0UzTsJ_Bj-3BNi9JgDcQcEEaRzQ1Wq7bQvLySxEODEGbOq69N_gW275MbBsyzwA_QOnkNQuYffW95SuLvctCV0VayBjETSii4dghOcj661n-rnS3H7SDb3Q62rqX5enjP-5aNTWP-e8RXKRURvmbqnREjYFmm9iE-8ZIBd6bU1IBpk3zE96PSu4_v/s886/01.%20LAN%20-%20Blog%20-%20ISP.drawio.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;314&quot; data-original-width=&quot;886&quot; height=&quot;195&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUpiE0UzTsJ_Bj-3BNi9JgDcQcEEaRzQ1Wq7bQvLySxEODEGbOq69N_gW275MbBsyzwA_QOnkNQuYffW95SuLvctCV0VayBjETSii4dghOcj661n-rnS3H7SDb3Q62rqX5enjP-5aNTWP-e8RXKRURvmbqnREjYFmm9iE-8ZIBd6bU1IBpk3zE96PSu4_v/w552-h195/01.%20LAN%20-%20Blog%20-%20ISP.drawio.png&quot; width=&quot;552&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;ISP Router&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;This is not a sophisticated router. It provides a Wi-Fi Access Point (AP) and a firewall with some basic security features. I simply disabled Wi-Fi and enabled the firewall to block incoming connections. This simplicity is very intentional. When my ISP goes down I simply patch my network out of the equation and let them fix the problem on their end.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;OPNsense Firewall&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Some background before jumping into this router&#39;s configuration. Since 2006 I&#39;ve been running some variant of &lt;a href=&quot;https://en.wikipedia.org/wiki/DD-WRT&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;DD-WRT&lt;/a&gt;. In 2006 it was probably the original DD-WRT, later on I used &lt;a href=&quot;https://en.wikipedia.org/wiki/Tomato_(firmware)&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Tomato&lt;/a&gt;, and most recently I was running &lt;a href=&quot;https://www.asuswrt-merlin.net/https://www.asuswrt-merlin.net/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Merlin&lt;/a&gt; on an Asus RT-AC68U. Originally I wanted to use these for a bridged routing feature that was not available on consumer routers at the time, later on I appreciated extra monitoring and plugin features. Custom firmware on these devices is not for the faint of heart: there is no support and every update has significant risk of bricking your router.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;In early 2025, mainly for performance reasons, I switched to a mini PC running &lt;a href=&quot;https://en.wikipedia.org/wiki/OPNsense&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;OPNsense&lt;/a&gt;. Finally, during my winter vacation in 2025-2026, &lt;a href=&quot;https://blog.willwinder.com/2026/01/a-cautionary-tale-of-ai-and-vlans.html&quot;&gt;I configured VLANs&lt;/a&gt;.&amp;nbsp;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Configuring the VLAN&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Finally, the good stuff! Setting up a VLAN is a careful balance of designing the logical topology, mapping that design to the physical hardware and cabling, and configuring all of the components correctly.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Logical Topology&lt;/h3&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Like many VLANs, my goal is to partition traffic into broad categories:&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li style=&quot;text-align: left;&quot;&gt;&lt;b&gt;USER&lt;/b&gt;&amp;nbsp;(192.168.10.x) - trusted devices.&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;&lt;b&gt;INFRA&lt;/b&gt;&amp;nbsp;(192.168.20.x) - restricted access for network devices.&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;&lt;b&gt;IOT&lt;/b&gt; (192.168.30.x) - locked down for untrusted IoT devices.&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;&lt;b&gt;CAMERA&lt;/b&gt; (192.168.40.x) - locked down for untrusted IP Cameras and QoS.&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;&lt;b&gt;WORK&lt;/b&gt;&amp;nbsp;(192.168.50.x) - locked down for company managed devices.&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&amp;nbsp;Physical Constraints&lt;/h3&gt;&lt;p style=&quot;text-align: left;&quot;&gt;My network has networking &quot;closets&quot; connected in series. The &lt;b&gt;main &lt;/b&gt;rack is located in the basement, everything that can go here is here. A single Ethernet cable runs to a&amp;nbsp;&lt;b&gt;central &lt;/b&gt;rack&amp;nbsp;on the second floor connected to a managed switch. Finally, this managed switch connects to another manage switch in the &lt;b&gt;annex &lt;/b&gt;rack.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The&amp;nbsp;&lt;b&gt;central&lt;/b&gt;&amp;nbsp;and&amp;nbsp;&lt;b&gt;annex&lt;/b&gt;&amp;nbsp;racks are configured with multiple VLANs, which means I need to use a &lt;a href=&quot;https://en.wikipedia.org/wiki/VLAN_Trunking_Protocol&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;trunking protocol&lt;/a&gt;. It also means I need to carefully configure each port to manage Tagged and Untagged traffic.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Hardware Availability&lt;/h3&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The last piece of the equation before getting into the physical layout and configuration is the available hardware. Each rack has a&amp;nbsp;&lt;a href=&quot;https://en.wikipedia.org/wiki/Network_switch&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;&lt;b&gt;Managed Switch&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;in order to configure traffic running across the trunk cables.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Notes on configuration order&amp;nbsp;&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;One extremely tedious part of this project is doing things in the right 
order. To configure managed switches, you need to connect to 
them, but once they&#39;re configured you need to use a specific port and/or a specific VLAN. This makes it incredibly easy to accidentally lose access to the configuration page. My suggestion: get familiar with the factory reset feature on your switch.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;This
 goes for OPNsense as well, but because it&#39;s also a mini-PC you always 
have the monitor+keyboard fallback for settings recovery.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Hardware Configuration Overview&lt;/b&gt;&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;In a VLAN network, every packet needs to be tagged. This can be done in many ways with varying levels of user convenience. The best way is to assign tags with a managed switch. At a high level, the switch will accept all incoming traffic on that port and insert a VLAN ID for all untagged traffic. Any traffic going in the other direction uses IP routing rules as usual (citation needed).&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;With that in mind, here is my network tree and how I&#39;ve configured each port on each device. Each bullet is an Ethernet cable connected between two devices. Inside the square brackets is how the port is configured on the switch side:&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;main&lt;/b&gt;&amp;nbsp;rack:&amp;nbsp;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li style=&quot;text-align: left;&quot;&gt;ONT/ISP router&lt;/li&gt;&lt;ul&gt;&lt;li style=&quot;text-align: left;&quot;&gt;OPNsense firewall&lt;/li&gt;&lt;ul&gt;&lt;li style=&quot;text-align: left;&quot;&gt;[USER] server&amp;nbsp;&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;managed trunk switch&lt;/li&gt;&lt;ul&gt;&lt;li style=&quot;text-align: left;&quot;&gt;[INFRA] AP (Access VLAN: 10)&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;[WORK] keystone&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;[USER] dumb switch&lt;/li&gt;&lt;ul&gt;&lt;li style=&quot;text-align: left;&quot;&gt;keystone&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;TV&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;li style=&quot;text-align: left;&quot;&gt;Central trunk&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;b&gt;central&lt;/b&gt; rack:&amp;nbsp;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li style=&quot;text-align: left;&quot;&gt;Central Trunk&lt;/li&gt;&lt;ul&gt;&lt;li style=&quot;text-align: left;&quot;&gt;Managed PoE switch&lt;/li&gt;&lt;ul&gt;&lt;li style=&quot;text-align: left;&quot;&gt;[IOT] Ziggbee Radio&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;[CAMERA] Dumb PoE switch&lt;/li&gt;&lt;ul&gt;&lt;li style=&quot;text-align: left;&quot;&gt;Camera 1&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;Camera 2&lt;/li&gt;&lt;/ul&gt;&lt;li style=&quot;text-align: left;&quot;&gt;[INFRA] AP (Access VLAN: 10)&lt;/li&gt;&lt;li style=&quot;text-align: left;&quot;&gt;[INFRA] Annex Trunk&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;&lt;b&gt;annex&lt;/b&gt; rack (not fully configured):&amp;nbsp;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li style=&quot;text-align: left;&quot;&gt;[INFRA] Annex Trunk&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&amp;nbsp;AP (Access VLAN: 10)&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Software Configuration&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&amp;nbsp;This is the part I should have written down in more detail 6 months ago! All of these configurations are made inside&amp;nbsp; OPNsense. To the best of my ability I&#39;ll describe the end-state configurations.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Interfaces&lt;/b&gt;&amp;nbsp;&amp;gt; &lt;b&gt;Assignments&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;This is where you configure the physical interface. For my configuration, I created entries for&amp;nbsp;&lt;b&gt;WAN&lt;/b&gt;,&amp;nbsp;&lt;b&gt;TRUNK&amp;nbsp;&lt;/b&gt;and&amp;nbsp;&lt;b&gt;Legacy&lt;/b&gt;. All VLAN traffic interfaces will be configured to use&amp;nbsp;&lt;b&gt;TRUNK&lt;/b&gt;.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Interfaces&lt;/b&gt;&amp;nbsp;&amp;gt;&amp;nbsp;&lt;b&gt;Devices&lt;/b&gt;&amp;nbsp;&amp;gt;&amp;nbsp;&lt;b&gt;VLAN&lt;/b&gt;&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Create an entry for each of your VLANs. My configuration includes TRUNK_USER, TRUNK_INFRA, TRUNK_IOT, etc.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Services&lt;/b&gt;&amp;nbsp;&amp;gt;&amp;nbsp; &lt;b&gt;ISC DHCPv4 [legacy]&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;I don&#39;t recall seeing the legacy tag before, looks like I need to update my configuration to&amp;nbsp;&lt;b&gt;Kea DHCP&lt;/b&gt;.&lt;b&gt;&amp;nbsp;&lt;/b&gt;My original configuration probably used this in early 2025.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&amp;nbsp;For either approach: enable a DHCP server for all VLANs. OPNsense will route traffic according to the VLAN tags and provide addresses.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;I took this one step further and configured static addresses by MAC address in the DHCP configurations. This was very convenient for the managed switches after factory resets.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Firewall&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&amp;nbsp;Last but not least, you can finally configure the firewall! At this stage, you may treat each VLAN interface as if it were any other interface. OPNsense gives you many advanced features to work with, which ones you use depends entirely on your goals, so I recommend &lt;a href=&quot;https://docs.opnsense.org/firewall.html&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;reading the docs&lt;/a&gt;.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Misc Details &amp;amp; Optimizations&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;b&gt;Cascading Firewalls:&lt;/b&gt;&amp;nbsp;the ISP + OPNsense firewall configuration means that if you do want to allow traffic on some port, you need to configure it twice. Once in each router.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;NAT Reflection:&lt;/b&gt;&amp;nbsp;You might have some services running with a reverse proxy inside your network. This means your local service hosted at &lt;b&gt;https://myservice.myproxy.com&amp;nbsp;&lt;/b&gt;will leave your network before coming right back. Search for &quot;nat reflection&quot; to learn more, I opted for the settings at &lt;b&gt;Services&lt;/b&gt;&amp;nbsp;&amp;gt; &lt;b&gt;Unbound DNS&lt;/b&gt;&amp;nbsp;&amp;gt; &lt;b&gt;Overrides&lt;/b&gt;.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Broadcast Discovery&lt;/b&gt;: there are an endless array of consumer devices (smart speakers, home assistants, tv streaming protocols) that use broadcast protocols for device discovery. This is a big problem if you put all of your devices on a separate VLAN from your user devices because those packets will never reach their intended recipient. There are ways to get them working (look up mDNS), but it&#39;s an endless game of cat and mouse. Take a long look in the mirror and decide if your principles are worth the hassle.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Custom cables&lt;/b&gt;: You need a lot of short cables to avoid creating a big mess in your networking closet. If you aren&#39;t already making your own cables, I&#39;d suggest buying a few hundred feet of cat6, a bag of RJ45s and a crimping tool.&amp;nbsp;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;&amp;nbsp;Next Steps&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;For my network, the next steps are mostly cosmetic. I&#39;d like to get my switches and servers securely mounted to something. Probably a simple &lt;a href=&quot;https://en.wikipedia.org/wiki/DIN_rail&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;DIN rail&lt;/a&gt;&amp;nbsp;with some 3D printed specialty mounts.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Conclusion&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;This was all setup about 6 months ago, since then I haven&#39;t done anything other than updating OPNsense. Overall I couldn&#39;t be happier. Everything works as it should, my network is safe, and it&#39;s still convenient enough to use that you wouldn&#39;t know it&#39;s there.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;</description><link>http://blog.willwinder.com/2026/07/the-missing-vlan-manual.html</link><author>noreply@blogger.com (Will Winder)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK0Ww2USiWdm9gvueZVfDyN8CsAFgSyI5xjpiYZlS_FLUj6JL3i8UoBDPlGxxAiy5v2b2VelRBDyRju9QeThBkdt9CMPlxPCyVMdU4uXEaf0oyh3-uXLzMuk-ofeKSsklDnPVi45bFqMbfL7y_IAmxteEuJRBST9pllNPIH9g0t3EJ1EyiJI482mEX_aZN/s72-w640-h482-c/PXL_20260722_144022388.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-7323049743458988791</guid><pubDate>Thu, 08 Jan 2026 15:44:00 +0000</pubDate><atom:updated>2026-01-09T07:57:23.885-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">self-hosting</category><title>A Cautionary Tale of AI and VLANs</title><description>&lt;p&gt;&amp;nbsp;&lt;/p&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/AVvXsEi3Wd8kYCvvU0ae0pbEX2M4SMgRaA4unSLu-PAyBwPSzdMcghr6CAPNnz5NfyzMwQlqeLGDIAydAfsV5yzUaXy3uYLD9usAqSMAdx9jPvFrawxiMz6ArLnHOYdlQ4Rplp0-qJrUJBd8I6ln1YpUDBctHb0KDTn0YnvWPO8qs7ABsCJSo4zVTpZodMEioq3X/s4080/PXL_20260109_125459120.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;3072&quot; data-original-width=&quot;4080&quot; height=&quot;241&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3Wd8kYCvvU0ae0pbEX2M4SMgRaA4unSLu-PAyBwPSzdMcghr6CAPNnz5NfyzMwQlqeLGDIAydAfsV5yzUaXy3uYLD9usAqSMAdx9jPvFrawxiMz6ArLnHOYdlQ4Rplp0-qJrUJBd8I6ln1YpUDBctHb0KDTn0YnvWPO8qs7ABsCJSo4zVTpZodMEioq3X/s320/PXL_20260109_125459120.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;This turned out to be a cautionary tale. At the beginning I was hoping to find an AI success story that demonstrated the power of how it lets you wade into the deep end of a domain and find success. It turned into a grueling week of troubleshooting rabbit holes that the AI insisted I get to the bottom of. It&#39;s a powerful thing, but it takes a mindset that I&#39;m still trying to figure out.&lt;/p&gt;&lt;p&gt;I have what you might call a home lab and was thinking about adding some smart home devices. After a bit of yak shaving it became clear that I needed to setup a VLAN in order to partition the traffic. While I was at it I&#39;d setup networks for IP cameras and my work computer. Last year I switched over to a proper firewall running OPNsense, so I figured all this would be a nice exercise to get practical experience with things I was generally already familiar with.&lt;br /&gt;&lt;br /&gt;Shortly after firing up ChatGPT I had a high level plan and a better understanding of how to design a network. With a couple managed switches ordered, I got right to work. The AI advice sounded correct and was matched general understanding of Networking and how VLANs should be configured. You probably know this already, but the AI is very reassuring. Every step of the way it let me know how clever I was for asking the right questions. I especially appreciated the way it insisted on designing the architecture ahead of time. It gave me a lot of confidence that I was going to have a solid foundation to my network that would evolve elegantly over time. Unfortunately, the hallucinations started almost immediately.&lt;br /&gt;&lt;br /&gt;The first problems were fairly innocuous. The advice regularly referred to OPNsense settings that don&#39;t exist. These were easy to ignore and I explained them away by saying the information could based on a different version. My human intelligence along with the actual documentation easily filled in the gaps and before I knew it, DHCP was configured and seemed to work. I plugged in a device and it got the correct IP!&lt;br /&gt;&lt;br /&gt;Things went downhill from here as the first major issue presented itself. For some reason my device couldn&#39;t ping the firewall and the firewall couldn&#39;t ping my device. This was a bit puzzling because the link was up and the IP address was assigned in the correct range. Clearly they could communicate at some level. The AI was happy to guide me through the issue and was quite confident that this was definitely a Layer 2 problem in the managed switch. After running through a conveniently generated troubleshooting checklist, the results remained conclusive: what I was looking at was clearly a Layer 3 issue in my firewall.&lt;/p&gt;&lt;p&gt;At this point I was starting to realize that the usefulness of the AI was beginning to approach zero. The prescribed root cause continued to waffle between the Network and Data Link layers. Ultimately, I went off script. The AI had previously recommended I put my switch into &quot;Basic VLAN&quot; mode. With nothing to lose I ventured into &quot;Advanced VLAN&quot; mode. It presented me with a new setting called the PVID table. With some trial and error, I was able to get ICMP and DHCP to work. The AI had previously told me that &quot;Advanced VLAN&quot; mode was notoriously buggy... when pressed on this issue it wasn&#39;t able to back up the claim. Go figure.&amp;nbsp;&lt;/p&gt;&lt;p&gt;With that I had my first &quot;access&quot; port configured, which is just the beginning of the story. I had multiple trunk ports to configure, access points and IP cameras to migrate, and a house full of people asking if I was doing something to the internet. To make a long story short the process continued for several days. By the end of it I had a pretty good idea about what I was doing and how things needed to work.&lt;br /&gt;&lt;br /&gt;Overall, the experience left me extremely nervous about how easy it is for the AI to write code for us. None of the issues I hit were particularly sophisticated, but networks are fairly binary things. They work or they don&#39;t, and that property makes small bugs pretty catastrophic and easy to spot.&lt;/p&gt;&lt;p&gt;So what are the takeaways, I certainly learned a few things:&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;AI probably allowed me to configure this network faster than I could have done it on my own. The high level advice was sound and it helped me wire my brain to think about the problem in the right way.&lt;/li&gt;&lt;li&gt;The hallucinations were present at every step of the way, regardless of whether or not I was able to identify them.&lt;/li&gt;&lt;li&gt;AI is a powerful and dangerous tool that should be used cautiously.&lt;/li&gt;&lt;li&gt;You can&#39;t trust anything the AI tells you.&lt;/li&gt;&lt;li&gt;OPNsense is a nice piece of software.&lt;/li&gt;&lt;/ul&gt;</description><link>http://blog.willwinder.com/2026/01/a-cautionary-tale-of-ai-and-vlans.html</link><author>noreply@blogger.com (Will Winder)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3Wd8kYCvvU0ae0pbEX2M4SMgRaA4unSLu-PAyBwPSzdMcghr6CAPNnz5NfyzMwQlqeLGDIAydAfsV5yzUaXy3uYLD9usAqSMAdx9jPvFrawxiMz6ArLnHOYdlQ4Rplp0-qJrUJBd8I6ln1YpUDBctHb0KDTn0YnvWPO8qs7ABsCJSo4zVTpZodMEioq3X/s72-c/PXL_20260109_125459120.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Boston, MA, USA</georss:featurename><georss:point>42.3555076 -71.0565364</georss:point><georss:box>14.045273763821157 -106.2127864 70.665741436178848 -35.9002864</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-4167323382091229618</guid><pubDate>Wed, 17 Jan 2024 22:11:00 +0000</pubDate><atom:updated>2024-01-19T08:23:12.166-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Programming</category><title>Terminal UI the Easy Way: BubbleTEA</title><description>&lt;p&gt;For the past 18 months I&#39;ve been tinkering with a terminal application that serves as an Algorand node frontend. I had a number of requirements in mind when choosing the technology: a full screen terminal application, works over ssh, extensible to allow it to evolve with different utilities and views of the system over time, be programmed with the Go programming language.&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://github.com/algorand/node-ui/blob/master/images/demo.png?raw=true&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;534&quot; data-original-width=&quot;800&quot; src=&quot;https://github.com/algorand/node-ui/blob/master/images/demo.png?raw=true&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;In the past I&#39;ve created these sorts of programs with bash, but it&#39;s not fun to manage terminal control characters and deal with re-drawing with bash. Using bash has always led my applications to be simplistic, typically never getting beyond simple spinners and progress bars. I knew about ncurses, but didn&#39;t want to lose the portability of bash -- some of these scripts were even POSIX compliant, and honestly I just didn&#39;t want to deal with low level interfaces and C bindings.&lt;br /&gt;&lt;br /&gt;Enter &lt;a href=&quot;https://github.com/charmbracelet/bubbletea&quot;&gt;bubbletea&lt;/a&gt;. The team at &lt;a href=&quot;https://charm.sh/&quot;&gt;Charm&lt;/a&gt; has built the tools I didn&#39;t even know I needed. There is an impressive array of documentation, example programs, and supporting tools. The &quot;getting started&quot; story is quite nice, there are even YouTube videos if that&#39;s your thing. Overall, my experience with these tools has been transformative and I&#39;m enabled to build advanced terminal applications. The sort that I knew were possible, but were previously too daunting to consider.&lt;br /&gt;&lt;br /&gt;One of my favorite things about bubbletea is that it&#39;s only one part of a larger ecosystem of libraries and tools. You can use &lt;a href=&quot;https://github.com/charmbracelet/wish&quot;&gt;wish&lt;/a&gt; to serve the UI over ssh, or &lt;a href=&quot;https://github.com/charmbracelet/glamour&quot;&gt;glamour&lt;/a&gt; to render markdown files in your app, and &lt;a href=&quot;https://github.com/charmbracelet/lipgloss&quot;&gt;lipgloss&lt;/a&gt; is great for formatting text. Then there are other tools that are generally useful on the terminal, like &lt;a href=&quot;https://github.com/charmbracelet/vhs&quot;&gt;vhs&lt;/a&gt; to record demos and &lt;a href=&quot;https://github.com/charmbracelet/wishlist&quot;&gt;wishlist&lt;/a&gt; to manage ssh targets. I have yet to get into &lt;a href=&quot;https://github.com/charmbracelet/charm&quot;&gt;charm&lt;/a&gt;, which seems to be their flagship project.&lt;br /&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Gotchas&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;This isn&#39;t to say everything is perfect. These tools can be used to quickly build a simple program, but there are no training wheels. Once you stray off the established path things quickly get complicated. For example:&lt;br /&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;managing multiple views, and their sizes.&lt;/li&gt;&lt;li&gt;themes across multiple components.&lt;/li&gt;&lt;li&gt;customizing an existing component.&lt;/li&gt;&lt;li&gt;input wizards.&lt;/li&gt;&lt;li&gt;dialogs.&lt;/li&gt;&lt;/ul&gt;&lt;p style=&quot;text-align: left;&quot;&gt;This can all be done, but the framework isn&#39;t designed to handle them. At the core of bubbletea is an event handler and not much more. The examples may lull you into a false sense of security here, because it kind of looks like an application framework. Don&#39;t be fooled.&lt;br /&gt;&lt;br /&gt;Some examples.&lt;br /&gt;&lt;br /&gt;If you have two views that should be displayed side by side and a third that should span the entire width of the terminal, you need to explicitly set all of their sizes. While this sounds pretty obvious, the event system really isn&#39;t conducive for it. Each view is self managed, so there&#39;s no easy way to clamp them to 50% of the width, that&#39;s something you need to build yourself. Not rocket science, but not provided by the event system.&lt;br /&gt;&lt;br /&gt;Themes are entirely managed by the View functions. How they are passed throughout the system is up to you. This becomes especially complicated when using off-the-shelf components, where you need to merge your themes with (if you&#39;re lucky) the theming capabilities of the different libraries. In some examples a global &quot;style&quot; object is passed around. But what to put in that global object... and how to use it. That is for you to decide. If you don&#39;t make a decision, you&#39;re gonna have a bad time (or a funky looking app).&lt;br /&gt;&lt;br /&gt;Until recently, a table wasn&#39;t available out of the box, which was a surprising omission. Thankfully there are several 3rd party alternatives. But every bubble library has a slightly different take on how it should be used. Sometimes this leads to some quirky issues. The list bubble is designed to be used as a single page application, and does things like handle the escape key to quit the program. In one of my programs I had a list as part of a wizard and wanted escape to go back. Fortunately there is a &quot;&lt;b&gt;DisableQuitKeybindings&lt;/b&gt;&quot; option, but figuring that out involved a frustrating debugging session.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Takeaway&lt;/h2&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li style=&quot;text-align: left;&quot;&gt;These are not problems, they&#39;re a misunderstanding. I thought I was using a framework but that&#39;s not the case. Bubbletea is an event system with some very convenient terminal events built in. There are a number of thoughtfully designed supporting libraries that turn it into a TUI toolkit. It&#39;s a smart decision that fits well with the go ethos.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The light touch used throughout also leaves the door open for some excellent community extensions like &lt;a href=&quot;https://github.com/lrstanley/bubblezone&quot;&gt;bubblezone&lt;/a&gt; and &lt;a href=&quot;https://github.com/charmbracelet/bubbles#additional-bubbles&quot;&gt;many others&lt;/a&gt;. You should use these libraries, and your own, when building applications of any size.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;The developer is responsible for knowing the exact size of each view. Take a look at how vertical and horizontal margins are passed in library code and figure out how you want to something similar in your program. My most complex program ended up having global variables that keep track of footer and header sizes to deal with the fact that I didn&#39;t have margins plumbed into every view. It&#39;s a glaring design flaw that leads to weird display issues when I modify the program. Margins should be carefully handed down to better fit with the Update function. There&#39;s room in the ecosystem for a layout-aware WindowSize event library.&lt;/li&gt;&lt;/ol&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;# What&#39;s Next&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;I build that layout-aware WindowSize library and will talk about that in a future blog post.&lt;br /&gt;&lt;br /&gt;For now &lt;a href=&quot;https://github.com/winder/bubblelayout#readme&quot;&gt;take a look at the README&lt;/a&gt; and enjoy building terminal applications!&lt;/p&gt;</description><link>http://blog.willwinder.com/2024/01/terminal-ui-easy-way-bubbletea.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-3590171167572569289</guid><pubDate>Sat, 11 Jun 2022 17:13:00 +0000</pubDate><atom:updated>2022-06-12T15:39:20.216-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">privacy</category><category domain="http://www.blogger.com/atom/ns#">self-hosting</category><title>Decommissioning the cloud: A self-hosted future</title><description>&lt;p&gt;Cloud technology has slowly crept its way into nearly every aspect of our digital lives. And why not? It&#39;s convenient and easy to use, nothing to install or configure, and it&#39;s often free. Email clients are a great example of this. People used to live out of &lt;b&gt;Microsoft Outlook&lt;/b&gt; or &lt;b&gt;Lotus Notes&lt;/b&gt;, today all you need is a web browser.&lt;/p&gt;&lt;p&gt;This starts becoming a problem when you consider your privacy. By using these services, especially the ones that don&#39;t cost money, the price is your private information. Consider your family photos and facial recognition, are you comfortable with Google knowing exactly who you associate with? What about your private documents? Even passwords are moving to the cloud with services like &lt;b&gt;LastPass&lt;/b&gt; and &lt;b&gt;1Password&lt;/b&gt;!&lt;/p&gt;&lt;p&gt;Another consideration is whether the service will exist in the future. If you keep notes on the cloud, how upset would you be if that service was shutdown? If you don&#39;t run it yourself you&#39;re beholden to the company providing the service. &lt;br /&gt;&lt;/p&gt;&lt;p&gt;Whether or not you&#39;re happy with all of this is a personal decision. What we&#39;re talking about is how to balance convenience against privacy and ownership. For my preferences, the pendulum has swung a bit farther than I want it to towards the cloud.&lt;br /&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;The best of both worlds.&lt;/h2&gt;&lt;p&gt;How do you get the convenience of cloud-like services and the privacy and ownership of local storage? This, of course, is the self-hosted future. The cost of that self-hosted future is time and money. Time to install and configure different pieces of software, and money for the hardware that runs it.&lt;/p&gt;&lt;p&gt;The hardware side is easier today than it has ever been. Off the shelf hardware products like &lt;b&gt;Synology&lt;/b&gt;, &lt;b&gt;QNAP&lt;/b&gt; and &lt;b&gt;Asustor&lt;/b&gt; are plug and play. Commodity hardware can also be configured with commercial software (&lt;b&gt;Unraid&lt;/b&gt;)&lt;b&gt; &lt;/b&gt;or open source software (&lt;b&gt;TrueNAS, Rockstor&lt;/b&gt;). Any of these can be setup and configured with limited technical knowledge. This is not an exhaustive list, just an example to show that there are more options today than there have ever been in the past.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I&#39;ll give a few examples of cloud services which can be self-hosted.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Data management&lt;/h3&gt;&lt;p style=&quot;text-align: left;&quot;&gt;After setting up a NAS, this is generally provided automatically. Enable one of the many filesharing options like &lt;b&gt;FTP, Samba, WebDAV, &lt;/b&gt;or &lt;b&gt;NFS&lt;/b&gt;. For the less techie &quot;cloud&quot; experience, you can use products like &lt;b&gt;NextCloud, ownCloud &lt;/b&gt;or &lt;b&gt;Seafile&lt;/b&gt;. Any one of these many options let you own your data.&lt;br /&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Password management&lt;/h3&gt;&lt;p style=&quot;text-align: left;&quot;&gt;This is perhaps the best example of when you might want to own your data. Every cloud based password manager is going to go into a lot of detail explaining their security model, and for obvious reasons. But do you trust them? Even if the answer is yes, do you want to place your trust a business?&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;One company leads the pack for self-hosted password management: &lt;b&gt;BitWarden&lt;/b&gt;. A user of this self hosted software would feel right at home coming from any of the other password managers. It has mobile integration and browser integrations that match the competition.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Personal notes&lt;/h3&gt;&lt;p&gt;This can be as easy or advanced as you want them. Simple files on your disk can get you pretty far, or even simple programs like &lt;a href=&quot;https://wiki.gnome.org/Apps/Tomboy/Features&quot;&gt;Tomboy&lt;/a&gt; can manage your work notes. But that really doesn&#39;t compare to a cross-device cloud native note program like &lt;b&gt;Evernote&lt;/b&gt;, &lt;b&gt;Google Keep&lt;/b&gt;, or my favorite of the lot &lt;b&gt;Apple Notes&lt;/b&gt;.&lt;/p&gt;&lt;p&gt;Today there are multiple self-hosted alternatives to these which give you cross-device access to your notes. One of the most popular open source options which provides mobile and desktop applications is &lt;b&gt;Joplin&lt;/b&gt;. For wiki style notes, there are many other options available as well.&lt;br /&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Other stuff&lt;/h3&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The list can go on and on as you start to decommission the cloud. Services like Spotify and Netflix can be replaced with &lt;b&gt;Plex&lt;/b&gt; to serve your DVD and MP3 collections. Social media platforms can be replaced &lt;b&gt;Mastadon&lt;/b&gt;. &lt;a href=&quot;https://github.com/awesome-selfhosted/awesome-selfhosted&quot;&gt;And much much more.&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;The penalty&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;It is costly to self host for many reasons. Not only do you have to buy the hardware, but you may end up investing many hours into configuring and deploying your services. Once deployed you need to consider how to backup the data.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The most important thought I would leave you with is this: you have the option of owning your data and privacy.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Where to get more information&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://www.reddit.com/r/homelab/wiki/index&quot;&gt;/r/homelab&lt;/a&gt; is a reddit community with people building home servers.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://www.reddit.com/r/selfhosted/&quot;&gt;/r/selfhosted&lt;/a&gt; is a reddit community all about software to run on your server.&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://github.com/awesome-selfhosted/awesome-selfhosted#communication&quot;&gt;awesome-selfhosted&lt;/a&gt; is a long list of software that you can host yourself.&lt;br /&gt;&lt;/p&gt;</description><link>http://blog.willwinder.com/2022/06/decomissioning-cloud-self-hosted-future.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-3914837620386023734</guid><pubDate>Fri, 03 Jan 2020 15:29:00 +0000</pubDate><atom:updated>2020-01-03T10:29:25.323-05:00</atom:updated><title>Using a JFileChooser to browse AWS S3</title><description>After a bit of searching I was unable to find an off the shelf solution to let me browse &lt;a href=&quot;https://aws.amazon.com/s3/&quot;&gt;S3&lt;/a&gt; with a &lt;a href=&quot;https://docs.oracle.com/javase/7/docs/api/javax/swing/JFileChooser.html&quot;&gt;JFileChooser&lt;/a&gt;. The closest I found was an &lt;a href=&quot;https://github.com/Upplication/Amazon-S3-FileSystem-NIO2&quot;&gt;S3 FileSystem&lt;/a&gt; implementation, but that doesn&#39;t seem to be used by JFileChooser. Instead you need a custom &lt;a href=&quot;https://docs.oracle.com/javase/7/docs/api/javax/swing/filechooser/FileSystemView.html&quot;&gt;FileSystemView&lt;/a&gt;, which I implemented using &lt;a href=&quot;https://github.com/minio/minio&quot;&gt;minio&lt;/a&gt; as a simple client library.&lt;br /&gt;
&lt;br /&gt;
The code is not incredibly complicated so I wont explain it in detail. You can see that the custom &lt;b&gt;S3FileSystemView&lt;/b&gt; is simply passed to &lt;b&gt;JFileChooser&lt;/b&gt; to insert the S3 logic. The most interesting method is &lt;b&gt;getFiles&lt;/b&gt;, that is where I decided to insert the buckets at the top level, so there is one switch which decides whether to call &lt;b&gt;listBuckets &lt;/b&gt;or&amp;nbsp;&lt;b&gt;listObjects&lt;/b&gt;. From that point my custom VirtualFile objects are carefully constructed so that the File class plays nicely with JFileChooser.&lt;br /&gt;
&lt;br /&gt;
The result from selecting a file is something like &lt;b&gt;s3:/bucket/path/to/file.txt&lt;/b&gt;&amp;nbsp;which you will need to download yourself.&amp;nbsp;&lt;b&gt;VirtualFile&lt;/b&gt;&amp;nbsp;could probably be extended to work like a normal file, but that was beyond my needs.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/winder/73a46de22ccb89a34ab85b46830848e2.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/winder/73a46de22ccb89a34ab85b46830848e2.js&quot;&gt;&lt;/script&gt;</description><link>http://blog.willwinder.com/2020/01/using-jfilechooser-to-browse-aws-s3.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-9031099658514650410</guid><pubDate>Tue, 24 Jul 2018 01:08:00 +0000</pubDate><atom:updated>2018-08-17T15:27:39.114-04:00</atom:updated><title>Novice to Coin Part 2 - Transactions</title><description>&lt;div class=&quot;p1&quot;&gt;
&lt;br /&gt;
&lt;br /&gt;
We left off with an immutable data structure storing arbitrary data, you can read about that in &lt;a href=&quot;https://blog.willwinder.com/2018/07/novice-to-coin-part-1-blockchain-data.html&quot;&gt;part one&lt;/a&gt;, or you can view the introduction to this series &lt;a href=&quot;https://blog.willwinder.com/2018/07/novice-to-coin-introduction.html&quot;&gt;here&lt;/a&gt;. As always,&amp;nbsp;&lt;a href=&quot;https://github.com/winder/LynxCoin&quot; style=&quot;background: rgb(255, 255, 255); color: #2196f3; font-family: roboto, sans-serif; font-size: 15px; outline: 0px;&quot;&gt;the companion code for this post can be found on GitHub.&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
In this post, we&#39;ll introduce transactions which are the primary feature needed to convert that data into a currency. Although a transaction simply needs to subtract coins from one account and add them to another, the way this is implemented in a blockchain is a little unusual. We&#39;ll cover all these details and more during this article before implementing them in the LynxCoin project.&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;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/AVvXsEjFdGbSHFdVlrk4Uy4tj7ENbO8o6n8HAxgur1QaQSLkTDXxsmdLx2e7PfS9lKTFucaTe6-WdxCPXkExyGqiKQyD2-GD8hUufXuTisk4cTSE8dsChQ9FuYX5dlt83zoYEvR5ky6Au6FFLuGH/s1600/Transactions_2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;428&quot; data-original-width=&quot;1069&quot; height=&quot;256&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFdGbSHFdVlrk4Uy4tj7ENbO8o6n8HAxgur1QaQSLkTDXxsmdLx2e7PfS9lKTFucaTe6-WdxCPXkExyGqiKQyD2-GD8hUufXuTisk4cTSE8dsChQ9FuYX5dlt83zoYEvR5ky6Au6FFLuGH/s640/Transactions_2.png&quot; width=&quot;640&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;&lt;b&gt;&lt;span style=&quot;font-size: small;&quot;&gt;This is what we&#39;ll be working towards today.&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;br /&gt;
The first thing you need to understand about currencies stored on a blockchain is that there is no concept of a balance. Instead, you hold rights to transactions. Specifically, if you received a transaction for 50 LynxCoins, you now have the right to spend them.&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot; style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYKIoS9ZSE-RcB78S-HZn0eLkbW9oWS4ruxvMhCFtnSD_7FJXGj5Otr3SZrARg1JInM0QJmOZOYmbiTrAhvTJcXVqtugMpZmAh9yrzeZ0dC1vyACLkz_s5jRthXW-oBMQvhwoJx41-Pm2l/s1600/Transactions_1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;211&quot; data-original-width=&quot;201&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYKIoS9ZSE-RcB78S-HZn0eLkbW9oWS4ruxvMhCFtnSD_7FJXGj5Otr3SZrARg1JInM0QJmOZOYmbiTrAhvTJcXVqtugMpZmAh9yrzeZ0dC1vyACLkz_s5jRthXW-oBMQvhwoJx41-Pm2l/s1600/Transactions_1.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot; style=&quot;text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot; style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;&lt;span style=&quot;font-size: small;&quot;&gt;Our updated block looks like this.&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot; style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
A transaction&amp;nbsp;may have one or more inputs and one or more outputs. During the transaction all coins from each of the inputs will be spent, they are divided up amongst the outputs. Each output can be given to a different individual, who may then use it as an input in one of their future transactions. Let&#39;s take a look at a few examples.&lt;/div&gt;
&lt;h2&gt;
&lt;b&gt;Example Transactions&lt;/b&gt;&lt;/h2&gt;
&lt;div class=&quot;p1&quot;&gt;
Suppose that Alice was given an output in &lt;b&gt;Transaction-A&lt;/b&gt; and received 50 LynxCoins. If she wanted to give those coins to Bob, she would use her output in&amp;nbsp;&lt;b&gt;Transaction-A&lt;/b&gt; as an input while creating a new transaction.&amp;nbsp; Bob would then be given an output to receive coins from that transaction.&lt;/div&gt;
&lt;div class=&quot;p1&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/AVvXsEhPDHHlJwnQ3hrdmwx3S-By-Qp8kSnDHwVkbRW3GjYfyEQ2Z69spXEguAgoVC3jy6f4Q__wSGjCt8kGT3B3gS5JFiYGR7emd9ibPFRObTVSxysCZe5IAcXPS8EF7mArzeLBQh66WvL7Iij0/s1600/Transactions_3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;184&quot; data-original-width=&quot;772&quot; height=&quot;152&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPDHHlJwnQ3hrdmwx3S-By-Qp8kSnDHwVkbRW3GjYfyEQ2Z69spXEguAgoVC3jy6f4Q__wSGjCt8kGT3B3gS5JFiYGR7emd9ibPFRObTVSxysCZe5IAcXPS8EF7mArzeLBQh66WvL7Iij0/s640/Transactions_3.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;p2&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
As long as you want to give away the entire output this works fine, but what if Alice only wanted to give 10 of those coins to Bob? There is actually no way to give away part of a transaction,&amp;nbsp;all of the coins from &lt;b&gt;Transaction-A Output 1&lt;/b&gt;&amp;nbsp;will be spent following the alternative&amp;nbsp;&lt;b&gt;Transaction-B&lt;/b&gt;&amp;nbsp;regardless of whether she gives Bob 10 coins or 50. The way we deal with this is the idea of &quot;change&quot;. Since Bob should only receive 10 coins, Alice will name herself as a recipient for the remaining 40 coins. At this point, Alice&#39;s coins in &lt;b&gt;Transaction-A&lt;/b&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;are considered spent. If Alice needed to give Bob another 15 coins, she could now reference her output from&amp;nbsp;&lt;b&gt;Transaction-B&lt;/b&gt;&amp;nbsp;while creating &lt;b&gt;Transaction-C&lt;/b&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/AVvXsEhjSi3bL6zZ2wtNEEBLe3OoH-LLE6EGO9_XL6TKwzNRCjEe9EniiOWs8bY87L8kstwpiw5PF6WYIw9JJSGY9n4jf5bm_q6jyo1DmD7TchI5440ZiJhyu7RuItBWj1qJJI7JuPp9VHHEUAaQ/s1600/Transactions_4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;458&quot; data-original-width=&quot;773&quot; height=&quot;378&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjSi3bL6zZ2wtNEEBLe3OoH-LLE6EGO9_XL6TKwzNRCjEe9EniiOWs8bY87L8kstwpiw5PF6WYIw9JJSGY9n4jf5bm_q6jyo1DmD7TchI5440ZiJhyu7RuItBWj1qJJI7JuPp9VHHEUAaQ/s640/Transactions_4.png&quot; width=&quot;640&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;p1&quot;&gt;
In one final example, let&#39;s suppose Bob wanted to send 20 coins to Carol. He has 10 coins from the &lt;b&gt;Transaction-B&lt;/b&gt; and 15 coins in &lt;b&gt;Transaction-C&lt;/b&gt;, so he needs to use both of those in order to give Carol all 20 coins. So when making &lt;b&gt;Transaction-D&lt;/b&gt;, Bob specifies &lt;b&gt;Transaction-B &lt;/b&gt;and &lt;b&gt;Transaction-C&lt;/b&gt; as the inputs, for a total of 25. He then names Carol as a recipient for 20 of those coins and himself as a recipient of the change, which is 5 coins.&lt;/div&gt;
&lt;div class=&quot;p1&quot;&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/AVvXsEj7uOydm0GTqcPxYW64R0ES2b7Y9R64Q8DVw8tOsKL7aHm10gKzq0ecLgd0ME5gtPLWCy5Mnc_lt25eJOp8STBQYQuVeBpwtH1MGDZK5wJWgGLqw0aCztgc1k3mtihROa79wimePhKS-Viu/s1600/Transactions_5.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;456&quot; data-original-width=&quot;773&quot; height=&quot;376&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7uOydm0GTqcPxYW64R0ES2b7Y9R64Q8DVw8tOsKL7aHm10gKzq0ecLgd0ME5gtPLWCy5Mnc_lt25eJOp8STBQYQuVeBpwtH1MGDZK5wJWgGLqw0aCztgc1k3mtihROa79wimePhKS-Viu/s640/Transactions_5.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&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;h2&gt;
&lt;b&gt;Where Coins Come From&lt;/b&gt;&lt;/h2&gt;
&lt;div class=&quot;p1&quot;&gt;
The previous examples assume that the coins were already somehow in the system but we haven&#39;t talked about where they come from originally. With the basic knowledge of how a transaction works, we can talk about a special transaction referred to as a &lt;b&gt;Coinbase&lt;/b&gt; which is used to add coins to the system.&lt;/div&gt;
&lt;div class=&quot;p2&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
As we saw in the first diagram, a new block can contain more than one transaction. For most blocks, there will indeed be multiple transactions and the first of these is special. It has no inputs, instead, there is a &lt;b&gt;Reward&lt;/b&gt;&amp;nbsp;built into the system which acts as a default input. Therefore only outputs should be defined to indicate where that reward should go. In this example, we&#39;ll give it to Alice. The &lt;b&gt;Coinbase&lt;/b&gt; transaction provides an incentive for people to create new blocks on the blockchain. It also prevents giving the power to &quot;print&quot; money to a central authority, in contrast, it actually acts as a way to distributes new coins across many different users which further reduces the influence that individual users have over the system.&amp;nbsp;We&#39;ll talk later about why an incentive is needed for creating new blocks.&lt;br /&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_6EExLzXiM_FKV0ozUCFMJqw38btpfKPJ4Ui7uv4etWsXAlEV7wdDraAkKXhlcj2WRU2YtOpDd4ux7JOLTqOEUkej8zhjZLN5PBlIsp_V6dPEFC0OouPg_8RY1ROFBJ-73Z7HA9CpB2qY/s1600/Coinbase+Transaction.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;184&quot; data-original-width=&quot;388&quot; height=&quot;188&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_6EExLzXiM_FKV0ozUCFMJqw38btpfKPJ4Ui7uv4etWsXAlEV7wdDraAkKXhlcj2WRU2YtOpDd4ux7JOLTqOEUkej8zhjZLN5PBlIsp_V6dPEFC0OouPg_8RY1ROFBJ-73Z7HA9CpB2qY/s400/Coinbase+Transaction.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p2&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
These properties are very important to a category of research called &lt;b&gt;Cryptoeconomics&lt;/b&gt;, which studies the economics of a distributed currency like LynxCoin, Bitcoin,&amp;nbsp;and Ethereum. There are many feedback mechanisms like the &lt;b&gt;Coinbase&lt;/b&gt; reward, and they are designed to encourage people for doing the right thing and punish them for doing the wrong thing. Details of &lt;b&gt;Cryptoeconomics&lt;/b&gt; are beyond the scope of this series (For now).&lt;/div&gt;
&lt;h2&gt;
&lt;b&gt;Signing Transactions&lt;/b&gt;&lt;/h2&gt;
&lt;div class=&quot;p1&quot;&gt;
So we have a way to transfer coins between parties, the transactions are immutable thanks to the blockchain, and there is an audit trail that anyone with the blockchain can validate. These are all critical properties of our currency, but how do we ensure that the person named in an Output is the only one who can spend it? It is finally time to introduce some cryptography.&lt;/div&gt;
&lt;div class=&quot;p2&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
We need a way to verify that data came from Alice, Bob, or Carol in a way that nobody else could spoof. Fortunately, we can leverage &lt;b&gt;Public-Key Cryptography&lt;/b&gt; to do just this. Like &lt;b&gt;Cryptoeconomics&lt;/b&gt;, many details about the inner workings of &lt;b&gt;Public-Key Cryptography&lt;/b&gt; will be left outside the scope of this series (For now). At a high level, algorithms like &lt;a href=&quot;https://en.wikipedia.org/wiki/RSA_(cryptosystem)&quot;&gt;RSA&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm&quot;&gt;ECDSA&lt;/a&gt;&amp;nbsp;work by generating &lt;b&gt;Public&lt;/b&gt;&amp;nbsp;and &lt;b&gt;Private&lt;/b&gt;&amp;nbsp;keys. Like their names suggest, the &lt;b&gt;Public&lt;/b&gt;&amp;nbsp;key is used to unlock data generated using the &lt;b&gt;Private&lt;/b&gt;&amp;nbsp;key. The locking mechanism works in two ways.&lt;/div&gt;
&lt;h3&gt;
Encryption and Decryption&lt;/h3&gt;
&lt;div class=&quot;p1&quot;&gt;
Suppose that Alice would like to send a message which can only Bob can read. By using Bob&#39;s &lt;b&gt;Public&lt;/b&gt;&amp;nbsp;key, she can encrypt the data. That encrypted data can then be sent over an unsecured network, now even though Eve can see the message only Bob has the &lt;b&gt;Private&lt;/b&gt;&amp;nbsp;key needed to decrypt the data:&lt;br /&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/AVvXsEi0GcS7GD1SkW3rO4Rd3hZVWTTS-pHvuMsDO2XCOyGJLRIGsAD7ZbKKnC_Fcp6E2eDaV6tZs8jnYCfOerH5UDlhuHu_YBkp9sTV8GdeZuKSpNgVICJQHTsOs9ywQLZeQdjAnf398qWnWzyK/s1600/EncryptDecrypt.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;308&quot; data-original-width=&quot;642&quot; height=&quot;306&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0GcS7GD1SkW3rO4Rd3hZVWTTS-pHvuMsDO2XCOyGJLRIGsAD7ZbKKnC_Fcp6E2eDaV6tZs8jnYCfOerH5UDlhuHu_YBkp9sTV8GdeZuKSpNgVICJQHTsOs9ywQLZeQdjAnf398qWnWzyK/s640/EncryptDecrypt.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h3&gt;
Signing and Verifying&lt;/h3&gt;
&lt;div class=&quot;p1&quot;&gt;
Encryption isn&#39;t useful for our Transactions, because rather than preventing others from seeing our message we need a way for everyone to verify that we created the message. To accomplish this we&#39;ll use a &lt;b&gt;Sign&lt;/b&gt;&amp;nbsp;and &lt;b&gt;Verify&lt;/b&gt;&amp;nbsp;routine with our &lt;b&gt;Public&lt;/b&gt;&amp;nbsp;and &lt;b&gt;Private&lt;/b&gt; keys. The steps are similar, but by &lt;b&gt;Signing&lt;/b&gt;&amp;nbsp;our message with the &lt;b&gt;Private&lt;/b&gt;&amp;nbsp;key, all users of the system can read those messages using the &lt;b&gt;Public&lt;/b&gt;&amp;nbsp;key. Alice&#39;s &lt;b&gt;Public&lt;/b&gt;&amp;nbsp;key is the only way to unlock messages signed by her&amp;nbsp;&lt;b&gt;Private&lt;/b&gt;&amp;nbsp;key, and because only Alice has access to her &lt;b&gt;Private&lt;/b&gt;&amp;nbsp;we can verify that Alice sent the message:&lt;br /&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/AVvXsEh0-kMnI-PZm_nPK78Hjpklzp0GSpeo4gi-4-tSwOuD2xsduauTgMchhBzNo1ltVVTpvBIpNl2oqEiw4xwGuz-FmFdESgtvGA3U8U1I8CYF524QG36igihp9wtr-M2fpJvvwyrqlPZNKEzP/s1600/SignVerify.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;366&quot; data-original-width=&quot;704&quot; height=&quot;332&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0-kMnI-PZm_nPK78Hjpklzp0GSpeo4gi-4-tSwOuD2xsduauTgMchhBzNo1ltVVTpvBIpNl2oqEiw4xwGuz-FmFdESgtvGA3U8U1I8CYF524QG36igihp9wtr-M2fpJvvwyrqlPZNKEzP/s640/SignVerify.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
&lt;h2&gt;
Signing Transactions&lt;/h2&gt;
&lt;/div&gt;
&lt;div class=&quot;p2&quot;&gt;
Using this tool, let&#39;s examine how we can secure the input to one of Alice&#39;s transactions. We now require that Alice has a&amp;nbsp;&lt;b&gt;Public&lt;/b&gt; key, available to everyone who uses the blockchain, and a secret &lt;b&gt;Private&lt;/b&gt;&amp;nbsp;key used to &lt;b&gt;Sign&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
To start, rather than assign an output to Alice, we&#39;ll assign it to her &lt;b&gt;Public&lt;/b&gt;&amp;nbsp;key. In fact, this key is used in the blockchain as the destination when Alice receives a transaction. This can be considered Alice&#39;s &lt;b&gt;Wallet&lt;/b&gt;. We&#39;ll use her &lt;b&gt;Private&lt;/b&gt;&amp;nbsp;key to sign inputs, this signature is added as a new property to the input. While we&#39;re at it, let&#39;s also introduce a unique-id to identify input/output pairs. With this input/output ID we no longer need to store the value in both places, so we will also remove the value from the input. A simple transaction now looks like this:&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/AVvXsEgKbJ_LFN8vP0XSR_6a7f2ZmZUr4LNHOkqjdL_s2gK-mE4PU7rrDhT132wqdTZGShJei3Ipm1PYJUHScA95OsMnhEV0bfjV0LPsd7_UYlM88TN0MjiLJylKD68vowTI5i7wok8luFZsyAGO/s1600/FinalSimpleTransaction.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;182&quot; data-original-width=&quot;823&quot; height=&quot;140&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKbJ_LFN8vP0XSR_6a7f2ZmZUr4LNHOkqjdL_s2gK-mE4PU7rrDhT132wqdTZGShJei3Ipm1PYJUHScA95OsMnhEV0bfjV0LPsd7_UYlM88TN0MjiLJylKD68vowTI5i7wok8luFZsyAGO/s640/FinalSimpleTransaction.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Let&#39;s take a closer look at that signature. As we showed earlier we can sign a message, in this diagram we call that operation &lt;b&gt;sig&lt;/b&gt;&amp;nbsp;and indicate an output to be signed. The message is only half of the equation though, we also need the &lt;b&gt;Private&lt;/b&gt;&amp;nbsp;key. Looking at the input-output pair with id=2 we see that it is assigned to Alice&#39;s &lt;b&gt;Public&lt;/b&gt; key, so the signature must be made with her &lt;b&gt;Private&lt;/b&gt;&amp;nbsp;key. Creating an input which uses output-2 now requires the following steps:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Lookup output 2.&lt;/li&gt;
&lt;li&gt;Serialize output 2 into a message.&lt;/li&gt;
&lt;li&gt;Sign the message using Alice&#39;s &lt;b&gt;Private&lt;/b&gt; key.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Any user of our currency can now verify the input:&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;Lookup the input and output with id=2.&lt;/li&gt;
&lt;li&gt;Serialize output 2 into a message.&lt;/li&gt;
&lt;li&gt;Use Alice&#39;s&amp;nbsp;&lt;b&gt;Public&lt;/b&gt;&amp;nbsp;key from the input to read the signature.&lt;/li&gt;
&lt;li&gt;Verify that the serialized message from step 2 equals the signature from step 3.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
&lt;h2&gt;
&lt;b&gt;Anonymity&lt;/b&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;div class=&quot;p2&quot;&gt;
You may have noticed that the final form of our transactions doesn&#39;t have any personally identifying information. The only thing linking a given transaction to an individual is the &lt;b&gt;Public&lt;/b&gt;&amp;nbsp;key. As it turns out, it is trivial to make new keys, so many &lt;b&gt;Cryptocurrency&lt;/b&gt;&amp;nbsp;users opt to generate a new pair of keys for every transaction. This way if someone were able to link some &lt;b&gt;Public&lt;/b&gt;&amp;nbsp;key to the user, they would only be able to see one of their transactions. It is a very curious property that all transactions are public, yet we still only have part of the story regarding who is making these transactions.&lt;br /&gt;
&lt;br /&gt;
The concept of &lt;b&gt;Zero Knowledge Proof&lt;/b&gt;s may change this one day, but I&#39;m not currently planning to implementing that in &lt;b&gt;LynxCoin&lt;/b&gt;.&lt;br /&gt;
&lt;h2&gt;
Transaction rules&lt;/h2&gt;
&lt;div&gt;
In addition to everything already talked about, there are a number of more obvious rules that our implementation must take into consideration:&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;Double Spending: An output may only be spent once.&lt;/li&gt;
&lt;li&gt;Insufficient Inputs: The sum of inputs must be greater than or equal to the sum of outputs.&lt;/li&gt;
&lt;li&gt;Unique Transaction IDs: There must be no duplicate Transaction IDs.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;h2&gt;
LynxCoin Implementation&lt;/h2&gt;
&lt;div&gt;
Continuing with our implementation of LynxCoin, there are four important pieces to add: Transaction objects, Encryption utilities, Transaction creation, and Block validation. Unlike our blockchain implementation which was only a small handful of code which could be quickly verified by reading and running the code, there are many new edge cases so testing is no longer optional. So we&#39;ll introduce unit testing.&lt;/div&gt;
&lt;h3&gt;
Transaction Object&lt;/h3&gt;
&lt;div&gt;
Kotlin makes this easy, we&#39;ll define a few data classes. Note that the full implementation includes serialize methods, but they are omitted here for clarity.&lt;/div&gt;
&lt;div&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;Input&lt;/span&gt;(
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; id: Long,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; signature: String)
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;Output&lt;/span&gt;(
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; id: Long,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; value: Double,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; destination: PublicKey)
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;Transaction&lt;/span&gt;(
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; inputs: List&amp;lt;Input&amp;gt;,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; outputs: List&amp;lt;Output&amp;gt;
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Integrating these into &lt;b&gt;LynxBlock&lt;/b&gt;&amp;nbsp;is a simple matter:&lt;br /&gt;
&lt;div&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt; data class LynxBlock(
     val lastHash: String,
&lt;span style=&quot;background-color: #ffcccc; border: 1px solid #CC0000;&quot;&gt;-    val data: Any&lt;/span&gt;
&lt;span style=&quot;background-color: #ccffcc; border: 1px solid #00CC00;&quot;&gt;+    val data: List&amp;lt;Transaction&amp;gt;&lt;/span&gt;
 )&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;
Encryption utilities&lt;/h3&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;Java includes&lt;/span&gt;&amp;nbsp;robust implementations for &lt;b&gt;encrypt/decrypt&lt;/b&gt; and &lt;b&gt;sign/verify&lt;/b&gt; routines. The details are a bit cryptic, so we make some simple helper methods to perform the operations according to our needs:&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;sign&lt;/span&gt;(text: String, privateKey: PrivateKey): String {
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; privateSignature = Signature.getInstance(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;SHA256withRSA&quot;&lt;/span&gt;)
  privateSignature.initSign(privateKey)
  privateSignature.update(text.toByteArray(UTF_8))

  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; signature = privateSignature.sign()

  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; Base64.getEncoder().encodeToString(signature)
}

&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;verify&lt;/span&gt;(text: String, signature: String, publicKey: PublicKey): Boolean {
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; publicSignature = Signature.getInstance(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;SHA256withRSA&quot;&lt;/span&gt;)
  publicSignature.initVerify(publicKey)
  publicSignature.update(text.toByteArray(UTF_8))

  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; signatureBytes = Base64.getDecoder().decode(signature)

  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; publicSignature.verify(signatureBytes)
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
If you look closely you&#39;ll notice that the implementation includes a SHA256 hash. It is a common convention to first take a hash of the message and compare hashes rather than fully encrypt/decrypt the message. Mainly because it is much faster to do this than to sign and verify a large message.&lt;br /&gt;
&lt;h3&gt;
Transaction Creation&lt;/h3&gt;
Before we were putting arbitrary data into the blocks, things are a bit more complicated now to deal with our transactions. Here is what the genesis block looks like, it includes a specification for the new &lt;b&gt;Coinbase&lt;/b&gt; transaction:&lt;br /&gt;
&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;background-color: #ffcccc; border: 1px solid #CC0000;&quot;&gt;-  val chain = LynxChain(LynxBlock(&quot;0&quot;, &quot;Genesis block&quot;))&lt;/span&gt;
&lt;span style=&quot;background-color: #ccffcc; border: 1px solid #00CC00;&quot;&gt;+  val key = generateKeyPair()&lt;/span&gt;
&lt;span style=&quot;background-color: #ccffcc; border: 1px solid #00CC00;&quot;&gt;+  val genesisBlock = LynxBlock(&quot;0&quot;,&lt;/span&gt;
&lt;span style=&quot;background-color: #ccffcc; border: 1px solid #00CC00;&quot;&gt;+      listOf(&lt;/span&gt;
&lt;span style=&quot;background-color: #ccffcc; border: 1px solid #00CC00;&quot;&gt;+          // Coinbase transaction, no inputs.&lt;/span&gt;
&lt;span style=&quot;background-color: #ccffcc; border: 1px solid #00CC00;&quot;&gt;+          Transaction(&lt;/span&gt;
&lt;span style=&quot;background-color: #ccffcc; border: 1px solid #00CC00;&quot;&gt;+              listOf(),&lt;/span&gt;
&lt;span style=&quot;background-color: #ccffcc; border: 1px solid #00CC00;&quot;&gt;+              listOf(Output(0, REWARD, key.public)))))&lt;/span&gt;
&lt;span style=&quot;background-color: #ccffcc; border: 1px solid #00CC00;&quot;&gt;+  val chain = LynxChain(genesisBlock)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Here is a larger demonstration of multiple transactions, they now include a signature:&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Create some keys for the demo.&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; key1 = generateKeyPair()
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; key2 = generateKeyPair()
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; key3 = generateKeyPair()

  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Genesis block with coinbase reward for key1.&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; genesisBlock = LynxBlock(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;0&quot;&lt;/span&gt;,
      listOf(
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Coinbase transaction&lt;/span&gt;
          Transaction(
              &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// No inputs.&lt;/span&gt;
              listOf(),
              &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// One output.&lt;/span&gt;
              listOf(Output(&lt;span style=&quot;color: #ff6600;&quot;&gt;0&lt;/span&gt;, REWARD, key1.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;public&lt;/span&gt;)))))
  
  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Create chain with genesis block.&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; chain = LynxChain(genesisBlock)

  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; transaction1 = LynxBlock(chain.last.getHash(),
      listOf(
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Coinbase transaction.&lt;/span&gt;
          Transaction(listOf(), listOf(Output(&lt;span style=&quot;color: #ff6600;&quot;&gt;1&lt;/span&gt;, REWARD, key2.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;public&lt;/span&gt;))),
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Send the initial reward to the owner of key2 and key3.&lt;/span&gt;
          Transaction(
              &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Sign output using key1&#39;s private key.&lt;/span&gt;
              listOf(Input(&lt;span style=&quot;color: #ff6600;&quot;&gt;0&lt;/span&gt;, chain.getSignature(&lt;span style=&quot;color: #ff6600;&quot;&gt;0&lt;/span&gt;, key1.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;private&lt;/span&gt;))),
              &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Create outputs for key2 and key3.&lt;/span&gt;
              listOf(
                  Output(chain.lastId + &lt;span style=&quot;color: #ff6600;&quot;&gt;2&lt;/span&gt;, &lt;span style=&quot;color: #ff6600;&quot;&gt;50.0&lt;/span&gt;, key2.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;public&lt;/span&gt;),
                  Output(chain.lastId + &lt;span style=&quot;color: #ff6600;&quot;&gt;3&lt;/span&gt;, &lt;span style=&quot;color: #ff6600;&quot;&gt;50.0&lt;/span&gt;, key3.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;public&lt;/span&gt;)
              ))))
  chain.add(transaction1)

  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; transaction2 = LynxBlock(chain.last.getHash(),
      listOf(
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Coinbase transaction.&lt;/span&gt;
          Transaction(listOf(), listOf(Output(chain.lastId + &lt;span style=&quot;color: #ff6600;&quot;&gt;1&lt;/span&gt;, REWARD, key2.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;public&lt;/span&gt;))),
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Multiple outputs&lt;/span&gt;
          Transaction(
              &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Use coinbase reward from transaction 1.&lt;/span&gt;
              listOf(Input(&lt;span style=&quot;color: #ff6600;&quot;&gt;1&lt;/span&gt;, chain.getSignature(&lt;span style=&quot;color: #ff6600;&quot;&gt;1&lt;/span&gt;, key2.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;private&lt;/span&gt;))),
              &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Split up output into multiple pieces to show that we can.&lt;/span&gt;
              listOf(
                  Output(chain.lastId + &lt;span style=&quot;color: #ff6600;&quot;&gt;2&lt;/span&gt;, &lt;span style=&quot;color: #ff6600;&quot;&gt;20.0&lt;/span&gt;, key3.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;public&lt;/span&gt;),
                  Output(chain.lastId + &lt;span style=&quot;color: #ff6600;&quot;&gt;3&lt;/span&gt;, &lt;span style=&quot;color: #ff6600;&quot;&gt;20.0&lt;/span&gt;, key3.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;public&lt;/span&gt;),
                  Output(chain.lastId + &lt;span style=&quot;color: #ff6600;&quot;&gt;4&lt;/span&gt;, &lt;span style=&quot;color: #ff6600;&quot;&gt;20.0&lt;/span&gt;, key3.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;public&lt;/span&gt;),
                  Output(chain.lastId + &lt;span style=&quot;color: #ff6600;&quot;&gt;5&lt;/span&gt;, &lt;span style=&quot;color: #ff6600;&quot;&gt;20.0&lt;/span&gt;, key3.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;public&lt;/span&gt;),
                  Output(chain.lastId + &lt;span style=&quot;color: #ff6600;&quot;&gt;6&lt;/span&gt;, &lt;span style=&quot;color: #ff6600;&quot;&gt;20.0&lt;/span&gt;, key3.&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;public&lt;/span&gt;)
              ))))
  chain.add(transaction2)

  chain.print()
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
After reading through the example you&#39;ll notice a couple helper methods: &lt;b&gt;getSignature&lt;/b&gt;&amp;nbsp;and &lt;b&gt;lastId&lt;/b&gt;. These help pull metadata out of the blockchain. &lt;b&gt;lastId&lt;/b&gt;&amp;nbsp;takes the last block and iterates over the&amp;nbsp;outputs, then returns the largest. If there is no last block it simply returns -1. &lt;b&gt;getSignature&lt;/b&gt;&amp;nbsp;uses our encryption utilities to lookup a specific output, and sign it with the provided private key. Note that although the private key is being passed into a helper method, we only store the signature in the transaction.&lt;br /&gt;
&lt;br /&gt;
Here is the code listing for these methods, it uses the Kotlin stream interface to work with data, see inline comments for operation details:&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   * Return the last ID used for an output, or -1 if there are none.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   */&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; lastId &lt;span style=&quot;color: #cc00ff;&quot;&gt;get&lt;/span&gt;() = chain.lastOrNull()
      ?.data               &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// get the transaction list&lt;/span&gt;
      ?.map { it.outputs } &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// get the output list&lt;/span&gt;
      ?.flatten()          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// flatten to output stream&lt;/span&gt;
      ?.map { it.id }      &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// get the IDs&lt;/span&gt;
      ?.max() ?: -&lt;span style=&quot;color: #ff6600;&quot;&gt;1L&lt;/span&gt;       &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// return the largest ID, or -1.&lt;/span&gt;

  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   * Create a signature of the Output.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   */&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;getSignature&lt;/span&gt;(id: Long, key: PrivateKey) : String {
    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Find the given output and serialize it to a string.&lt;/span&gt;
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;sign&lt;/span&gt;(find(id).serialize(), key)
  }

  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   * Lookup a specific transaction, throw an exception if missing.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   */&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;find&lt;/span&gt;(id: Long) : Output {
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; unspentOutputCache[id] &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Cache to speed up most lookups.&lt;/span&gt;
        ?: chain.asSequence()     &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Stop processing when a match is found.&lt;/span&gt;
            .map { it.data }      &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Get transaction list.&lt;/span&gt;
            .flatten()            &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Get transactions.&lt;/span&gt;
            .map { it.outputs }   &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Get output list.&lt;/span&gt;
            .flatten()            &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Get outputs.&lt;/span&gt;
            .find { it.id == id } &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Look for output with given ID.&lt;/span&gt;
        ?: &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;throw&lt;/span&gt; NoSuchElementException(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;Not found: $id&quot;&lt;/span&gt;)
  }
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;br /&gt;
The result from our transactions looks like the following when we print the chain object:&lt;br /&gt;
&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;Size: 3

Block 0
hash: 0cf19c40407e59beb4132284e776f4f8759237ca41de14c8dd1150f89fd129e0
last hash: 0
transactions:
  Transaction: 0 (coinbase)
Disconnected from the target VM, address: &#39;127.0.0.1:54115&#39;, transport: &#39;socket&#39;
  | inputs: 0
  | outputs: 1
  |   tx: 0, value: 100.0, key: 06b80bee761...

Block 1
hash: bfdb9b282322ff4fc630f2c53ae531ead0a4f3353aac2f4f4b751266482591f9
last hash: 0cf19c40407e59beb4132284e776f4f8759237ca41de14c8dd1150f89fd129e0
transactions:
  Transaction: 0 (coinbase)
  | inputs: 0
  | outputs: 1
  |   tx: 1, value: 100.0, key: aa453d1992d...

  Transaction: 1
  | inputs: 1
  |   tx: 0, sig: NQ7IAZhyq9c...
  | outputs: 2
  |   tx: 2, value: 50.0, key: aa453d1992d...
  |   tx: 3, value: 50.0, key: caa0ff040e4...

Block 2
hash: 426ca1b9bf96171dec56d0d0f3c435c810c1145835f96ec1797fd16d1c6ecf30
last hash: bfdb9b282322ff4fc630f2c53ae531ead0a4f3353aac2f4f4b751266482591f9
transactions:
  Transaction: 0 (coinbase)
  | inputs: 0
  | outputs: 1
  |   tx: 4, value: 100.0, key: aa453d1992d...

  Transaction: 1
  | inputs: 1
  |   tx: 1, sig: eVOcNO4ozmV...
  | outputs: 5
  |   tx: 5, value: 20.0, key: caa0ff040e4...
  |   tx: 6, value: 20.0, key: caa0ff040e4...
  |   tx: 7, value: 20.0, key: caa0ff040e4...
  |   tx: 8, value: 20.0, key: caa0ff040e4...
  |   tx: 9, value: 20.0, key: caa0ff040e4...
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;
Block Validation&lt;/h3&gt;
Although validation is significantly more complex than before, it can be encapsulated as a single method which gets called right before adding a new block. One detail to notice is that the &lt;b&gt;unspentOutputCache&lt;/b&gt;&amp;nbsp;which was used by our &lt;b&gt;find&lt;/b&gt;&amp;nbsp;method is updated each time a block is added.&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   * Add and validate a new block.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   */&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;add&lt;/span&gt;(block: LynxBlock) {
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; state = validate(block)
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;when&lt;/span&gt; (state) {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;is&lt;/span&gt; Valid -&amp;gt; chain.add(block)
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;else&lt;/span&gt;      -&amp;gt; &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;throw&lt;/span&gt; state
    }

    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;this&lt;/span&gt;.unspentOutputCache = unspentOutputs()
  }
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
To help organize the code errors are coded into a sealed class, the return type for &lt;b&gt;validate&lt;/b&gt;&amp;nbsp;is &lt;b&gt;ValidationState&lt;/b&gt;&amp;nbsp;which may either be &lt;b&gt;Valid&lt;/b&gt; or one of the error states. This isn&#39;t strictly necessary but may come in handy later on as we build out our coin.&lt;br /&gt;
&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Validation types&lt;/span&gt;
&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;sealed&lt;/span&gt; &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #00aa88; font-weight: bold;&quot;&gt;ValidationState&lt;/span&gt;: RuntimeException()
object Valid: ValidationState()
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;InvalidLastHash&lt;/span&gt;(&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; expected: String, &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; actual: String) : ValidationState()
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;InsufficientInputValue&lt;/span&gt;(&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; transactions: List&amp;lt;Transaction&amp;gt;) : ValidationState()
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;TransactionDoubleSpend&lt;/span&gt;(&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; transactionIds: List&amp;lt;Long&amp;gt;) : ValidationState()
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;PreviouslySpentTransactions&lt;/span&gt;(&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; transactionIds: List&amp;lt;Long&amp;gt;) : ValidationState()
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;CoinbaseRewardTooLarge&lt;/span&gt;(&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; rewardSize: Double) : ValidationState()
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;InvalidTransactionIds&lt;/span&gt;(&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; transactionIds: List&amp;lt;Long&amp;gt;) : ValidationState()
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;BadSignatures&lt;/span&gt;(&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; inputs: List&amp;lt;Input&amp;gt;) : ValidationState()
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
It may be interesting to read the following code, but more important is that you understand the different cases being validated. All of the cases have been described above. The code is organized with private methods performing each of the validation algorithms at the top, and a simple check which leverages these methods to generate an error when necessary.&lt;br /&gt;
&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   * Logic for validating the content of new block.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   */&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;validate&lt;/span&gt;(block: LynxBlock) : ValidationState {
    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     * Last hash must equal previous block&#39;s hash.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     */&lt;/span&gt;
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;badLastHash&lt;/span&gt;() = chain.isNotEmpty() &amp;amp;&amp;amp; (chain.last().getHash() != block.lastHash)

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     * Find inputs referencing spent transaction-ids.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     */&lt;/span&gt;
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;previouslySpentTransactions&lt;/span&gt;() : List&amp;lt;Input&amp;gt; {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; block.data
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Get all the input transactions.&lt;/span&gt;
          .map {it.inputs }.flatten()
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Get inputs which are NOT identified in the unspent cache.&lt;/span&gt;
          .filter { !unspentOutputCache.containsKey(it.id) }
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     * Look for duplicate transactionIds in the current transaction list.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     */&lt;/span&gt;
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;doubleSpendsInCurrentTransaction&lt;/span&gt;() : Map&amp;lt;Long,Int&amp;gt; {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; block.data
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Get all the input transactions.&lt;/span&gt;
          .map { it.inputs }.flatten()
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Group them by transaction ID, count occurrences.&lt;/span&gt;
          .groupingBy{ it.id }.eachCount()
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Filter out the valid cases (spent 0 or 1 times).&lt;/span&gt;
          .filter { it.value &amp;gt; &lt;span style=&quot;color: #ff6600;&quot;&gt;1&lt;/span&gt; }
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     * Make sure the sum of Inputs is &amp;gt;= the sum of Outputs&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     */&lt;/span&gt;
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;inputsGreaterThanOutputs&lt;/span&gt;() : List&amp;lt;Transaction&amp;gt; {
      &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Lookup inputs and tally their values.&lt;/span&gt;
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;getInputsValue&lt;/span&gt;(transaction: Transaction) : Double =
          transaction.inputs
              .map { &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;this&lt;/span&gt;.unspentOutputCache[it.id]?.value ?: &lt;span style=&quot;color: #ff6600;&quot;&gt;0.0&lt;/span&gt; }
              .sum()

      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; block.data
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// skip coinbase transaction&lt;/span&gt;
          .drop(&lt;span style=&quot;color: #ff6600;&quot;&gt;1&lt;/span&gt;)
          .filter { getInputsValue(it) &amp;lt; it.value() }
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     * Compute the coinbase transaction size.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     */&lt;/span&gt;
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;coinbaseTransactionSize&lt;/span&gt;() : Double {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; block.data[&lt;span style=&quot;color: #ff6600;&quot;&gt;0&lt;/span&gt;].outputs.map { it.value }.sum()
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     * Transaction IDs should all be greater than the largest ID in the last block,&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     * and unique in the current transaction list.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     */&lt;/span&gt;
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;invalidTransactionIds&lt;/span&gt;() : Map&amp;lt;Long, Int&amp;gt; {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; block.data
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Get all the input transactions.&lt;/span&gt;
          .map { it.outputs }.flatten()
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Group them by transaction ID, count occurrences.&lt;/span&gt;
          .groupingBy{ it.id }.eachCount()
          &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Look for duplicate transaction IDs.&lt;/span&gt;
          .filter { it.value &amp;gt; &lt;span style=&quot;color: #ff6600;&quot;&gt;1&lt;/span&gt; || it.key &amp;lt;= lastId }
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     * Verify that the signatures match.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;     */&lt;/span&gt;
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;badSignatures&lt;/span&gt;() : List&amp;lt;Input&amp;gt; {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; block.data
          .map { it.inputs }
          .flatten()
          .filter { !verifySignature(it) }
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;//&lt;/span&gt;
    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;//&lt;/span&gt;
    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Do the checks.&lt;/span&gt;
    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;//&lt;/span&gt;
    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;//&lt;/span&gt;

    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;if&lt;/span&gt; (badLastHash()) {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;InvalidLastHash&lt;/span&gt;(chain.last().getHash(), block.lastHash)
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Inputs must not be spent.&lt;/span&gt;
    previouslySpentTransactions().let {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;if&lt;/span&gt; (it.isNotEmpty()) {
        &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;PreviouslySpentTransactions&lt;/span&gt;(it.map { it.id }.toList())
      }
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// A transaction must not be used in multiple Inputs.&lt;/span&gt;
    doubleSpendsInCurrentTransaction().let {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;if&lt;/span&gt; (it.isNotEmpty()) {
        &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;TransactionDoubleSpend&lt;/span&gt;(it.map { it.key }.toList() )
      }
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Transaction inputs &amp;gt; outputs.&lt;/span&gt;
    inputsGreaterThanOutputs().let {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;if&lt;/span&gt; (it.isNotEmpty()) {
        &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;InsufficientInputValue&lt;/span&gt;(it)
      }
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Coinbase reward transaction is not too large.&lt;/span&gt;
    coinbaseTransactionSize().let {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;if&lt;/span&gt; (it &amp;gt; REWARD) {
        &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;CoinbaseRewardTooLarge&lt;/span&gt;(it)
      }
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Validate transaction IDs are unique and increase in size.&lt;/span&gt;
    invalidTransactionIds().let {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;if&lt;/span&gt; (it.isNotEmpty()) {
        &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;InvalidTransactionIds&lt;/span&gt;(it.map { it.key }.toList())
      }
    }

    badSignatures().let {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;if&lt;/span&gt; (it.isNotEmpty()) {
        &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;BadSignatures&lt;/span&gt;(it)
      }
    }

    &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Verify transaction signatures.&lt;/span&gt;
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; Valid
  }&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;
Tests&lt;/h2&gt;
&lt;div&gt;
I won&#39;t be covering the tests. &lt;a href=&quot;https://github.com/winder/LynxCoin/blob/master/kotlin/02_transactions/src/test/kotlin/com/willwinder/lynx/LynxChainTest.kt&quot;&gt;They can be explored in the full source listing on GitHub&lt;/a&gt;. I happen to be using JUnit5, which uses the &lt;b&gt;@Test&lt;/b&gt;&amp;nbsp;annotation preceding each test method. One of the tests also uses &lt;b&gt;@ParameterizedTest&lt;/b&gt;&amp;nbsp;which is a new feature for JUnit5 allowing dynamic test inputs.&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/AVvXsEgIFyoPclkG7erFL_wb4ydG2KTs_pZjcenFatpeAq1E6FWGbe-HlGzXAkACi4oFt8G2bjxIdftswCGnnw8JW2RWJs1nNUAYUB0fvTGZ2Hoteh6MXXZb2MGh3A4dhtJ4ktJfovGWE8TfCSLZ/s1600/tests.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;582&quot; data-original-width=&quot;866&quot; height=&quot;268&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIFyoPclkG7erFL_wb4ydG2KTs_pZjcenFatpeAq1E6FWGbe-HlGzXAkACi4oFt8G2bjxIdftswCGnnw8JW2RWJs1nNUAYUB0fvTGZ2Hoteh6MXXZb2MGh3A4dhtJ4ktJfovGWE8TfCSLZ/s400/tests.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;
Next time&lt;/h2&gt;
&lt;div&gt;
Things are really ramping up now. The blockchain concept was simple, and even though the idea of Transactions is pretty simple as well, as we found, there are a lot of small details that need to be just right. We&#39;ll continue adding on details next time as we start to look into what it takes to make &lt;b&gt;LynxCoin&lt;/b&gt;&amp;nbsp;decentralized. We&#39;ll discuss &lt;b&gt;Mining,&amp;nbsp;&lt;/b&gt;the &lt;b&gt;Byzantine Generals&#39; Problem&lt;/b&gt;&amp;nbsp;and some of the solutions. We&#39;ll implement &lt;b&gt;Proof of Work&lt;/b&gt;&amp;nbsp;and enable some simple endpoints to allow&amp;nbsp;&lt;b&gt;LynxCoin&lt;/b&gt;&amp;nbsp;to go distributed!&lt;/div&gt;
&lt;/div&gt;
</description><link>http://blog.willwinder.com/2018/07/novice-to-coin-part-2-transactions.html</link><author>noreply@blogger.com (Will Winder)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFdGbSHFdVlrk4Uy4tj7ENbO8o6n8HAxgur1QaQSLkTDXxsmdLx2e7PfS9lKTFucaTe6-WdxCPXkExyGqiKQyD2-GD8hUufXuTisk4cTSE8dsChQ9FuYX5dlt83zoYEvR5ky6Au6FFLuGH/s72-c/Transactions_2.png" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-272712518123900468</guid><pubDate>Wed, 11 Jul 2018 20:34:00 +0000</pubDate><atom:updated>2018-08-03T16:10:11.034-04:00</atom:updated><title>Novice to Coin Part 1 - Blockchain Data Structure</title><description>This is the first post in a series building a cryptocurrency from scratch, the series introduction can be found &lt;a href=&quot;https://blog.willwinder.com/2018/07/novice-to-coin-introduction.html&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Every day new developers are learning just how simple blockchain technologies are. One of the first things you may notice is that the collection of algorithms are &lt;a href=&quot;https://blockgeeks.com/guides/what-is-cryptoeconomics/&quot;&gt;profoundly clever&lt;/a&gt;, but the mechanics of creating a verifiably immutable chain of data is borderline trivial. If you need proof, take a look&amp;nbsp;at how many introductory&amp;nbsp;&lt;a href=&quot;https://amarszalek.net/blog/2018/03/20/simple-blockchain-in-kotlin/&quot;&gt;blog&lt;/a&gt; &lt;a href=&quot;https://blockgeeks.com/guides/blockchain-developer/&quot;&gt;posts&lt;/a&gt; &lt;a href=&quot;https://medium.freecodecamp.org/the-authoritative-guide-to-blockchain-development-855ab65b58bc&quot;&gt;are&lt;/a&gt; &lt;a href=&quot;https://hackernoon.com/learn-blockchains-by-building-one-117428612f46&quot;&gt;building&lt;/a&gt; &lt;a href=&quot;https://medium.com/crypto-currently/lets-build-the-tiniest-blockchain-e70965a248b&quot;&gt;a&lt;/a&gt; &lt;a href=&quot;https://medium.com/@lhartikk/a-blockchain-in-200-lines-of-code-963cc1cc0e54&quot;&gt;toy&lt;/a&gt; &lt;a href=&quot;https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa&quot;&gt;blockchain&lt;/a&gt;. These all include working implementations of a blockchain. One good reason to implement your own is to prove to yourself just how trivial the data structure is. Certainly, my first reaction to reading up on &lt;a href=&quot;https://bitcoin.org/en/developer-reference#block-headers&quot;&gt;bitcoins block header&lt;/a&gt; was: is this all?&lt;br /&gt;
&lt;br /&gt;
Of course, the answer is no. Creating a blockchain is table-stakes, and from there a series of clever algorithms allow transactions to occur without a central authority. But this structure is the heart of all that follows, so building a blockchain is a great place to start.&lt;br /&gt;
&lt;br /&gt;
Like you may have guessed, a blockchain contains a series of blocks. The smallest useful block contains an arbitrary amount of data and a special hash used to verify the block that comes before it. In Diagram 1 you can see that our data structure resembles a linked list, except that the blocks do not point to each other. Rather, they verify each other. In addition, you&#39;ll notice that in order to create a new block, the existing blocks do not need to change.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;mxgraph&quot; data-mxgraph=&quot;{&amp;quot;highlight&amp;quot;:&amp;quot;#0000ff&amp;quot;,&amp;quot;nav&amp;quot;:true,&amp;quot;resize&amp;quot;:true,&amp;quot;toolbar&amp;quot;:&amp;quot;zoom layers lightbox&amp;quot;,&amp;quot;edit&amp;quot;:&amp;quot;_blank&amp;quot;,&amp;quot;xml&amp;quot;:&amp;quot;&amp;lt;mxfile userAgent=\&amp;quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\&amp;quot; version=\&amp;quot;8.9.4\&amp;quot; editor=\&amp;quot;www.draw.io\&amp;quot; type=\&amp;quot;google\&amp;quot;&amp;gt;&amp;lt;diagram id=\&amp;quot;88a3cd6e-b202-fa8f-5d7b-3d4d496638fb\&amp;quot; name=\&amp;quot;Page-1\&amp;quot;&amp;gt;7VnLcpswFP0aZtqFM7wNy9hJm0W7yqLtUiDxaARyhfzq11cyEjYIHE9j7CSTyUwsjh5I51zdI9mGMy82XylYZN8JRNiwTbgxnDvDtqeex/8LYFsD3lQCKc1hDVl74DH/iyRoSnSZQ1S1GjJCMMsXbTAmZYli1sIApWTdbpYQ3H7rAqRIAx5jgHX0Rw5ZVqOB7e/xB5SnmXqz5Yd1TQFUY7mSKgOQrA8g595w5pQQVpeKzRxhwZ3ipe73ZaC2mRhFJTulg+0EYZTYThi7CHoITNQaKrZVq6VkWUIkOpiGM8tYgXnR4kW0ydlPCYvyL1G+8cRTyej2p2omHuo6W1T+RoxtpahgyQiHCGUZSUkJ8DdCFrJfPREENS32i5NQRZY0RgMrmsoAATRFbGjVDfU8ZBEpEJ8x70MRBixftd8PZPCkTbs9v7wgKT6V7qlGt1jwo3wsSck/Zm9cAcs9QQL/ahLUY64AXsq3GLaP+TRnMF+1pPH/LMW+nDG0YROA87Q0nFveAqOE7Wt5KZWfu1Ei2kX4FHdDK3RwvwkN1lnO0OMC7Nhd83TajoAkx3hOMKG7vk6SID+OOV4xSp7QQQ2chpFpNu9bIcqXcVxWXQ3VQaWvrcrLMr+t99mwaZMdZMLQHEFARxPQfBmnKQZVJXdVk6CtM1HndKizeqgze6izx6DO1aiDgIGXsddiTAvPxB4ITz/yPX8kjt0TOXbH4DjUOJ5hEj9xSA9TkVk6bHaJ8uMARYlOLQQoSAS1MjHdxZwtxCtngsucH15uZUWRQyhe2CtkW+pGS7OzLTCIEJ6B+CnddVDzkH4VkyKPZUsIqqwZbwRxA/+K+8fSc8+HeTwjoOtq5nHjXdE+LD0JPvCY/aR2qfV5JDfpbmAPBdDtYz+wI8c/U250Pc1/+ti/3A7y3qUFaTS7J9M8iglZ/qAL2f/jQoEp/jrsyux/EQM6t610BQumV9wUulgfrvKMfnbwmq4k0+OWYl7KUtSZcPgUeQ7qw9d0pQnepZ9oHF/1SqO+h+1xE0tj+hQ3UQecoSPRm7SUrmaXvKn4QWi7gR8AG0Ug8SfWoKMkhK+GC6a+ZFeeYR2xj14LEgNN6mGEA1nBYqMPcZeDlIKiDpTGeqIeO6rnNeBHz8WUPImMeDgxT46kw8vw8WvzOWJO/ZIiY64JpsOgc84TdPxx/0PFru7g1x7n/h8=&amp;lt;/diagram&amp;gt;&amp;lt;/mxfile&amp;gt;&amp;quot;}&quot; style=&quot;border: 1px solid transparent; max-width: 100%;&quot;&gt;
&lt;/div&gt;
&lt;script src=&quot;https://www.draw.io/js/viewer.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt; The hash function used here has several important properties. There are two critical properties of a good hash function. First, for any given input to the hash function, the result must always be the same. Meaning if you and I were to hash the string &quot;hello world&quot;, we would both get the same hash. The second critical property is that it is extremely difficult to find two inputs to the hashing function which produce the same output. Bitcoin and many other cryptocurrencies have elected to use the SHA256 hashing function which has these properties as well as several other&#39;s which we will not cover.&lt;br /&gt;
&lt;br /&gt;
Therefore it would be extremely difficult to modify &lt;b&gt;Block-0&lt;/b&gt; in &lt;b&gt;Diagram-1&lt;/b&gt; in such a way that &lt;b&gt;Hash(Block-0)&lt;/b&gt; in &lt;b&gt;Block-1&lt;/b&gt; would stay the same. In addition, if someone wanted to remove &lt;b&gt;Block-1&lt;/b&gt;, the chain would no longer be valid because &lt;b&gt;Block-2&lt;/b&gt; contains &lt;b&gt;Hash(Block-1)&lt;/b&gt;. Because of this, like links in an actual chain, you cannot remove or change one of the blocks. &lt;b&gt;Block-0 &lt;/b&gt;is often referred to as the &lt;b&gt;Genesis Block&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Introducing: LynxCoin&lt;/h2&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/AVvXsEg-g5bWi4ZgT13lphlDF4f-7EoWkROkm5tY9Ca7IVA41LNxd37cfnaKYishI5YhzuQu22VDO-RQcULRxlfPyON-LQ3-0UEdzEtxsBrWbRdCU8HxP76ALUJuW7WLvWsWCnVLVwe0n2yt7_lP/s1600/LynxConstellation.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1138&quot; data-original-width=&quot;1296&quot; height=&quot;280&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-g5bWi4ZgT13lphlDF4f-7EoWkROkm5tY9Ca7IVA41LNxd37cfnaKYishI5YhzuQu22VDO-RQcULRxlfPyON-LQ3-0UEdzEtxsBrWbRdCU8HxP76ALUJuW7WLvWsWCnVLVwe0n2yt7_lP/s320/LynxConstellation.jpg&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;It&#39;s a chain of stars, too much?&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
Our toy implementation will be called LynxCoin, named after the&amp;nbsp;Lynx&amp;nbsp;constellation. Let&#39;s begin with the block object. As we learned it should contain a previous hash and some data. In Kotlin, this can be achieved with a data class containing two variables and a hash function. Notice that the hash joins all of the data together before passing it into the hashing function, this ensures that all data is taken into account before computing the hash.&lt;br /&gt;
&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; blockChain = mutableListOf&amp;lt;ToyBlock&amp;gt;()

&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt; * Add and validate a new block.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt; */&lt;/span&gt;
&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;add&lt;/span&gt;(block: ToyBlock) {
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;if&lt;/span&gt; (blockChain.isNotEmpty()) {
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;if&lt;/span&gt; (blockChain.last().getHash() != block.lastHash) {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; message = &lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;Invalid block: wrong lastHash.&quot;&lt;/span&gt;
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;throw&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;IllegalArgumentException&lt;/span&gt;(message)
    }
  }

  blockChain.add(block)
}

&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt; * Create and add a new block containing some data.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt; */&lt;/span&gt;
&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;add&lt;/span&gt;(data: Any) {
  add(ToyBlock(blockChain.last().getHash(), data))
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Now tie it all together with our main function, and a helper to view the chain:&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;main&lt;/span&gt;(args: Array&amp;lt;String&amp;gt;) {
  add(ToyBlock(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;0&quot;&lt;/span&gt;, &lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;Genesis block&quot;&lt;/span&gt;))
  add(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;More data&quot;&lt;/span&gt;)
  add(LocalDateTime.now())
  printChain()
}

&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt; * Print the blocks.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt; */&lt;/span&gt;
&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;printChain&lt;/span&gt;() {
  print(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;Size: ${blockChain.size}&quot;&lt;/span&gt;)

  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;var&lt;/span&gt; i = &lt;span style=&quot;color: #ff6600;&quot;&gt;0&lt;/span&gt;
  blockChain.forEach {
    println(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;\n&quot;&lt;/span&gt;)
    println(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;Block ${i++}&quot;&lt;/span&gt;)
    println(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;hash: ${it.getHash()}&quot;&lt;/span&gt;)
    println(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;last hash: ${it.lastHash}&quot;&lt;/span&gt;)
    println(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;data: ${it.data}&quot;&lt;/span&gt;)
  }
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Running our program produces the following output:&lt;br /&gt;
&lt;div style=&quot;background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;Size: 3

Block 0
hash: FE81582DB2E60D3933F31981CB920EA74189E9574695C0915401E6B7332FB758
last hash: 0
data: Genesis block

Block 1
hash: 7012C5E83A583F02ADCADAF2A7319CAFE45FBFA8649FE53FAFE9AD444D57AA28
last hash: FE81582DB2E60D3933F31981CB920EA74189E9574695C0915401E6B7332FB758
data: More data

Block 2
hash: BDAE7AA36CCFB6431C8788D0F354935A6E5D77987243FB075AF60EC837B14FE1
last hash: 7012C5E83A583F02ADCADAF2A7319CAFE45FBFA8649FE53FAFE9AD444D57AA28
data: 2018-07-08T15:12:13.268
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Notice how the hash of each block matches the last hash in the following block, the hash for &lt;b&gt;Block-0&lt;/b&gt;&amp;nbsp;starts with FE815, and &lt;b&gt;Block-1&lt;/b&gt;&amp;nbsp;has the same value for last hash. As we continue we&#39;ll add additional data and constraints to these hashes which give us some very interesting results.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/winder/LynxCoin&quot;&gt;The code for this project can be found on GitHub.&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;
Next time...&lt;/h2&gt;
This concludes our introduction to the blockchain data structure. Next time&amp;nbsp;&lt;span style=&quot;background-color: white; color: #333333; font-family: &amp;quot;arial&amp;quot;; font-size: 14px;&quot;&gt;we&#39;ll dive deeper and introduce the concepts of currency and transactions. Once transactions are part of the chain we can also investigate transaction validation. With this, we begin to see the underpinnings that will later allow our currency to be decentralized.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;background-color: white; color: #333333; font-family: &amp;quot;arial&amp;quot;; font-size: 14px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: #333333; font-family: arial;&quot;&gt;&lt;span style=&quot;background-color: white; font-size: 14px;&quot;&gt;&lt;a href=&quot;https://blog.willwinder.com/2018/07/novice-to-coin-part-2-transactions.html&quot;&gt;When you&#39;re ready, the next article is here.&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;background-color: white; color: #333333; font-family: &amp;quot;arial&amp;quot;; font-size: 14px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</description><link>http://blog.willwinder.com/2018/07/novice-to-coin-part-1-blockchain-data.html</link><author>noreply@blogger.com (Will Winder)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-g5bWi4ZgT13lphlDF4f-7EoWkROkm5tY9Ca7IVA41LNxd37cfnaKYishI5YhzuQu22VDO-RQcULRxlfPyON-LQ3-0UEdzEtxsBrWbRdCU8HxP76ALUJuW7WLvWsWCnVLVwe0n2yt7_lP/s72-c/LynxConstellation.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-8062094994885925476</guid><pubDate>Wed, 11 Jul 2018 20:31:00 +0000</pubDate><atom:updated>2018-10-09T16:15:15.685-04:00</atom:updated><title>Novice to Coin Introduction</title><description>&lt;div class=&quot;p1&quot;&gt;
In this series of blog posts, the concepts needed to understand and implement the fundamentals of blockchain and cryptocurrency technologies will be introduced. During each post a new cryptocurrency concept will be introduced, then the technical details will be implemented as a practical demonstration of how they work. The currency will be implemented with Kotlin, which I&#39;ve found to be expressive while maintaining readability. Future iterations of this project may include alternate implementations in other languages.&lt;/div&gt;
&lt;div class=&quot;p2&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
This series will be broken into several parts:&lt;/div&gt;
&lt;ol class=&quot;ol1&quot;&gt;
&lt;li class=&quot;li1&quot;&gt;&lt;a href=&quot;https://blog.willwinder.com/2018/07/novice-to-coin-part-1-blockchain-data.html&quot;&gt;Data Structure&lt;/a&gt;&lt;/li&gt;
&lt;li class=&quot;li1&quot;&gt;&lt;a href=&quot;https://blog.willwinder.com/2018/07/novice-to-coin-part-2-transactions.html&quot;&gt;Currency and Transactions&lt;/a&gt;&lt;/li&gt;
&lt;li class=&quot;li1&quot;&gt;Decentralization and Mining&lt;/li&gt;
&lt;li class=&quot;li1&quot;&gt;Optimization: Merkle Trees&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;p1&quot;&gt;
&lt;b&gt;Data Structure&lt;/b&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
An introduction to the mechanisms that allow us to verify that data in the chain has not been tampered with. We&#39;ll begin with a high-level explanation of how it works and move onto a succinct implementation which will serve as a stepping stone for the following posts.&lt;/div&gt;
&lt;div class=&quot;p2&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
&lt;b&gt;Currency and Transactions&lt;/b&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
Once familiar with the basic construction of a blockchain, we&#39;ll dive deeper and introduce the concepts of currency and transactions. Once transactions are part of the chain we can also investigate transaction validation. With this, we begin to see the underpinnings that will later allow our currency to be decentralized.&lt;/div&gt;
&lt;div class=&quot;p2&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
&lt;b&gt;Decentralization and Mining&lt;/b&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
We&#39;ll discuss some of the concepts that allow&lt;span class=&quot;Apple-converted-space&quot;&gt;&lt;/span&gt; us to create a trustless peer to peer network by introducing the Byzantine Generals Problem and how it is solved by Proof of Work and Proof of Stake. Our toy implementation goes distributed as we create a small web service to submit blocks.&lt;/div&gt;
&lt;div class=&quot;p2&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
&lt;b&gt;Optimization: Merkle Trees&lt;/b&gt;&lt;/div&gt;
&lt;div class=&quot;p1&quot;&gt;
Now that we have a decentralized cryptocurrency we can begin looking into various optimizations to make them more usable. One of the most important ideas involves the usage of Merkle Tree&#39;s, their use allows us to validate transactions in a block without downloading the entire blockchain.&lt;/div&gt;
&lt;style type=&quot;text/css&quot;&gt; p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px &#39;Helvetica Neue&#39;; color: #454545} p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px &#39;Helvetica Neue&#39;; color: #454545; min-height: 14.0px} li.li1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px &#39;Helvetica Neue&#39;; color: #454545} ol.ol1 {list-style-type: decimal} &lt;/style&gt;</description><link>http://blog.willwinder.com/2018/07/novice-to-coin-introduction.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-5704748938199238650</guid><pubDate>Wed, 18 Apr 2018 02:50:00 +0000</pubDate><atom:updated>2018-05-15T11:16:24.933-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Programming</category><title>Building a REST Client with Kotlin and Retrofit2</title><description>&lt;div style=&quot;color: #333333; font-family: Georgia, &amp;quot;Times New Roman&amp;quot;, &amp;quot;Bitstream Charter&amp;quot;, Times, serif; font-size: 16px;&quot;&gt;
If you regularly work with REST endpoints you&#39;re probably used to a lot of tedium. Between carefully constructing the parameters, encoding data in just the right format, efficiently sending the requests, parsing the results and handling errors at every step along the way; there is a lot of room to make mistakes.&lt;/div&gt;
&lt;div style=&quot;color: #333333; font-family: Georgia, &amp;quot;Times New Roman&amp;quot;, &amp;quot;Bitstream Charter&amp;quot;, Times, serif; font-size: 16px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #333333; font-family: Georgia, &amp;quot;Times New Roman&amp;quot;, &amp;quot;Bitstream Charter&amp;quot;, Times, serif; font-size: 16px;&quot;&gt;
For simple APIs,&amp;nbsp;it often makes sense to manually format the request and send it out. Checking a status code, and maybe mapping results to an object&amp;nbsp;aren&#39;t that big a deal for a simple API. But what if there are 20 different endpoints? Or you need to manage authorization headers? Eventually,&amp;nbsp;it makes sense to start looking for a wrapper library, or maybe making your own wrapper layer.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
That&#39;s&amp;nbsp;what this post is about.&lt;br /&gt;
&lt;br /&gt;
For JVM developers, pairing Kotlin, Retrofit2&amp;nbsp;and Gson make it easy to bang out a type-safe&amp;nbsp;client wrapper.&lt;br /&gt;
&lt;br /&gt;
Let us jump right into the code:&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Create data classes for serializing inputs and outputs.&lt;/span&gt;
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #00aa88; font-weight: bold;&quot;&gt;Results&lt;/span&gt;&amp;lt;&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;out&lt;/span&gt; T&amp;gt;(
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; next: String?,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; previous: String?,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; results: List&amp;lt;T&amp;gt;
)

data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;CryptoQuote&lt;/span&gt; (
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; ask_price: Double,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; bid_price: Double,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; mark_price: Double,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; high_price: Double,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; low_price: Double,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; open_price: Double,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; symbol: String,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; id: String,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; volume: Double
)

&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Bounds, Intervals, and Spans are enum types.&lt;/span&gt;
data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;Historical&lt;/span&gt; (
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; data_points: List&amp;lt;DataPoint&amp;gt;,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; bounds: Bounds,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; interval: Intervals,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; span: Spans,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; symbol: String,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; id: String,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; open_price: Double?,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; open_time: Date?,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; previous_close_price: Double?,
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; previous_close_time: Date?
) {
  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Nested data object used by Historical&lt;/span&gt;
  data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;DataPoint&lt;/span&gt;(
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; begins_at: Date,
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; open_price: Double,
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; close_price: Double,
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; high_price: Double,
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; low_price: Double,
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; volume: Double,
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; session: String,
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; interpolated: Boolean
  )
}

&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Define interface using Retrofit annotations and data objects.&lt;/span&gt;
&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;interface&lt;/span&gt; RobinhoodCryptoQuotesApi {
  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   * @param ids comma separated list of ids&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   */&lt;/span&gt;
  @GET(&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;“&lt;/span&gt;marketdata/forex/quotes/&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;”&lt;/span&gt;)
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;getQuoteIds&lt;/span&gt;(
    @Query(&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;“&lt;/span&gt;ids&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;”&lt;/span&gt;) ids: String
  ): Call&amp;lt;Results&amp;lt;CryptoQuote&amp;gt;&amp;gt;

  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   * @param symbols comma separated list of symbols&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   */&lt;/span&gt;
  @GET(&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;“&lt;/span&gt;marketdata/forex/quotes/&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;”&lt;/span&gt;)
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;getQuoteSymbols&lt;/span&gt;(
    @Query(&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;“&lt;/span&gt;symbols&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;”&lt;/span&gt;) symbols: String
  ): Call&amp;lt;Results&amp;lt;CryptoQuote&amp;gt;&amp;gt;

  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;/**&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   * @param symbol id or symbol for a pairing.&lt;/span&gt;
&lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;   */&lt;/span&gt;
  @GET(&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;“&lt;/span&gt;marketdata/forex/quotes/{symbol}/&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;”&lt;/span&gt;)
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;getQuote&lt;/span&gt;(
    @Path(&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;“&lt;/span&gt;symbol&lt;span style=&quot;background-color: #ffaaaa; color: #aa0000;&quot;&gt;”&lt;/span&gt;) symbol: String
  ): Call&amp;lt;CryptoQuote&amp;gt;

  
  @GET(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;marketdata/forex/historicals/{id-pair}/&quot;&lt;/span&gt;)
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;getHistoricals&lt;/span&gt;(
      @Path(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;id-pair&quot;&lt;/span&gt;) idPair: String,
      @Query(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;bounds&quot;&lt;/span&gt;) bounds: Bounds,
      @Query(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;spans&quot;&lt;/span&gt;) spans: Spans,
      @Query(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;interval&quot;&lt;/span&gt;) interval: Intervals
  ): Call&amp;lt;Historical&amp;gt;
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
This is a complete listing of&amp;nbsp;the Robinhood market data endpoints. If you aren&#39;t familiar with Kotlin, this code is essentially declaring an interface and some POJOs. By sprinkling in some special annotations used by Retrofit2, it can be converted into a concrete object for accessing the endpoints. Errors are even handled by the &lt;b&gt;Call&lt;/b&gt;&amp;nbsp;objects which wrap your results.&lt;br /&gt;
&lt;br /&gt;
To go from endpoint definition to concrete object we need to invoke the Retrofit2 builder. While the HttpClient doesn&#39;t really support anything besides OkHttp, there are hooks to adapt the serialization and call adapter layers. Here you can see I configure OkHttp to insert a token header, and add Gson and &lt;a href=&quot;https://github.com/winder/Robinhood4j/blob/master/src/main/kotlin/com/willwinder/robinhood4j/retrofit/EnumRetrofitConverterFactory.java&quot;&gt;Enum&lt;/a&gt; adapters for serialization.&lt;br /&gt;
&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #f0f3f3; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Inject the authentication header.&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;TokenInterceptor&lt;/span&gt;(&lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; token: String) : Interceptor {
    &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;override&lt;/span&gt; &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;fun&lt;/span&gt; &lt;span style=&quot;color: #cc00ff;&quot;&gt;intercept&lt;/span&gt;(chain: Interceptor.Chain): Response {
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; newRequest = chain.request().newBuilder()
          .addHeader(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;, &lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;Token $token&quot;&lt;/span&gt;)
          .build()
      &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;return&lt;/span&gt; chain.proceed(newRequest)
    }
  }

  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Build a token aware HttpClient&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; tokenHttpClient = OkHttpClient.Builder()
      .addInterceptor(TokenInterceptor(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;my-token&quot;&lt;/span&gt;))
      .build()

  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Build the api&lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; api = Retrofit.Builder()
      .addConverterFactory(GsonConverterFactory.create())
      .addConverterFactory(EnumRetrofitConverterFactory())
      .baseUrl(&lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;https://api.robinhood.com/&quot;&lt;/span&gt;)
      .client(tokenHttpClient)
      .build()
      .create(RobinhoodCryptoQuotesApi::class.java)

  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Make a request &lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;val&lt;/span&gt; response = api.getHistoricals(
      idPair = &lt;span style=&quot;color: #cc3300;&quot;&gt;&quot;3d961844-d360-45fc-989b-f6fca761d511&quot;&lt;/span&gt;,
      spans = Spans.DAY,
      bounds = Bounds.TWENTY_FOUR_SEVEN,
      interval = Intervals.DAY
  ).execute()

  &lt;span style=&quot;color: #0099ff; font-style: italic;&quot;&gt;// Print out some data  &lt;/span&gt;
  &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;for&lt;/span&gt;(data &lt;span style=&quot;color: #006699; font-weight: bold;&quot;&gt;in&lt;/span&gt; response!!.body()!!.data_points) {
    println(data.high_price)
  }
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
And that&#39;s all! In the final code, there was some additional work to make an &lt;a href=&quot;https://github.com/winder/Robinhood4j/blob/master/src/main/kotlin/com/willwinder/robinhood4j/ApiClient.kt#L122&quot;&gt;OAuthTokenInterceptor&lt;/a&gt;, but all of that complexity is constrained to the constructor. Defining the endpoints is a matter of typing out the data classes for serialization, naming your methods, and applying the Retrofit2 annotations.&lt;br /&gt;
&lt;br /&gt;
All in all, Retrofit is a very concise way to define a client library in a type-safe way. &lt;a href=&quot;https://github.com/winder/Robinhood4j&quot;&gt;The code described in this article can be found on GitHub.&lt;/a&gt;</description><link>http://blog.willwinder.com/2018/04/building-rest-client-with-kotlin-and.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-7290802781160896419</guid><pubDate>Fri, 01 Sep 2017 21:02:00 +0000</pubDate><atom:updated>2017-09-01T17:02:43.101-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">NetbeansPlatform</category><category domain="http://www.blogger.com/atom/ns#">Programming</category><category domain="http://www.blogger.com/atom/ns#">UGS</category><title>Netbeans Platform: Mac OSX App Bundle and Embedded JRE with Maven</title><description>One problem every Java Desktop developer has to deal with is that Java apps just don&#39;t act like native applications. Whether it&#39;s the JRE dependency, the swing look and feel, or just bad memories about slow performance in old versions of Java. On Mac OSX there is an additional hurdle where users expect their applications to be a .app bundle inside a DMG image with a shortcut to /Application allowing them to drag/drop the installation. The built in Netbeans Platform installer is nice, but it&#39;s a far cry from the native behavior.&lt;br /&gt;
&lt;br /&gt;
With that in mind, I&#39;m going to describe how you can really step up your Netbeans Platform Maven build to include an OSX deployment with a bundled JRE all packaged up as an APP inside a DMG image. What a mouth full.&lt;br /&gt;
&lt;br /&gt;
All of the source code for my implementation in &lt;a href=&quot;https://github.com/winder/Universal-G-Code-Sender/&quot;&gt;Universal Gcode Sender&lt;/a&gt; can be found on &lt;a href=&quot;https://github.com/winder/Universal-G-Code-Sender/commit/7140e25829df699e509df7e67f1aea1dd19c7444&quot;&gt;GitHub in this commit&lt;/a&gt;. The techniques used in this post were heavily inspired (and in many cases copy/pasted from) the &lt;a href=&quot;https://gephi.org/&quot;&gt;Gephi project&lt;/a&gt;, which has a very nice packaging and deployment strategy.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Overview&lt;/h2&gt;
1. Define properties - we&#39;ll expose some custom properties in our maven file in order to make it easier to understand and maintain.&lt;br /&gt;
2. Create and modify resource files.&lt;br /&gt;
3. Resource Filtering - some extra steps need to be taken to apply our properties to configuration files, so we&#39;ll have a filtering step utilizing the maven-resources-plugin.&lt;br /&gt;
4. Build the artifact - using the maven-antrun-plugin we&#39;ll build the Mac OSX bundle.&lt;br /&gt;
5. How to use it - What commands need to be run and from where.&lt;br /&gt;
6. Next steps - integration with CI, app signing, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Define Properties&lt;/h3&gt;
We start things off simple by defining a few properties. Here I&#39;ve prefixed my app&#39;s properties with &quot;ugs.&quot; to avoid any possible naming collisions and put them in my root POM.xml:&lt;br /&gt;
&lt;div style=&quot;background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;ugs.app.title&amp;gt;&lt;/span&gt;Universal Gcode Platform ${project.version}&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/ugs.app.title&amp;gt;&lt;/span&gt;
  
&lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!--==== Mac OS X bundle settings ====--&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;ugs.appbundle.name&amp;gt;&lt;/span&gt;Universal Gcode Platform&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/ugs.appbundle.name&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;ugs.bundle.jre.url&amp;gt;&lt;/span&gt;http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/ugs.bundle.jre.url&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;ugs.bundle.jre.version&amp;gt;&lt;/span&gt;jre-8u131-macosx-x64&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/ugs.bundle.jre.version&amp;gt;&lt;/span&gt;

&lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Mac OS X signing identity - must match with a verified Apple developer certificate in the keychain --&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;ugs.codesign.identity&amp;gt;&lt;/span&gt;Developer ID Application&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/ugs.codesign.identity&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #007700;&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&lt;pre style=&quot;color: black; line-height: 16.25px;&quot;&gt;&lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- You probably already have this property --&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/span&gt;&lt;/pre&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;pre style=&quot;line-height: 16.25px;&quot;&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;brandingToken&amp;gt;&lt;/span&gt;ugsplatform&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/brandingToken&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h3&gt;
Create and Modify Resource Files&lt;/h3&gt;
OSX applications use an &lt;a href=&quot;https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html#//apple_ref/doc/uid/TP40009254-SW1&quot;&gt;information property list&lt;/a&gt; file named &quot;Info.plist&quot; to configure the application, we will need to create such a file using the above properties. There are a number of additional configurations which can be made in this file like associating file extensions with your app, but they won&#39;t be covered here. You can read more about that in &lt;a href=&quot;https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html&quot;&gt;the documentation&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Create this file in your &lt;b&gt;application&lt;/b&gt;&amp;nbsp;module at &lt;b&gt;applicaiton/src/main/resource/Info.plist:&lt;/b&gt;&lt;br /&gt;
&lt;div style=&quot;background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #557799;&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;plist&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;version=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;

        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleName&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;${ugs.appbundle.name}&amp;lt;/string&amp;gt;&lt;/pre&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;    
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleVersion&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;${project.version}&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleExecutable&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;${brandingToken}&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundlePackageType&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;APPL&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleShortVersionString&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;${project.version}&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleSignature&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;????&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleInfoDictionaryVersion&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;6.0&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleIdentifier&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;${project.groupId}&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleIconFile&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;${brandingToken}.icns&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;NSHighResolutionCapable&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt; 
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
    &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
For&amp;nbsp;convenience, you should also rename your&amp;nbsp;&lt;b&gt;launcher.conf&lt;/b&gt;&amp;nbsp;file to your&amp;nbsp;brandingToken&amp;nbsp;and put it in the same directory as the&amp;nbsp;&lt;b&gt;Info.plist&lt;/b&gt;, for me this file is named&amp;nbsp;&lt;b&gt;ugsplatform.conf&lt;/b&gt;. The important part in this file is that it contains the commented out jdkhome&amp;nbsp;property, it will be used later for bundling the JRE. Additional options can be configured with the &quot;default_options&quot; parameter:&lt;br /&gt;
&lt;div style=&quot;background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #888888;&quot;&gt;# \${HOME} will be replaced by user home directory according to platform&lt;/span&gt;
&lt;span style=&quot;color: #0000cc;&quot;&gt;default_userdir&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;\${HOME}/.\${APPNAME}/${project.version}/dev&quot;&lt;/span&gt;
&lt;span style=&quot;color: #0000cc;&quot;&gt;default_mac_userdir&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;\${HOME}/Library/Application Support/\${APPNAME}/${project.version}/dev&quot;&lt;/span&gt;

&lt;span style=&quot;color: #888888;&quot;&gt;# options used by the launcher by default, can be overridden by explicit&lt;/span&gt;
&lt;span style=&quot;color: #888888;&quot;&gt;# command line switches&lt;/span&gt;
&lt;span style=&quot;color: #0000cc;&quot;&gt;default_options&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;--branding ${brandingToken} -J-Xms64m -J-Xverify:none -J-Dsun.java2d.noddraw=true -J-Dsun.awt.noerasebackground=true -J-Dnetbeans.indexing.noFileRefresh=true&quot;&lt;/span&gt;
&lt;span style=&quot;color: #888888;&quot;&gt;# for development purposes you may wish to append: -J-Dnetbeans.logger.console=true -J-ea&lt;/span&gt;

&lt;span style=&quot;color: #888888;&quot;&gt;# default location of JDK/JRE, can be overridden by using --jdkhome &amp;lt;dir&amp;gt; switch&lt;/span&gt;
&lt;span style=&quot;color: #888888;&quot;&gt;#jdkhome=&quot;/path/to/jdk&quot;&lt;/span&gt;

&lt;span style=&quot;color: #888888;&quot;&gt;# clusters&#39; paths separated by path.separator (semicolon on Windows, colon on Unices)&lt;/span&gt;
&lt;span style=&quot;color: #888888;&quot;&gt;#extra_clusters=&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Finally, we also need to create a&amp;nbsp;&lt;b&gt;.icns&lt;/b&gt;&amp;nbsp;icon file, it should have the same name as your brandingToken and be located in the&amp;nbsp;&lt;b&gt;application&amp;nbsp;&lt;/b&gt;module at&amp;nbsp;&lt;b&gt;application/src/main/app-resources/&lt;/b&gt;&lt;br /&gt;
&lt;h3&gt;
Resource Filtering&lt;/h3&gt;
Now that we have our resource files, we need to substitute the properties. This is done using the&amp;nbsp;&lt;b&gt;maven-resources-plugin&lt;/b&gt;. There is nothing especially fancy here, we are setting filtering to true and copying the filtered resources into the target directory:&lt;br /&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Copy ressources ${brandingToken}.conf and Info.plist with filtering --&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-resources-plugin&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
            &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;generate-app-conf-file&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
            &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;generate-resources&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
            &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;copy-resources&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
            &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
            &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;outputDirectory&amp;gt;&lt;/span&gt;${basedir}/target/&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/outputDirectory&amp;gt;&lt;/span&gt;
                &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;resources&amp;gt;&lt;/span&gt;
                    &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;resource&amp;gt;&lt;/span&gt;
                        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;src/main/resources&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
                        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
                            &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;include&amp;gt;&lt;/span&gt;${brandingToken}.conf&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
                            &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;include&amp;gt;&lt;/span&gt;Info.plist&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
                        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
                        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;filtering&amp;gt;&lt;/span&gt;true&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/filtering&amp;gt;&lt;/span&gt;
                    &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/resource&amp;gt;&lt;/span&gt;
                &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/resources&amp;gt;&lt;/span&gt;
                &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;escapeString&amp;gt;&lt;/span&gt;\&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/escapeString&amp;gt;&lt;/span&gt;
            &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
    &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h3&gt;
Build the Artifact&lt;/h3&gt;
This is the big one, we download the JRE, create the package structure, sign the application and package it all up into a DMG image. By utilizing the &lt;b&gt;maven-antrun-plugin&lt;/b&gt;&amp;nbsp;we can do this in a procedural way.&lt;br /&gt;
&lt;br /&gt;
Because we don&#39;t want to create the DMG image for every build, this is created as a profile which can be enabled independently of other features. I&#39;ll briefly describe the sections commented below to explain what is happening, but for further detail you&#39;ll need to carefully read the code.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
Clean&lt;/h4&gt;
&lt;div&gt;
In case the build fails, we need to delete any files which may have been left behind from a previous build. This will prevent accidentally releasing stale files.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
Create folders&lt;/h4&gt;
&lt;div&gt;
A &lt;a href=&quot;https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1&quot;&gt;special file structure&lt;/a&gt; needs to be created in order for the &lt;b&gt;.app&lt;/b&gt;&amp;nbsp;to be used, we start setting up that file structure by creating the folders.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
Copy application&lt;/h4&gt;
&lt;div&gt;
One important piece of the &lt;b&gt;.app&lt;/b&gt;&amp;nbsp;file structure is that the executable is put in a specific location. First we&#39;re copying the executable into the build directory so that we can do further modifications to it in a moment.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
Copy logo and configuration files&lt;/h4&gt;
&lt;div&gt;
Two more important files, copy the&amp;nbsp;&lt;b&gt;${brandingToken}.icns&lt;/b&gt; and &lt;b&gt;Info.plist&lt;/b&gt;&amp;nbsp;files to their specified location.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
Modify application script and copy to final location&lt;/h4&gt;
&lt;div&gt;
We now move the &lt;b&gt;executable&lt;/b&gt;&amp;nbsp;into its final location in the &lt;b&gt;.app&lt;/b&gt;&amp;nbsp;file, which happens to be &lt;b&gt;Contents/MacOS/&lt;/b&gt;. For OSX, the netbeans platform uses a shell script. We need to make a slight modification to this script because the relative location of the resources is different with this new file structure. To that end the &lt;b&gt;replace&lt;/b&gt;&amp;nbsp;command to update the working directory back to the &lt;b&gt;bin&lt;/b&gt;&amp;nbsp;directory so that everything works as it ought to.&lt;/div&gt;
&lt;h4&gt;
Bundling the JRE&lt;/h4&gt;
&lt;div&gt;
To bundle the JRE we first download it and untar the &lt;b&gt;jre&lt;/b&gt;&amp;nbsp;directory into &lt;b&gt;.app/Contents/PlugIns/&lt;/b&gt;, some additional manipulation is needed to make sure everything works smoothly. Like resetting a quarantine bit and updating libjli.dylib&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We also need to set the &lt;b&gt;jdkhome&lt;/b&gt;&amp;nbsp;variable in our &lt;b&gt;.conf&lt;/b&gt;&amp;nbsp;file, this is done with another substitution looking for&amp;nbsp;&lt;b&gt;#jdkhome=&quot;/path/to/jdk&quot;&lt;/b&gt; and replacing it with the relative path to our bundled JRE.&lt;/div&gt;
&lt;h4&gt;
&lt;b&gt;Codesign JRE and App&lt;/b&gt;&lt;/h4&gt;
&lt;div&gt;
This piece is something I haven&#39;t actually gotten to work yet. Presumably if you have your codesign identity configured properly on the build machine it should work as expected.&lt;/div&gt;
&lt;h4&gt;
Create the DMG&lt;/h4&gt;
&lt;div&gt;
Finally we need our DMG file complete with a symling to the &lt;b&gt;/Application&lt;/b&gt;&amp;nbsp;directory. You can take a look at the &lt;b&gt;hdiutil&lt;/b&gt;&amp;nbsp;and &lt;b&gt;genisoimage&lt;/b&gt;&amp;nbsp;commands which build the image on a OSX and Linux machines respectively.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;!-- HTML generated using hilite.me --&gt;&lt;br /&gt;
&lt;div style=&quot;background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;&quot;&gt;
&lt;pre style=&quot;line-height: 125%; margin: 0;&quot;&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;
  &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;create-dmg&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
  &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;skipCreateDmg&amp;gt;&lt;/span&gt;false&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/skipCreateDmg&amp;gt;&lt;/span&gt;
  &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
  &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-antrun-plugin&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
          &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Create the Mac OS X application bundle and dmg file --&amp;gt;&lt;/span&gt;
          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
              &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;create-app-bundle&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
              &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;package&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
              &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                  &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;run&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
              &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
              &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                  &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;skip&amp;gt;&lt;/span&gt;${skipCreateDmg}&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/skip&amp;gt;&lt;/span&gt;
                  &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;target&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Clean --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;delete&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;includeEmptyDirs=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;failonerror=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;removeNotFollowedSymlinks=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;fileset&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;followsymlinks=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;fileset&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.app.title}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;followsymlinks=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;fileset&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${project.artifactId}-${project.version}.dmg&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;followsymlinks=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/delete&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;delete&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${project.artifactId}-${project.version}.dmg&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;failonerror=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Create folders --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;mkdir&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;mkdir&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/MacOS&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Copy application --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;copy&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;todir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/Resources/${brandingToken}&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;fileset&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${brandingToken}&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/copy&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Copy logo and configuration files --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;copy&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;tofile=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/Resources/${brandingToken}.icns&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;src/main/app-resources/${brandingToken}.ic&lt;/span&gt;
&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;ns&quot;&lt;/span&gt; &lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;copy&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;tofile=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/Info.plist&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/Info.plist&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Modify application script and copy to final location --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;move&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/Resources/${brandingToken}/bin/${brandingToken}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;todir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/&lt;/span&gt;
&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;${ugs.appbundle.name}.app/Contents/MacOS&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;replace&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/MacOS/${brandingToken}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;token=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;`dirname &amp;amp;quot;$PRG&amp;amp;quot;`&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;`dirname &amp;amp;quot&lt;/span&gt;
&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;;$PRG&amp;amp;quot;`&amp;amp;quot;/../Resources/${brandingToken}/bin&amp;amp;quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;chmod&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/MacOS/${brandingToken}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;perm=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;ugo+rx&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Download and untar JRE --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;exec&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;executable=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;curl&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-L&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-C&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-b&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;&amp;amp;quot;oraclelicense=accept-securebackup-cookie&amp;amp;quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-O&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${ugs.bundle.jre.url}/${ugs.bundle.jre.version}.tar.gz&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/exec&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Unzip archive --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;mkdir&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/PlugIns&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;exec&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;executable=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;tar&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-zxf&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${ugs.bundle.jre.version}.tar.gz&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-C &amp;amp;quot;${ugs.appbundle.name}.app/Contents/PlugIns&amp;amp;quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/exec&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Remove quarantine bit set recursively on JRE --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;exec&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;os=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;Mac OS X&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;executable=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;xattr&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-rd&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;com.apple.quarantine&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;line=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;&amp;amp;quot;${ugs.appbundle.name}.app/Contents/PlugIns&amp;amp;quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/exec&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Get the JRE folder name --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;id=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;jre_name&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;dirset&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/PlugIns&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;includes=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;jre*&quot;&lt;/span&gt; &lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/path&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;name=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;bundle.jre.path&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;refid=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;jre_name&quot;&lt;/span&gt; &lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;basename&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;property=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;bundle.jre.name&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${bundle.jre.path}&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Configure relative JRE path into ugsplatform.conf --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;replace&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/Resources/${brandingToken}/etc/${brandingToken}.conf&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;token=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;#jdkhome=&amp;amp;quot;/pat&lt;/span&gt;
&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;h/to/jdk&amp;amp;quot;&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;jdkhome=&amp;amp;quot;../../PlugIns/${bundle.jre.name}/Contents/Home&amp;amp;quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;chmod&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/PlugIns/**&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;perm=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;+x&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;type=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;both&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Fix JRE by replacing libjli.dylib symlink with real file --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;delete&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/PlugIns/${bundle.jre.name}/Contents/MacOS/libjli.dylib&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;copy&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app/Contents/PlugIns/${bundle.jre.name}/Contents/Home/lib/jli/libjli.dylib&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;todir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.bui&lt;/span&gt;
&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;ld.directory}/${ugs.appbundle.name}.app/Contents/PlugIns/${bundle.jre.name}/Contents/MacOS&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Codesign JRE --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;exec&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;os=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;Mac OS X&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;executable=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;codesign&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-fs&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${ugs.codesign.identity}&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-v&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${ugs.appbundle.name}.app/Contents/PlugIns/${bundle.jre.name}/&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/exec&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Codesign app --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;exec&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;os=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;Mac OS X&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;executable=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;codesign&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-fs&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${ugs.codesign.identity}&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-v&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${ugs.appbundle.name}.app&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/exec&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Create application folder and add Applications dynamic link --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;mkdir&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.app.title}&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;move&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;file=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.appbundle.name}.app&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;todir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.app.title}/&quot;&lt;/span&gt; &lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;symlink&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;link=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.app.title}/Applications&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;resource=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;/Applications&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;failonerror=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Create DMG (Mac OS X) --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;exec&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;os=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;Mac OS X&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;executable=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;hdiutil&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;create&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-noanyowners&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-imagekey&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;zlib-level=9&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-srcfolder&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.app.title}&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.artifactId}-${project.version}.dmg&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/exec&amp;gt;&lt;/span&gt;

                      &lt;span style=&quot;color: #888888;&quot;&gt;&amp;lt;!-- Create DMG (Linux), only for testing --&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;exec&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;dir=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;os=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;Linux&quot;&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;executable=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;genisoimage&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-V&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${ugs.appbundle.name}&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-U&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-D&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-l&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-allow-multidot&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-max-iso9660-filenames&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-relaxed-filenames&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-no-iso-translate&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-r&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-o&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.artifactId}-${project.version}.dmg&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;-root&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.app.title}&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&quot;color: #0000cc;&quot;&gt;value=&lt;/span&gt;&lt;span style=&quot;background-color: #fff0f0;&quot;&gt;&quot;${project.build.directory}/${ugs.app.title}&quot;&lt;/span&gt;&lt;span style=&quot;color: #007700;&quot;&gt;/&amp;gt;&lt;/span&gt;
                      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/exec&amp;gt;&lt;/span&gt;
                  &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;
              &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
          &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
      &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
  &lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: #007700;&quot;&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h3&gt;
How to use it&lt;/h3&gt;
After all that, using it is a simple matter of running &quot;&lt;b&gt;mvn package -P create-dmg&lt;/b&gt;&quot;. The resulting file will look something like&amp;nbsp;&lt;b&gt;ugs-platform-app-2.0-SNAPSHOT.dmg &lt;/b&gt;and be located in your &lt;b&gt;application/target&lt;/b&gt;&amp;nbsp;directory.&lt;br /&gt;
&lt;h3&gt;
Next Steps&amp;nbsp;&lt;/h3&gt;
&lt;div&gt;
The next steps for me are to create a windows installer with a bundled JRE, and setup TravisCI (which has OSX machines available) to build my artifacts.&lt;/div&gt;
</description><link>http://blog.willwinder.com/2017/09/netbeans-platform-mac-osx-app-bundle.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-3549922715139230769</guid><pubDate>Thu, 10 Aug 2017 14:42:00 +0000</pubDate><atom:updated>2017-08-10T11:38:59.204-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">NetbeansPlatform</category><title>Netbeans Platform: Good, Bad and Ugly</title><description>&lt;br class=&quot;Apple-interchange-newline&quot; /&gt;
&lt;h2&gt;
The Good&lt;/h2&gt;
&lt;br /&gt;
The Netbeans Platform is incredibly simple to get started with. There are extensive tutorial based guides to get you started, like this tutorial about&amp;nbsp;&lt;a href=&quot;https://platform.netbeans.org/tutorials/nbm-crud.html&quot;&gt;creating a CRUD application with zero code&lt;/a&gt;, or making&amp;nbsp;&lt;a href=&quot;https://platform.netbeans.org/tutorials/nbm-paintapp.html&quot;&gt;a paint app&lt;/a&gt;, and&amp;nbsp;&lt;a href=&quot;https://platform.netbeans.org/tutorials/nbm-10-top-apis.html&quot;&gt;videos too&lt;/a&gt;, not to mention&amp;nbsp;&lt;a href=&quot;https://blogs.oracle.com/geertjan/welcome-to-me&quot;&gt;Geertjan&#39;s Blog&lt;/a&gt;&amp;nbsp;which goes into many advanced examples. If you&#39;re using the Netbeans IDE you also get a huge assortment of wizards to help you along with various annotation-based integrations. The wizards have excellent ANT support and pretty good Maven support as well. There are many advanced features in the box from window management, plugins, configurable key-bindings and an auto-update framework.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
The Bad&lt;/h2&gt;
When you want to do something advanced which isn&#39;t covered by a tutorial, you quickly need to roll up your sleeves and dig into the source code. For example, if you choose to migrate from the default ANT build to Maven,&amp;nbsp;&lt;a href=&quot;http://wiki.netbeans.org/DevFaqMavenHowToMigrateFromANT&quot;&gt;this guide is lacking&lt;/a&gt;&amp;nbsp;if you aren&#39;t very familiar with Maven. Or even if you used the built in Maven wizard, there is no documentation that you need to run with the deployment profile to build the installers (if you&#39;re stuck on this one, it&#39;s &quot;&lt;span style=&quot;color: #38761d;&quot;&gt;&lt;b&gt;mvn&amp;nbsp;package -P deployment&lt;/b&gt;&lt;/span&gt;&quot;). Another big pain point for me was trying to create actions at runtime with localization which also showed up in menu&#39;s, this took &lt;a href=&quot;https://blogs.oracle.com/geertjan/dynamically-creating-menu-items-part-1&quot;&gt;lots&lt;/a&gt; &lt;a href=&quot;https://blogs.oracle.com/geertjan/dynamically-creating-menu-items-part-2&quot;&gt;of&lt;/a&gt; &lt;a href=&quot;https://forums.netbeans.org/topic65421.html&quot;&gt;research&lt;/a&gt; before eventually creating a &lt;a href=&quot;http://wiki.netbeans.org/DevFaqActionsAddAtRuntime&quot;&gt;custom service&lt;/a&gt;, and they still didn&#39;t work for all cases. &lt;a href=&quot;http://blog.willwinder.com/2015/03/automated-builds-with-netbeans-platform.html&quot;&gt;Even integrating my build with Jenkins was a headache&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
The Ugly&lt;/h2&gt;
When you&#39;re finished with your application, you still have a Java application. No matter how convenient and productive the language is there is still a strong bias against Java on the Desktop, and that bias doesn&#39;t seem to be going away (maybe &lt;a href=&quot;http://docs.oracle.com/javafx/2/deployment/self-contained-packaging.htm&quot;&gt;JavaFX with native bundling&lt;/a&gt; will help sway people). Until it&#39;s easier to bundle your Netbeans Platform application with a native look and feel that bias isn&#39;t going anywhere.</description><link>http://blog.willwinder.com/2017/08/netbeans-platform-good-bad-and-ugly.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-4848080747683978249</guid><pubDate>Wed, 11 Mar 2015 13:15:00 +0000</pubDate><atom:updated>2017-08-10T11:38:36.664-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">NetbeansPlatform</category><category domain="http://www.blogger.com/atom/ns#">Programming</category><category domain="http://www.blogger.com/atom/ns#">UGS</category><title>UGS 2.0 to Netbeans Platform first impressions</title><description>For the past year &lt;a href=&quot;https://github.com/winder/Universal-G-Code-Sender&quot;&gt;Universal G-code Sender&lt;/a&gt; has suffered pretty severe feature creep. Lots of people had lots of good ideas, but it made the front end code confusing and hard to extend. In large part this is just because it&#39;s hard to compartmentalize a complex GUI. &lt;a href=&quot;https://github.com/grbl/grbl&quot;&gt;GRBL&lt;/a&gt; has had a lot of interesting features added in the past year which are begging to be leveraged, but UGS wasn&#39;t in a position to use them. With all this in mind I started trying to come up with a solution.&lt;br /&gt;
&lt;br /&gt;
The first step was to decouple the &quot;model&quot; object from the GUI. Inspiration for this started nearly a year ago when Bob Jones implemented a web interface for UGS. Bob made a thin API layer which exposed a few simple functions and allowed him to build a completely separate web-based user interface. This pattern is known as &lt;a href=&quot;http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter&quot;&gt;Model-View-Presenter&lt;/a&gt;, and allows for multiple front ends which use the same back end code. It enhances test-ability of the business logic and helps facilitate a flexible front end. It didn&#39;t take more than a few hours to flesh out the rest of the functions this API needed so that the thin API exposed all of the back end features needed for the classic UGS GUI. After that it was a matter of re-implementing the Swing GUI to utilize the new API.&lt;br /&gt;
&lt;br /&gt;
With the foundation laid it was time to consider what to build on top of it. &lt;a href=&quot;https://github.com/winder/Universal-G-Code-Sender/issues/232&quot;&gt;Rugbymauri on github&lt;/a&gt; clued me into an idea that more experienced GUI developers have probably known for a long time: plugin based applications. Then he took it a step further and told me about the&amp;nbsp;&lt;a href=&quot;http://wiki.eclipse.org/index.php/Rich_Client_Platform&quot;&gt;Eclipse Rich Client Platform (Eclipse RCP)&lt;/a&gt; and the &lt;a href=&quot;https://netbeans.org/features/platform/&quot;&gt;Netbeans Platform&lt;/a&gt;. After some initial investigation I decided to pursue Netbeans Platform.&lt;br /&gt;
&lt;br /&gt;
It took about a week for me to completely settle on the idea of a plugin based architecture. In the end, it was a perfect fit for UGS. The back end application already had features for sending status updates to any number of front end listeners. This has been used by the Visualizer from day one, it is a completely separate GUI component and it listens to the same back end events as the main GUI. Something like the Netbeans Platform would take this a step further and facilitate the creation of these modules - whereas in the classic UGS interface all these windows are managed in a monolithic &quot;MainWindow&quot; class.&lt;br /&gt;
&lt;br /&gt;
Moving forward the concept of modules allows me to think about other features in a way that wont cause the code to feel bloated. For instance - how would a Gcode editor that can highlight some number of lines and have it displayed in the 3D visualizer? In the classic interface this would be a huge undertaking that would massively complicate the &quot;MainWindow&quot;. On the other hand with a modular design there just needs to be a new &quot;Editor&quot; module, the Netbeans Platform handles sharing of the gcode file and provides APIs for binding selection events between the Editor and Visualizer. What would have been a week of effort could now be done in an afternoon or two. By the way, the Netbeans Platform also comes with all the modules created for the Netbeans IDE, so it already has GUI components for a code editor with breakpoints (Breakpoints? Maybe another afternoon).&lt;br /&gt;
&lt;br /&gt;
There are a few areas that I&#39;m not sold on yet, for instance UGS already has several observer API&#39;s and I&#39;m not sure if I&#39;ll convert them to use corresponding Netbeans Platform features. Also the library wrapper modules are a little kludgy, especially if your project is built using a separate library jar like UGS is. Every time I rebuild my library jar I need to update the library wrapper module. The workaround I settled on was to modify my library&#39;s build script to dump the jar inside the library wrapper module, a side effect to which is that if I do a clean on that project it breaks the library wrapper.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;
The UGS Platform, UGS&#39;s 2.0 release, is currently in a proof of concept state. All the major integration has been done, now its a matter of creating all the different modules that exist in the Classic GUI. After that other features features of the Netbeans Platform can be leveraged for things like integrated Preferences and Configuration menu&#39;s, and default Window layout schemes.&lt;/div&gt;
</description><link>http://blog.willwinder.com/2015/03/ugs-20-to-netbeans-platform-first.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-6734987181647183236</guid><pubDate>Tue, 10 Mar 2015 23:00:00 +0000</pubDate><atom:updated>2015-03-10T20:52:15.842-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">NetbeansPlatform</category><category domain="http://www.blogger.com/atom/ns#">Programming</category><category domain="http://www.blogger.com/atom/ns#">UGS</category><title>Automated builds with Netbeans Platform 8.0.2</title><description>I&#39;ve been converting UGS to&amp;nbsp;run on the&amp;nbsp;&lt;a href=&quot;https://netbeans.org/features/platform/&quot;&gt;Netbeans Platform&lt;/a&gt;&amp;nbsp;over the past week, and in general its been great. &amp;nbsp;The features and documentation I used while migrating UGS away from Swing were very well done. So aside from a couple hiccups, the Netbeans Platform has been great. Until a few days ago when I decided it was time to integrate my new changes with my Jenkins CI server.&lt;br /&gt;
&lt;br /&gt;
It turns out that you can&#39;t build a Netbeans Platform application without connecting to the Netbeans IDE first. This has to do with the extra modules, platform components and build harness. Searching the subject online provides a lot of documentation, everything I&#39;ve found is either out dated, contradictory, or I could only get it working with a new project and not my existing one.&lt;br /&gt;
&lt;br /&gt;
The final solution was so simple, and minimally invasive that I wanted to create this blog post.&lt;br /&gt;
&lt;br /&gt;
There are 4 interesting files for a Netbeans Platform build:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;b&gt;build.xml&lt;/b&gt;&amp;nbsp;- Some documentation suggests adding a&amp;nbsp;download-netbeans-rcp target here. That isn&#39;t needed. If you have a custom target you can put it here, otherwise there is no need to modify this file.&lt;br /&gt;
&lt;b&gt;nbproject/build-impl.xml&lt;/b&gt;&amp;nbsp;- This is loaded by build.xml, don&#39;t modify this file.&lt;br /&gt;
&lt;b&gt;nbproject/project.properties&lt;/b&gt;&amp;nbsp;- This file seems to identify your modules and the applications name, no need to modify this file either.&lt;br /&gt;
&lt;b&gt;nbproject/platform.properties&lt;/b&gt;&amp;nbsp;- Modify this file!&lt;/blockquote&gt;
The Netbeans Platform has facilities built into its build script to download all required files. &lt;b&gt;platform.properties&lt;/b&gt;&amp;nbsp;is where you need to specify what it should download.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
# These are used later in the build script&lt;br /&gt;
nbplatform.active.dir=${suite.dir}/latestStableBuild&lt;br /&gt;
suite.dir=${basedir}&lt;br /&gt;
harness.dir=${nbplatform.active.dir}/harness&lt;br /&gt;
# Provide some URLs&lt;br /&gt;
bootstrap.url=http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastStableBuild/artifact/nbbuild/netbeans/harness/tasks.jar&lt;br /&gt;
autoupdate.catalog.url=http://dlc-cdn.sun.com/netbeans/updates/8.0.2/uc/final/distribution/catalog.xml.gz&lt;/blockquote&gt;
&lt;div&gt;
Thats it! It probably took me 5+ hours to find that. Now I can run &quot;ant build-zip&quot; on my Jenkins server and get a zip archive in return.&lt;/div&gt;
</description><link>http://blog.willwinder.com/2015/03/automated-builds-with-netbeans-platform.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-5201469098344527986</guid><pubDate>Sat, 01 Feb 2014 19:55:00 +0000</pubDate><atom:updated>2014-02-03T09:11:11.045-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CNC</category><category domain="http://www.blogger.com/atom/ns#">linux</category><category domain="http://www.blogger.com/atom/ns#">osx</category><category domain="http://www.blogger.com/atom/ns#">Programming</category><title>Easy and accurate SVG to DXF conversion using Inkscape and pstoedit</title><description>If you&#39;ve ever worked with a vector tool to design something to be carved with a CNC routing machine, you may have needed to convert the resulting SVG file into a DXF. In my workflow I often use Inkscape to generate my SVG graphic, but my CAD software is CamBam and it requires a DXF file.&lt;br /&gt;
&lt;br /&gt;
Many people have tried to make the perfect Inkscape plugin for exporting DXF, there is&amp;nbsp;&lt;a href=&quot;http://www.bobcookdev.com/inkscape/inkscape-dxf.html&quot;&gt;Better DXF Export&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;http://tim.cexx.org/?p=590&quot;&gt;Better Better DXF Export&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href=&quot;http://www.bigbluesaw.com/saw/big-blue-saw-blog/general-updates/big-blue-saws-dxf-export-for-inkscape.html&quot;&gt;Big Blue Saw&#39;s DXF Export&lt;/a&gt;. I&#39;ve had the most luck with the last one, but it was still finicky and often broke when updating Inkscape or OSX. Not only that, but at the end of the Big Blue Saw page, Simon mentions that he doesn&#39;t even use the plugin most of the time, instead relying on a command line tool called pstoedit.&lt;br /&gt;
&lt;br /&gt;
With that in mind I made a small shell script which will convert an SVG file to DXF, you can save the script and run it directly or add the functions to your startup script.&lt;br /&gt;
&lt;br /&gt;
The script is available on github under the project &lt;a href=&quot;https://github.com/winder/svgToDxf&quot;&gt;svgToDxf&lt;/a&gt;.

If you choose to add the functions directly to your startup script, as I have, simply copy the functions here and paste them in your .bashrc file.&amp;nbsp;Now all you need to do to convert an SVG file to DXF is use the command &lt;b&gt;svgToDxf MyCoolDesign.svg&lt;/b&gt; and the output will be &lt;b&gt;MyCoolDesign.dxf&lt;/b&gt;&lt;script src=&quot;https://gist.github.com/winder/8757756.js&quot;&gt;&lt;/script&gt;</description><link>http://blog.willwinder.com/2014/02/easy-and-accurate-svg-to-dxf-conversion.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>12</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-2115471841690672489</guid><pubDate>Sun, 22 Dec 2013 05:13:00 +0000</pubDate><atom:updated>2017-08-10T11:39:14.836-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Programming</category><title>OpenSCAD linear extrude that interpolates between two shapes</title><description>Full source code is available for &lt;a href=&quot;http://www.thingiverse.com/thing:209964&quot;&gt;this thing on thingiverse&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
OpenSCAD has proven to be an easy to use and flexible tool, but at times I&#39;m left scratching my head to figure out ways around its limitations. In this case I had two squares with rounded corners, their sizes and the corner radius for both squares are configurable. Now I wanted a square tube to join these two pieces together.&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;http://www.flickr.com/photos/39069226@N03/11489759775/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot; title=&quot;Final shape by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Final shape&quot; src=&quot;http://farm6.staticflickr.com/5479/11489759775_9e57b1c80d.jpg&quot; height=&quot;332&quot; width=&quot;312&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
My hope was that I could get it close enough with some fancy parameters piped into the OpenSCAD linear_extrude function. It seemed possible since there are some pretty fancy parameters regarding rotations and twists. After checking out the documentation it didn&#39;t look like there was anything that would be close.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
For this project I found a module by &lt;a href=&quot;https://github.com/Metamaquina/Metamaquina2Beta/blob/master/rounded_square.scad&quot;&gt;Felipe C. da S. Sanches to create squares with rounded corners&lt;/a&gt;. I&#39;m sure there are many like it, but this was the first one I found and it worked as advertised.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
So made a little module using this which wraps around a rectangle for one side, and another which fits into a rectangle for the other side:&lt;/div&gt;
&lt;script src=&quot;https://gist.github.com/winder/8078285.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;
This simply creates a rectangle with rounded corners and subtracts a slightly smaller rectangle from the middle, then extrudes the whole assembly some height. This piece goes around a rectangle with square corners, so the inside has square corners. Then I created a second tube designed to fit into a rectangle with rounded corners, so the outside of this tube must have rounded corners. After spacing them out I get something that looks like this:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/11489656364/&quot; title=&quot;Multiple rounded rectangles by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Multiple rounded rectangles&quot; src=&quot;http://farm4.staticflickr.com/3821/11489656364_b0801b26b0.jpg&quot; height=&quot;363&quot; width=&quot;388&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
This is where I realized I need some fancy function to extrude the bottom shape into something which meets up with the upper shape. The solution I ended up with works the same way as the 3D printer I&#39;ll print this object with: slice it up into multiple extrusions. For each extrusion I interpolate the inner radius, outer radius, width and length depending on how far into the overall extrusion we are.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/winder/8078442.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
With this I can now create a shape which connects my two adapters. The final product printed out nicely:&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;http://www.flickr.com/photos/39069226@N03/11489922923/&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; title=&quot;Printed object by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Printed object&quot; src=&quot;http://farm3.staticflickr.com/2885/11489922923_65566c6543.jpg&quot; height=&quot;375&quot; width=&quot;500&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;Finished product!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&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;http://www.flickr.com/photos/39069226@N03/11489923883/&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; title=&quot;Installed adapter by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Installed adapter&quot; src=&quot;http://farm8.staticflickr.com/7362/11489923883_83cf1697b9.jpg&quot; height=&quot;375&quot; width=&quot;500&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;Fits perfectly.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
</description><link>http://blog.willwinder.com/2013/12/openscad-linear-extrude-that.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-7181167675192315076</guid><pubDate>Tue, 30 Apr 2013 10:32:00 +0000</pubDate><atom:updated>2015-12-14T14:13:24.738-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">3D printing</category><category domain="http://www.blogger.com/atom/ns#">projects</category><title>A week of 3D printing with the OrdBot Hadron</title><description>It has been about a week since my &lt;a href=&quot;http://blog.willwinder.com/2013/04/building-ordbot-3d-printer.html&quot;&gt;OrdBot&#39;s&lt;/a&gt; first successful calibration print and it seems like a good time to write down some of my initial problems, experiences and solutions. However before I get into that a review of my 3D printer stack.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Hardware&lt;/b&gt;&lt;br /&gt;
OrdBot Hadron&lt;br /&gt;
3/8&quot;-12 TPI ACME Z-Axis screws&lt;br /&gt;
1.8 degree Nema17 X/Y motors&lt;br /&gt;
1.8 degree Nema23 Z motors&lt;br /&gt;
Qu-bd MBE Extruder (with some modifications)&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Electronics&lt;/b&gt;&lt;br /&gt;
AzteegX1 v1.0 (&lt;span style=&quot;background-color: #eeeeee; font-family: &amp;quot;arial&amp;quot; , &amp;quot;verdana&amp;quot; , &amp;quot;helvetica&amp;quot; , sans-serif; font-size: 12px;&quot;&gt;644P processor)&lt;/span&gt;&lt;br /&gt;
Lava heatbed, I have only used PLA at this point so this is still disconnected.&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;Software&lt;/b&gt;&lt;br /&gt;
Mac OSX&lt;br /&gt;
Marlin firmware&lt;br /&gt;
Slic3r v0.9.9&lt;br /&gt;
Pronterface (March 2012 release)&lt;br /&gt;
Repetier-host Mac (0.56, lower versionthan linux/windows releases for some reason)&lt;br /&gt;
&lt;br /&gt;
Now for the problems encountered during the past week, approximately in the order which I ran into them.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Pololu A4988&lt;/b&gt;&amp;nbsp;- I have used these before on my ShapeOko CNC machine. They have been so reliable that I completely forgot how to set them up, as such I blew up 3 drivers while setting up the electronics. So &lt;b&gt;be careful&lt;/b&gt;, &lt;b&gt;check the polarity&lt;/b&gt;&amp;nbsp;and for the love of everything &lt;b&gt;don&#39;t measure voltage when your multimeter is in continuity mode!&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Leveling the bed&lt;/b&gt;&amp;nbsp;- This was tedious but not particularly difficult. The LAVA heatbed has 8 M3 screws along the perimeter so calibration was just a matter of carefully tightening them until the height is just right. To measure the height I lowered the printer nozzle until it had just enough space for a piece of paper to slide underneath with little resistance, at this point I manually jogged the nozzle along the perimeter adjusting the M3 screws as necessary until the bed was level.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Setting the Z-Axis endstop&lt;/b&gt;&amp;nbsp;- This step is ongoing and was very tedious without a way to precisely raise or lower the endstop actuator (a screw in sliding t-slot nut). I have it set fairly close, but still find myself hitting the &quot;stop motor&quot; button and manually turning the Z-Axis screws while the first layer is printing. Eventually I will print out &lt;a href=&quot;http://www.thingiverse.com/thing:26534&quot;&gt;something like this&lt;/a&gt; and dial in the Z endstop a more reliably.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Configuring steps per mm&lt;/b&gt;&amp;nbsp;- This was the longest part of the process, and I&#39;m still not sure why. Fortunately it wasn&#39;t difficult, just tedious. I know the steps per revolution on my motors (200), the microstep resolution for the steppers (16x), the pitch of MXL belts (2.032mm) and the number of teeth on the pulley (16). Using the equation on the &lt;a href=&quot;http://www.buildlog.net/wiki/doku.php?id=ord:software&quot;&gt;buildlog.net configuration page&lt;/a&gt; gave me a number which ended up being way too small. My calibration object of choice was the &lt;a href=&quot;http://www.thingiverse.com/thing:11261&quot;&gt;nickel test thing&lt;/a&gt;&amp;nbsp;on thingiverse, I just kept printing them out, measuring the insides and adding a ratio multiplier until satisfied that things were the correct size.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Pololu A4988&amp;nbsp;overheating&lt;/b&gt;&amp;nbsp;- Once I had my machine calibrated I started trying to print larger objects that took more time. Without fail after 5-10 minutes after starting the machine would start losing steps. Eventually I noticed how hot the stepper drivers were and mounted a fan pointing at them which fixed the problem.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;The notorious QU-BD MBE&lt;/b&gt;. This thing gave me a ton of trouble, so much so that I ordered a J-Head after the first day of trying to get it up and running. That said I&#39;ve managed to get it to print very reliably and haven&#39;t yet felt the need to install the J-Head.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;QU-BD pre-upgrades&lt;/b&gt;&amp;nbsp;- Before I even had my printer assembled I performed some upgrades on the QU-BD. First is the &lt;a href=&quot;http://store.makerslide.com/index.php?main_page=product_info&amp;amp;cPath=12&amp;amp;products_id=87&quot;&gt;QU-BD Modification Kit&lt;/a&gt; from the MakerSlide store. Secondly I hobbed the stock raptor gear with a small tap in my electric hand drill.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;QU-BD overheating jam&lt;/b&gt;&amp;nbsp;- There is a fan and heatsink on the extruder. I wired it (incorrectly) to the Azteeg X1 fan input, making it configurable in gcode (M106 and M107). My slic3r configuration toggled the fan on and off, so&amp;nbsp;occasionally&amp;nbsp;the cold-end of the extruder was heating up too much causing a jam. My current solution is disabling the cooling option in slic3r, but eventually I will wire the fan directly to the 12v input.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;QU-BD underheating jam&lt;/b&gt;&amp;nbsp;- My printer is in the kitchen and there is a nice crossdraft going through the room now that it is nice outside. The crossdraft was causing the nozzle to drop below 165 degrees, when this happens things usually jam. I need to wrap the hot end in some sort of insulation which will hopefully fix this problem.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;QU-BD slicer jam&lt;/b&gt;&amp;nbsp;- While printing the &lt;a href=&quot;http://www.thingiverse.com/thing:27739&quot;&gt;nautilus gear thing&lt;/a&gt;&amp;nbsp;I was noticing frequent jams during the infill stage. The thing about this object was that there was very little amount of infill needed so the filament moved very&amp;nbsp;slowly. My best guess is that the filament was sitting in one place so long that it heated up too high in the extruder which caused the jam. After reconfiguring slic3r to leave the object hollow there were no more jams on this print.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Pronterface vs. Repetier-host&lt;/b&gt;&amp;nbsp;- This could probably be a blog post on its own. They both work but suffice to say that repetier-host is leaps and bounds ahead of pronterface in terms of usability. The one problem I had was that repetier-host allows you to configure the controller buffer size, the default is 63 which causes stuttering when printing complex surfaces with short line segments. I upped this value to 127 when I was 75% finished with the owl print (below) and you can tell just from looking at the surface of the model where I made that change because the surface quality improved dramatically. I just wish I had found that setting before printing all those feathers!&lt;br /&gt;
&lt;br /&gt;
This owl is my longest print to date at 3 1/2 hours. This print was running while a draft was running past the printer, I probably fixed 5 or 6 jams while it printed which caused a several&amp;nbsp;noticeable&amp;nbsp;defects (most of them on the owls back).&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8696665516/&quot; title=&quot;3D Prints by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;3D Prints&quot; src=&quot;http://farm9.staticflickr.com/8534/8696665516_2ef5251487.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
After many attempts I finally got the Nautilus Gear to print. Now that everything is dialed in I could print one out in about an hour (~12 minutes for the two clips and ~18 minutes per gear).&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8696666774/&quot; title=&quot;3D Prints by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;3D Prints&quot; src=&quot;http://farm9.staticflickr.com/8258/8696666774_83a5534517.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;</description><link>http://blog.willwinder.com/2013/04/a-week-of-3d-printing-with-ordbot-hadron.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-5683025890908174205</guid><pubDate>Sat, 27 Apr 2013 23:01:00 +0000</pubDate><atom:updated>2017-08-10T11:39:56.961-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">3D printing</category><category domain="http://www.blogger.com/atom/ns#">CNC</category><category domain="http://www.blogger.com/atom/ns#">projects</category><title>Building an OrdBot 3D Printer</title><description>I&#39;ve been a fan of &lt;a href=&quot;https://www.google.com/url?sa=t&amp;amp;rct=j&amp;amp;q=&amp;amp;esrc=s&amp;amp;source=web&amp;amp;cd=4&amp;amp;cad=rja&amp;amp;ved=0CEwQtwIwAw&amp;amp;url=http%3A%2F%2Fwww.kickstarter.com%2Fprojects%2F93832939%2Fmakerslide-open-source-linear-bearing-system&amp;amp;ei=MUl8Uf_cGsHN0gGem4GYAQ&amp;amp;usg=AFQjCNHiZKoIZ3E00-WZq2eso8p0fBcOnQ&amp;amp;sig2=_z-iZANkqqXzPTkWLmvsXw&amp;amp;bvm=bv.45645796,d.dmQ&quot;&gt;MakerSlide&lt;/a&gt;&amp;nbsp;ever since building my &lt;a href=&quot;http://blog.willwinder.com/2013/04/shapeoko-cnc-mill.html&quot;&gt;ShapeOko CNC Mill&lt;/a&gt;, and have been interested in 3D printing since first hearing about the RepRap project in 2007. So when I first saw the OrdBot, I knew that it would be the printer I build. It didn&#39;t hurt that I had 10 feet of extra MakerSlide and a whole bunch of the special bearings and eccentric spacers left over from my ShapeOko build.&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;http://www.buildlog.net/wiki/lib/exe/fetch.php?cache=&amp;amp;media=ord_bot:171066485817833513_p9xcprqs_c.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://www.buildlog.net/wiki/lib/exe/fetch.php?cache=&amp;amp;media=ord_bot:171066485817833513_p9xcprqs_c.jpg&quot; height=&quot;296&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
One of my goals was to build all the custom parts myself, the blue and black pieces in the photo above. Cutting aluminum on my new CNC machine pushed it to the limit, but worked out in the end. One of the larger OrdBot pieces is the handle, here is a shot of the ShapeOko making short work of it:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8588922873/&quot; title=&quot;Ord Bot Handle by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Ord Bot Handle&quot; src=&quot;http://farm9.staticflickr.com/8519/8588922873_6ae9283427.jpg&quot; height=&quot;500&quot; width=&quot;374&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
There are a couple parts that I modified or upgraded during the build. Most notably the Z axis. After reading about bent Z-rods I decided to get some ACME rods, and I wanted to use some spare Nema-23 motors that I had on hand.&lt;br /&gt;
&lt;br /&gt;
The NEMA-23 motors for the Z axis were easy, I slightly modified the stock motor part to fit the new motor:&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8590015592/&quot; title=&quot;Ord Bot Modified Z Mount by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Ord Bot Modified Z Mount&quot; src=&quot;http://farm9.staticflickr.com/8227/8590015592_a195b35bee.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Attaching the ACME rod was a little trickier, but managed to hand fabricate a bracket with some 1/8&quot; thick angle aluminum:&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8588918345/&quot; title=&quot;Ord Bot Modified Z Mount by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Ord Bot Modified Z Mount&quot; src=&quot;http://farm9.staticflickr.com/8091/8588918345_b54ed83386.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
In the spirit of being thrifty, I signed up for one of the (now notorious) Qu-bd extruders during their kickstarter campaign. So far I&#39;ve managed to get the extruder to work with a couple of simple motifications. Some more hand fabricated mounts and I had the extruder attached:&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8590007270/&quot; title=&quot;Ord Bot Qu-bd mount by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Ord Bot Qu-bd mount&quot; src=&quot;http://farm9.staticflickr.com/8229/8590007270_b6f53891a8.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
For the electronics I am using one of the first generation Azteeg X1 boards. So far I&#39;ve been very happy with it (save for an exciting wiring mistake where I had the polarity backwards). I opted to run the wires through the hollow MakerSlide extrusions whenever possible. Here is the nearly completed wiring, I really appreciated the zip-tie holes:&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8592135587/&quot; title=&quot;Ord Bot Wiring by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Ord Bot Wiring&quot; src=&quot;http://farm9.staticflickr.com/8111/8592135587_e7047687bf.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
After a few days of tweaking and calibrating, I&#39;m getting some nice results. Here are some action shots with the machine up and running:&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8686442447/&quot; title=&quot;OrdBot in action by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;OrdBot in action&quot; src=&quot;http://farm9.staticflickr.com/8395/8686442447_4220209c78.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8687568086/&quot; title=&quot;OrdBot in action by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;OrdBot in action&quot; src=&quot;http://farm9.staticflickr.com/8121/8687568086_28bfb3a45e.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8687558922/&quot; title=&quot;OrdBot in action by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;OrdBot in action&quot; src=&quot;http://farm9.staticflickr.com/8546/8687558922_2b6347527d.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8687560288/&quot; title=&quot;OrdBot in action by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;OrdBot in action&quot; src=&quot;http://farm9.staticflickr.com/8537/8687560288_018fdf987c.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8687560772/&quot; title=&quot;OrdBot in action by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;OrdBot in action&quot; src=&quot;http://farm9.staticflickr.com/8115/8687560772_21c00f8fe7.jpg&quot; height=&quot;374&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;</description><link>http://blog.willwinder.com/2013/04/building-ordbot-3d-printer.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>13</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-771289785576263334</guid><pubDate>Sat, 27 Apr 2013 22:20:00 +0000</pubDate><atom:updated>2013-09-25T14:15:34.804-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CNC</category><category domain="http://www.blogger.com/atom/ns#">projects</category><category domain="http://www.blogger.com/atom/ns#">woodworking</category><title>ShapeOko CNC Mill</title><description>April of 2012 I signed up for the first batch of ShapeOko kits from inventables.com. Unsure of how popular the kit would be, inventables had a kickstarter-style order of 150 (or so) kits. That number was reached handily and several more batches followed. Since then the ShapeOko has become a standard item in their store.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I&#39;ve wanted a CNC mill for a long time, but could never justify the expense. Now there are products like the MakerSlide linear rail system that made it possible for low cost machines. The first round of kits were only $200 for the entire mechanical platform - add your electronics and a dremel tool and the machine can start cutting.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
So thats what I did.&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;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.youtube.com/embed/mnjPWBgxunI?feature=player_embedded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The stock kit plus motors after assembly:&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/7447242664/&quot; title=&quot;Assembled ShapeOko Front by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Assembled ShapeOko Front&quot; height=&quot;375&quot; src=&quot;http://farm9.staticflickr.com/8163/7447242664_400367e6dd.jpg&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
One of the nice things about the ShapeOko is how hackable it is. For instance if you want to make the cutting area larger you can just replace the MakerSlide with longer rails. So I added longer rails, a second Y-axis motor, a torsion box to mount everything on, a bigger router and some woodworking T-slot to hold work. Here is a picture of the machine a few weeks ago while cutting a large aluminum part for another project:&lt;br /&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/8588922873/&quot; title=&quot;Ord Bot Handle by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;Ord Bot Handle&quot; height=&quot;500&quot; src=&quot;http://farm9.staticflickr.com/8519/8588922873_6ae9283427.jpg&quot; width=&quot;374&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
The current cost of the machine is $663.75, that includes all components bought for the machine regardless of whether or not they were used and shipping. So far I&#39;ve spent another $150.55 on endmill of various sizes for for cutting various materials.&lt;/div&gt;
</description><link>http://blog.willwinder.com/2013/04/shapeoko-cnc-mill.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-6498828602176638728</guid><pubDate>Sat, 26 Jan 2013 16:27:00 +0000</pubDate><atom:updated>2013-01-26T11:32:42.939-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">projects</category><title>Projects Past: The 2009 Automatic Cat Feeder</title><description>&lt;br /&gt;
In 2009 I took a week off from work to build an&amp;nbsp;&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/sets/72157622639983549/&quot;&gt;Automatic Cat Feeder&lt;/a&gt;&amp;nbsp;to ration food to the overweight household cat. This was a project built from need and it had a deadline because my girlfriend and I were heading out of town for Thanksgiving.&lt;br /&gt;
&lt;br /&gt;
The requirements were simple: It needed to be easy to refill and dispense the correct amount of food twice daily. Early on I had an idea for a large rotating drum to dispense the food, it needed to be easily removable for refilling and heavy enough that it could sit on a motorized wheel and cause enough friction to be turned.&lt;br /&gt;
&lt;br /&gt;
Figuring out a latch mechanism that dropped the correct quantity of food after one rotation was the first tricky part. I didn&#39;t want any electronics on the drum so it had to be completely mechanical. In the end I hot glued a small box to the outside of the drum which filled during rotation, when it started moving back towards the top a tab would be hit to fling a door open and drop the food out. A magnet held the door shut and gravity would shut the door &amp;nbsp;when the box made it back to the top. I&#39;m extremely proud of this mechanism and it worked perfectly.&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;http://farm3.staticflickr.com/2422/4102025124_cee9a3e2eb_b.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;http://farm3.staticflickr.com/2422/4102025124_cee9a3e2eb_b.jpg&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;The dispenser mechanism! A marvel of engineering, hot glue, magnets and JB Quick Weld.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
With all the details figured out, hacking it together turned out to be pretty easy. The hardware store patiently cut a sheet of 1/4&quot; MDF into pieces for me which formed the box, and I was able to glue the pieces together with gorilla glue. &amp;nbsp;A small stepper motor and some electronics were harvested from a 5 1/4&quot; floppy drive and I was able to control it with an arduino. &amp;nbsp;The program couldn&#39;t have been more than 50 lines, I figured out how many steps it took for 1 revolution of the drum through trial and error. Then coded it to do that every 12 hours.&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;http://farm3.staticflickr.com/2640/4133191088_f015d6c368_b.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;http://farm3.staticflickr.com/2640/4133191088_f015d6c368_b.jpg&quot; width=&quot;240&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;The finished product. Seriously, we used it looking like this for several years.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&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;http://farm3.staticflickr.com/2629/4133795454_94c27d0ddb_b.jpg&quot; imageanchor=&quot;1&quot; style=&quot;display: inline !important; margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;http://farm3.staticflickr.com/2629/4133795454_94c27d0ddb_b.jpg&quot; width=&quot;240&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;The happy customer. My wife added a chute made from a mountain dew bottle.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
I did make a couple upgrades after these shots were taken. The stepper motor died so I upgraded to sparkfun&#39;s cheapest stepper and an EasyDriver stepper driver. I also added a reed switch to the box and put a magnet on the drum so that I didn&#39;t have to count steps, this was needed because depending on how much food was in the drum it would sometimes slip and require a different number of steps to get back to the top.&lt;/div&gt;
</description><link>http://blog.willwinder.com/2013/01/projects-past-2009-automatic-cat-feeder.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-8276850921295008031</guid><pubDate>Sat, 26 Jan 2013 15:52:00 +0000</pubDate><atom:updated>2013-01-27T11:23:36.492-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">projects</category><title>Projects Past: The 2007 DIY Projector</title><description>I&#39;ve had many projects over the years, the most involved by far has got to be my DIY projector. &amp;nbsp;It is also my first&amp;nbsp;serious project. By the end of it I had vastly improved my knowledge of optics, electronics, woodworking, and even a little thermal dynamics.&lt;br /&gt;
&lt;br /&gt;
Research started sometime mid 2007, the now defunct lumenlab forums had a whole community of people building DIY projectors. There is a lot of theory to learn, optics to figure out, components to buy, and bringing all the pieces together is no simple task. &amp;nbsp;For such a complexe machine, at the core it has some very simple principles: A point light source radiates light into a fresnel lens, which straightens the light through the LCD screen, then another fresnel lens angles the light into a special lens which can focus the light on a screen. Here is an image from engadget which demonstrates the light path:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.blogcdn.com/www.engadget.com/media/2006/10/hd-projector-design-pj-howto.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;190&quot; src=&quot;http://www.blogcdn.com/www.engadget.com/media/2006/10/hd-projector-design-pj-howto.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
The major problem with these sorts of projectors is that they tended to be very large. 1080p monitors at the time tended to be at least 19&quot; diagonal, which meant the entire box would need to be 3-4 feet long! Fortunately there were a few&amp;nbsp;entrepreneurial&amp;nbsp;types who started sourcing LCD screens that were smaller and ideal for DIY projection and selling them directly from the manufacturers. I managed to get my hands on one such monitor that was only 10.6&quot; and had 720p resolution. With the LCD picked out I was able to begin designing the enclosure around December 2007.&lt;br /&gt;
&lt;br /&gt;
Other than the LCD, the most important component is the light source. Some of the brightest bulbs consumed 400 watts of electricity or more, and had very impressive results. But with such high powered bulbs came a lot of heat that needed to be dealt with. I ended up going with a smaller 150 watt bulb.&lt;br /&gt;
&lt;br /&gt;
The final pieces were the fresnel lenses and the projection lens. These were a fairly standard item and lumenlab took a lot of the guess work out of things by selling them from their store.&lt;br /&gt;
&lt;br /&gt;
My final design to incorporate all these parts was a two chamber design.&amp;nbsp;&amp;nbsp;The top chamber had the optics. &amp;nbsp;The bottom chamber contained all of the electronics: The LCD controller, the light bulb components (transformer and starting capacitor), and AC adapters and a heat sensitive trigger which would keep the fans running until the heat in the box dropped far enough.&lt;br /&gt;
&lt;br /&gt;
The enclosure was designed to be simple to build and easy to assemble. The parts were cut on my dads table saw, and pieces were&amp;nbsp;laminated&amp;nbsp;together so that components could slide in and out of the enclosure during assembly. This design worked nicely.&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;http://farm9.staticflickr.com/8334/8415975731_06c18699e2_o.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;http://farm9.staticflickr.com/8334/8415975731_06c18699e2_o.jpg&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;In this picture you can see all the electronics (minus the thermal switch) and the various slots for the LCD, fresnel lenses, and chamber separators.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
The LCD and Fresnel Lenses were built into frames that could easily slide into the slots:&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;http://farm9.staticflickr.com/8077/8417071954_255af856c2_o.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;http://farm9.staticflickr.com/8077/8417071954_255af856c2_o.jpg&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;The LCD frame had special protection for the delicate flexible cables of the monitor electronics. The frames for the fresnel lenses were similar.&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&gt;&lt;a href=&quot;http://farm9.staticflickr.com/8054/8415975837_96348158b5_o.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;http://farm9.staticflickr.com/8054/8415975837_96348158b5_o.jpg&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;&gt;The light source needs to be precisely positioned for the light to focus properly, so this box was built with rare earth magnets on the bottom. It attaches to a metal plate in the box which can be seen in the upper left of this photo.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&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;http://farm9.staticflickr.com/8331/8415975867_d74b9baa43_o.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;http://farm9.staticflickr.com/8331/8415975867_d74b9baa43_o.jpg&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;The back was hinged so that adjustments could be made as needed, here you can also see the light box in place.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&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&gt;&lt;a href=&quot;http://farm9.staticflickr.com/8518/8417071860_dcb1b8f76f_o.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;http://farm9.staticflickr.com/8518/8417071860_dcb1b8f76f_o.jpg&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;&gt;The triplet lens is another clever mechanism I came up with, by building the box a little loose I was able to pad it with felt so that the lens carriage could slide in/out but still be held snugly in place. The slot at the bottom was for a holding screw, but ended up being&amp;nbsp;unnecessary.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&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;http://farm9.staticflickr.com/8073/8415975859_74aa4cbccf_o.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;http://farm9.staticflickr.com/8073/8415975859_74aa4cbccf_o.jpg&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;Fully assembled, on the left is a front surface mirror which allows the box to be 11&quot; deep instead of &amp;nbsp;nearly 30.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;br /&gt;
The final result is big and ugly, but it worked and I used it for years. I&#39;ll never forget how&amp;nbsp;surprised&amp;nbsp;my girlfriend was when I plugged it in for the first time and an image,&amp;nbsp;apparently&amp;nbsp;she thought I was crazy for those 5 months. In the end it really did work nicely. Designing it was a lot of fun, but my craftsmanship really limited my options.&lt;br /&gt;
&lt;br /&gt;
More pictures can be found on my flickr page: &lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/sets/72157632611903813/&quot;&gt;DIY video projector&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</description><link>http://blog.willwinder.com/2013/01/projects-past-2007-diy-projector.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>14</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-6193196859370264319</guid><pubDate>Thu, 19 May 2011 12:34:00 +0000</pubDate><atom:updated>2017-08-10T11:40:11.750-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">iphone</category><category domain="http://www.blogger.com/atom/ns#">Programming</category><title>XCode4: UITabBarController with UINavigationController using Interface Builder</title><description>&lt;div style=&quot;margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;&quot;&gt;
If this helped you, please consider downloading my &lt;b&gt;FREE&lt;/b&gt;&amp;nbsp;iPhone app&amp;nbsp;&quot;&lt;a href=&quot;http://itunes.apple.com/app/hydrateyourself/id392649838?mt=8&quot;&gt;Hydrate Yourself&lt;/a&gt;&quot;and leaving a review.&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
There are a&amp;nbsp;&lt;a href=&quot;http://www.cimgf.com/2009/06/25/uitabbarcontroller-with-uinavigationcontroller-using-interface-builder/&quot;&gt;whole&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://21gingerman.wordpress.com/2009/04/06/tutorial-and-sample-code-for-iphone-app-with-tab-bar-and-nav-bar/&quot;&gt;bunch&lt;/a&gt;&amp;nbsp;of&amp;nbsp;&lt;a href=&quot;http://www.jamellacreations.com/blog/build-app-with-tab-bar-navigation-bar-using-interface-builder-part-1/&quot;&gt;tutorials&lt;/a&gt;&amp;nbsp;to create a Navigation Controller inside a TabBarController with XCode3. &amp;nbsp;But there are a number of small changes in XCode4 which can trip up someone not familiar with them. &amp;nbsp;So this guide is essentially an updated version of those other guides, designed to create the same simple demo using the new tool.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;1. Create a new Project&lt;/span&gt;&lt;br /&gt;
We are going to start with a Tab Bar Application, so go ahead and create a new project as usual. I&#39;m calling mine Navigation Tab Bar.&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/5736724110/&quot; title=&quot;1. Create new project by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;1. Create new project&quot; src=&quot;http://farm4.static.flickr.com/3250/5736724110_0cbb4231ce.jpg&quot; height=&quot;338&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;2. Select the Tab Bar&lt;/span&gt;&lt;br /&gt;
Click MainWindow.xib and you should see the integrated Interface Builder. On the left is a section called Objects, select the Tab Bar Controller.&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/5736173931/&quot; title=&quot;2. Select Tab Bar by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;2. Select Tab Bar&quot; src=&quot;http://farm4.static.flickr.com/3099/5736173931_fbf65c131f.jpg&quot; height=&quot;238&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;3. Add the Navigation Controller&lt;/span&gt;&lt;br /&gt;
On the right side of your screen click the Utilities box. This is new in XCode4 and should look familiar if you&#39;ve been using XCode3. At the bottom find the Navigation Controller and drag it into the Tab Bar in the middle of your screen.&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/5736724178/&quot; title=&quot;3. Expand Utility View and Add Navigation Controller by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;3. Expand Utility View and Add Navigation Controller&quot; src=&quot;http://farm4.static.flickr.com/3159/5736724178_ddd8c8f56a.jpg&quot; height=&quot;500&quot; width=&quot;246&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&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;http://www.flickr.com/photos/39069226@N03/5736174001/&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; title=&quot;3b. Navigation Controller Added by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;3b. Navigation Controller Added&quot; src=&quot;http://farm4.static.flickr.com/3093/5736174001_6f89c41a97.jpg&quot; height=&quot;118&quot; width=&quot;380&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;Here you can see I added it as the middle Tab&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;4. Configure new tab&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Back in the Objects section you can now expand the Navigation Controller and select the View Controller object. Now on the right side you can select the Identity Inspector at the top and put in your UIViewController&#39;s class name. For the purpose of this demo I am calling it &quot;YourNewViewController&quot; which we will make at the end.&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/5736724252/&quot; title=&quot;4. Expand new Navigation Controller select identity inspector and fill in new View Controller name by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;4. Expand new Navigation Controller select identity inspector and fill in new View Controller name&quot; src=&quot;http://farm3.static.flickr.com/2159/5736724252_6a76c19d90.jpg&quot; height=&quot;158&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Once that is done switch to the Attribute Inspector and put in the name of your XIB file in the NIB Name section. Note: do not add the .xib extension, this will cause a runtime crash.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/5736724282/&quot; title=&quot;4b. Switch to Attribute Inspector and Fill in the nib name by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;4b. Switch to Attribute Inspector and Fill in the nib name&quot; src=&quot;http://farm4.static.flickr.com/3520/5736724282_541b53cca9.jpg&quot; height=&quot;169&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;5. Create the new UI View Controller&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
This is probably familiar to you already, create file from the File menu and select&amp;nbsp;a UIViewController subclass.&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/5736724314/&quot; title=&quot;5. Create new UIViewController by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;5. Create new UIViewController&quot; src=&quot;http://farm3.static.flickr.com/2697/5736724314_b230648e13.jpg&quot; height=&quot;337&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&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;http://www.flickr.com/photos/39069226@N03/5736174131/&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; title=&quot;5b. With XIB by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;5b. With XIB&quot; src=&quot;http://farm6.static.flickr.com/5150/5736174131_98de262e4e.jpg&quot; height=&quot;103&quot; width=&quot;500&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;Be sure to select With XIB for user interface&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&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;http://www.flickr.com/photos/39069226@N03/5736174177/&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; title=&quot;5c. Use the same file name as before by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;5c. Use the same file name as before&quot; src=&quot;http://farm4.static.flickr.com/3519/5736174177_7e449b7fcb.jpg&quot; height=&quot;312&quot; width=&quot;426&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;Use the same file name as you used before.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;6. Add an IBAction&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
We will be adding a button to push a view onto the navigation controller. &amp;nbsp;Open up the header file and add an action.&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&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;http://www.flickr.com/photos/39069226@N03/5736174217/&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; title=&quot;8. Add an IBAction for our button by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;8. Add an IBAction for our button&quot; src=&quot;http://farm3.static.flickr.com/2580/5736174217_300302b0fd.jpg&quot; height=&quot;227&quot; width=&quot;395&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;I just added the buttonPushView line&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Switch to the implementation file and add another few lines for a simple call to pushViewController.&lt;/div&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;http://www.flickr.com/photos/39069226@N03/5736724414/&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; title=&quot;9. Tell the navigationController to push a new View Controller by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;9. Tell the navigationController to push a new View Controller&quot; src=&quot;http://farm3.static.flickr.com/2655/5736724414_88359c9930.jpg&quot; height=&quot;140&quot; width=&quot;500&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;This is the simplest pushViewController line I could come up with&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;7. Add a button&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
Select YourNewViewController.xib in the file menu. Adding a button is a simple matter of dragging/dropping from the bottom right Objects section.&lt;br /&gt;
&lt;br /&gt;
Next you link the button to the new IBAction by right clicking the button and dragging the arrow to File&#39;s Owner, in the Placeholders section. Select buttonPushView.&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;http://www.flickr.com/photos/39069226@N03/5736724448/&quot; title=&quot;10. In the new XIB file drag in a button and link to the IBAction by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;10. In the new XIB file drag in a button and link to the IBAction&quot; src=&quot;http://farm3.static.flickr.com/2410/5736724448_2acb8f01d9.jpg&quot; height=&quot;330&quot; width=&quot;500&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;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;8. You&#39;re done!&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
All thats left now is the build and run (Command-R). &amp;nbsp;Select the second tab to see your new Navigation Controller and click the button to see it in action.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/5736174293/&quot; title=&quot;8. Build and run the simulator. Press the button in the second bar item to try it out by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;8. Build and run the simulator. Press the button in the second bar item to try it out&quot; src=&quot;http://farm6.static.flickr.com/5026/5736174293_2954ca27da.jpg&quot; height=&quot;400&quot; width=&quot;205&quot; /&gt;&lt;/a&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;a href=&quot;http://www.flickr.com/photos/39069226@N03/5736724518/&quot; title=&quot;8b. It works by WillWinder, on Flickr&quot;&gt;&lt;img alt=&quot;8b. It works&quot; src=&quot;http://farm6.static.flickr.com/5150/5736724518_b9bab084e2.jpg&quot; height=&quot;400&quot; width=&quot;204&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
</description><link>http://blog.willwinder.com/2011/05/xcode4-uitabbarcontroller-with.html</link><author>noreply@blogger.com (Will Winder)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm4.static.flickr.com/3250/5736724110_0cbb4231ce_t.jpg" height="72" width="72"/><thr:total>27</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-5642217914464955253</guid><pubDate>Tue, 05 Apr 2011 15:00:00 +0000</pubDate><atom:updated>2011-04-05T11:00:36.860-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Programming</category><category domain="http://www.blogger.com/atom/ns#">vim</category><title>Vim Search for Tags File</title><description>I use ctags with vim. This works great when the tags file is in the same directory as the files I&#39;m working on, but is a chore when projects have nested directories.&lt;br /&gt;
&lt;br /&gt;
Add this to your .vimrc file and the recursive search up the directory structure will be done for the tags file:&lt;br /&gt;
&lt;blockquote&gt;set tags=tags;/&lt;/blockquote&gt;</description><link>http://blog.willwinder.com/2011/04/vim-search-for-tags-file.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-9138744375799431562</guid><pubDate>Sat, 08 Jan 2011 01:57:00 +0000</pubDate><atom:updated>2011-01-07T20:57:39.024-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">iphone</category><category domain="http://www.blogger.com/atom/ns#">projects</category><title>Why Hydrate Yourself?</title><description>&lt;b&gt;This is a short essay about why I decided to create &quot;&lt;a href=&quot;http://www.hydrate-yourself.com/&quot;&gt;Hydrate Yourself&lt;/a&gt;&quot;. It is a free app for the iPhone, iPod touch and iPod which tracks water intake and helps encourage a healthier lifestyle.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
According to webmd.com [&lt;a href=&quot;http://www.webmd.com/a-to-z-guides/dehydration-adults&quot;&gt;1&lt;/a&gt;] and the US National Library of Medicine [&lt;span id=&quot;goog_531015673&quot;&gt;&lt;/span&gt;&lt;span id=&quot;goog_531015674&quot;&gt;&lt;/span&gt;&lt;a href=&quot;http://www.nlm.nih.gov/medlineplus/ency/article/000982.htm&quot;&gt;2&lt;/a&gt;], symptoms of dehydration include: Dizziness, Lethargy, Lightheadedness, Headaches, Fainting, Weakness and (naturally) a Dry mouth. Other symptoms even include heart palpitations (that feeling of your heart jumping or pounding).&lt;br /&gt;
&lt;br /&gt;
So why is it that water intake is such a secondary concern to so many adults? All it takes is a little attention throughout the day, such as keeping track of how much water you are drinking or carrying a water bottle with you at all times. I strongly believe that drinking enough water is one of the easiest ways to improve your health. But how much water is enough?&lt;br /&gt;
&lt;br /&gt;
Its hard to say, since so many foods we eat every day contain water. Common &quot;rules of thumb&quot; include 8-10 glasses a day, or your body weight divided by two. In reality it will always vary from person to person depending on their lifestyle. For example if you drink a lot of diuretics, like coffee or caffeinated sodas, you will retain some of that water but not all, which skews how much you need to drink. Another factor is your personal activity levels, anything which causes you to sweat causes you to lose more water which needs to be replenished.&lt;br /&gt;
&lt;br /&gt;
One of the most accurate litmus tests for determining whether you should drink more water or not is the color of your urine [&lt;a href=&quot;http://www.urinecolors.com/dehydration.php&quot;&gt;3&lt;/a&gt;]. The web page &lt;a href=&quot;http://www.urinecolors.com/dehydration.php&quot;&gt;urinecolors.com&lt;/a&gt; has a color chart stating that if your urine is light orange or darker that you need to drink water immediately.&lt;br /&gt;
&lt;br /&gt;
With all of that known it is easy to see why you should make an effort drink enough water. I spent the last three months of 2010 creating a program to help with just that. After being unsatisfied with the alternative water tracking applications I designed &quot;&lt;a href=&quot;http://www.hydrate-yourself.com/&quot;&gt;Hydrate Yourself&lt;/a&gt;&quot; from the ground up. There were three key features that needed to be done right: Easy to use multiple containers, multiple calculators for recommended intake, reminders throughout the day to drink more water.&lt;br /&gt;
&lt;br /&gt;
I added two&amp;nbsp;daily intake calculators&amp;nbsp;and an option to enter a custom amount. These calculations can then be used or customized to fit your ideal intake amount. There is a simple calculator that is based off just your weight and basic activity levels (low or high), then there is a complex calculator which is based on your weight, daily minutes of activity, climate, illnesses and more. Of course you can also skip all that and enter a direct amount - maybe your doctor made a recommendation.&lt;br /&gt;
&lt;br /&gt;
A problem with the alternatives is that it can be difficult to use multiple containers, or modify them on the fly. Taking this into account I added a configurable spot on the main page for three containers (such as a water bottle, your favorite glass, and a drinking fountain). From there you can just tap the icon and have it logged or press and hold to get extra options - add a partial serving (half a water bottle), change the default portion size (a new waterbottle holds 32oz instead of 20oz) or add a serving that you forgot to enter earlier (I often forget to add my first glass in the morning).&lt;br /&gt;
&lt;br /&gt;
Perhaps the most useful feature is the ability to add customizable reminders. The hardest part about drinking water throughout the day is remembering to drink it at all. To help with that I added a reminder. It goes off a customizable amount of time after you add a portion, or finish using the app. If it goes off I know that its time to get myself some more water.&lt;br /&gt;
&lt;br /&gt;
[1] http://www.webmd.com/a-to-z-guides/dehydration-adults&lt;br /&gt;
[2] http://www.nlm.nih.gov/medlineplus/ency/article/000982.htm&lt;br /&gt;
[3] http://www.urinecolors.com/dehydration.php</description><link>http://blog.willwinder.com/2011/01/why-hydrate-yourself.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-7975270669684682920</guid><pubDate>Thu, 02 Dec 2010 23:32:00 +0000</pubDate><atom:updated>2017-08-10T11:40:35.463-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">iphone</category><category domain="http://www.blogger.com/atom/ns#">Programming</category><title>Fetching a large data set with Core-Data</title><description>I needed loop over a potentially large set of data stored in Core Data, but did not want to force the system to load the entire data set in memory or have Core Data constantly process faults. There seem to be several ways to do this, some easier than others. One robust way would be to use an &lt;a href=&quot;http://www.raywenderlich.com/999/core-data-tutorial-how-to-use-nsfetchedresultscontroller&quot;&gt;NSFetchedResultController&lt;/a&gt;, but it looked a little more complex than I wanted to deal with ( and involves implementing yet another protocol).&lt;br /&gt;
&lt;br /&gt;
After poking around the NSFetchResult class page for a while I noticed these methods: &lt;span class=&quot;Apple-style-span&quot; style=&quot;color: #38761d;&quot;&gt;setFetchLimit&lt;/span&gt; and &lt;span class=&quot;Apple-style-span&quot; style=&quot;color: #38761d;&quot;&gt;setFetchOffset&lt;/span&gt;. It turns out that these are just like the LIMIT and OFFSET keywords in SQL, which is just what I was looking for.&lt;br /&gt;
&lt;br /&gt;
Using this method is just a few extra lines of code. This is what it looks like:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/726312.js&quot;&gt; &lt;/script&gt;</description><link>http://blog.willwinder.com/2010/12/fetching-large-data-set-with-core-data.html</link><author>noreply@blogger.com (Will Winder)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4187587304957081274.post-6768070523564825785</guid><pubDate>Thu, 18 Nov 2010 14:28:00 +0000</pubDate><atom:updated>2011-06-04T15:04:27.832-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">iphone</category><category domain="http://www.blogger.com/atom/ns#">Programming</category><title>Custom UISwitch to show YES / NO</title><description>You can see this in action in my free water tracking application &quot;&lt;a href=&quot;http://itunes.apple.com/app/hydrateyourself/id392649838?mt=8&quot;&gt;Hydrate Yourself&lt;/a&gt;&quot;. If this helped you please consider downloading it and leaving a review.&lt;br /&gt;
&lt;br /&gt;
One of the great things about iPhone development are that there are lots of user interface pieces that are easy to put together and customize. Unfortunately there are also a lot of pieces which don&#39;t have all the basic customizations that may people want. The UISwitch is one of those.&lt;br /&gt;
&lt;br /&gt;
Here is the standard UISwitch:&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9n4JTHLqaiRyB3-K-1UyYszPfWRImC_ZaYgqtGn3Gf8gKJE3Mtqv6ZbTTj32JzzNYm7gFpKWuB_DuRJuAv47RNr7bGkk05JBley1FVWFw8_AMmHxQco9AsTNkLe4CNozMISYR7qpJSU6M/s1600/UISwitch.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/AVvXsEh9n4JTHLqaiRyB3-K-1UyYszPfWRImC_ZaYgqtGn3Gf8gKJE3Mtqv6ZbTTj32JzzNYm7gFpKWuB_DuRJuAv47RNr7bGkk05JBley1FVWFw8_AMmHxQco9AsTNkLe4CNozMISYR7qpJSU6M/s1600/UISwitch.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Its size, colors, and ON / OFF text are not changeable. Fortunately there are several great re-implementations (and a few hacks that use restricted API calls) to modify all this. One that I am using is the &lt;a href=&quot;http://osiris.laya.com/projects/rcswitch/&quot;&gt;RCSwitch class available here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
I made two small changes to this code, first I renamed his RCSwitchOnOff class to RCSwitchYesNo and changed the labels to YES / NO. Second I added an init method to the RCSwitch.m base class which called the usual init function but provided the default frame size. This works fine since I am positioning the x and y coordinates using the &quot;center&quot; property:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/705027.js&quot;&gt;
&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
The final result looks and behaves just like the original UISwitch, but is actually completely rewritten using the UIControl class. My switches now look like this:&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/AVvXsEjUXq5U7knct12_GBxOmn9CTuQgM_5-UpP3kCTvFS1-N2ZYQ4PP5URdXRXPdKo13mnnjOixzT5T6bL6C6fMJQpbpBbI4eL52r-89Nuhyphenhyphena9XXYQ57TNo7Hy2AclU2VQoDJGrd5ZLx7rnbeKn/s1600/yesno.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/AVvXsEjUXq5U7knct12_GBxOmn9CTuQgM_5-UpP3kCTvFS1-N2ZYQ4PP5URdXRXPdKo13mnnjOixzT5T6bL6C6fMJQpbpBbI4eL52r-89Nuhyphenhyphena9XXYQ57TNo7Hy2AclU2VQoDJGrd5ZLx7rnbeKn/s1600/yesno.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;</description><link>http://blog.willwinder.com/2010/11/custom-uiswitch-to-show-yes-no.html</link><author>noreply@blogger.com (Will Winder)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9n4JTHLqaiRyB3-K-1UyYszPfWRImC_ZaYgqtGn3Gf8gKJE3Mtqv6ZbTTj32JzzNYm7gFpKWuB_DuRJuAv47RNr7bGkk05JBley1FVWFw8_AMmHxQco9AsTNkLe4CNozMISYR7qpJSU6M/s72-c/UISwitch.png" height="72" width="72"/><thr:total>1</thr:total></item></channel></rss>