Saturday, July 27, 2013

Rails helpers

I have a task to fix a conversion bug in a Rails app (2.12), then I found out that there are copies of the same method in everywhere - that's so bad. So I decided to clean it up by making a common helper that provides common methods for global usage, like below example:

 module UtilsHelper  
  def convertValue(value)  
   return "0" if value.nil?  
   return (value*2).to_s  
  end  
 end  

Google around, finally get it to run:

In the Controller, use helper method to include UtilsHelper to use in the template:
 class HomeController < ApplicationController  
  helper UtilsHelper #include won't work  
 end  

index.rhtml template:
 <%= convertValue(row.value) %>  

But in a class, we must use include method
 class Data  
  include UtilsHelper #require won't work  
  @value = nil  
  def to_s  
   return convertValue(@value)  
  end  
 end  

Thursday, July 25, 2013

Installing MySQL and gem on Snow Leopard 10.6.8

When I start an old Rails app with Ruby 1.8.7, Rails 2.1.2 on my MacBook laptop, I got error with MySQL connection, check the log:

WARNING: You're using the Ruby-based MySQL library that ships with Rails. This library is not suited for production. Please install the C-based MySQL library instead (gem install mysql).

Before that, I had installed mysql-5.5.21-osx10.6-x86.dmg
I tried several ways to fix that like gem install mysql2 or sudo env ARCHFLAGS="-arch i386" gem install mysql --with-mysql-dir=/usr/local/mysql --with-mysql-lib=/usr/local/mysql/lib   --with-mysql-include=/usr/local/mysql/include  --with-mysql-config=/usr/local/mysql/bin/mysql_config
...

But it still didn't work. So I decided to remove MySQL and install it from source as others suggested.

Remove MySQL
- First, backup databases.
- Stop MySQL (then check & kill exist MySQL processes)
- Follow instructions
sudo rm /usr/local/mysql
sudo rm -rf /usr/local/mysql*
sudo rm -rf /Library/StartupItems/MySQLCOM
sudo rm -rf /Library/PreferencePanes/My*
edit /etc/hostconfig and remove the line MYSQLCOM=-YES-
rm -rf ~/Library/PreferencePanes/My*
sudo rm -rf /Library/Receipts/mysql*
sudo rm -rf /Library/Receipts/MySQL*
sudo rm -rf /private/var/db/receipts/*mysql*

Download & install MySQL
Follow instructions
(Note that the mysql 5.5 use cmake to configure, so just need 5.1 for now)

cd ~/Downloads
curl -O http://mysql.mirrors.pair.com/Downloads/MySQL-5.1/mysql-5.1.70.tar.gz
tar xvzf mysql-5.1.70.tar.gz
cd mysql-5.1.70
./configure --prefix=/usr/local/mysql --with-extra-charsets=complex \
--enable-thread-safe-client --enable-local-infile --enable-shared \
--with-plugins=innobase
make
sudo make install
cd /usr/local/mysql
sudo chown -R mysql ./var

sudo ./bin/mysql_install_db --user=mysql

#error!
Installing MySQL system tables...
ERROR: 1004  Can't create file '/var/tmp/#sql1599d_1_0.frm' (errno: 9)
130725  8:00:57 [ERROR] Aborting

130725  8:00:57 [Note] /usr/local/mysql/libexec/mysqld: Shutdown complete

#fix error
#clean up data, must! - if not, then the mysql table is still empty, can't login as root
sudo rm -Rf var
#create tmp dir
sudo mkdir -p var/tmp
sudo chown -R mysql var
sudo ./bin/mysql_install_db --user=mysql --tmpdir=/usr/local/mysql/var/tmp
sudo /usr/local/mysql/libexec/mysqld --skip-grant --user=mysql
(checking:
mysql -uroot
mysql > use mysql;
mysql > select * from user;
mysql > exit)

sudo /usr/local/mysql/bin/mysqladmin -u root shutdown

ps aux | grep mysql
kill all exist mysql processes

sudo /usr/local/mysql/bin/mysqld_safe --user=mysql &

sudo /usr/local/mysql/bin/mysqladmin -u root password 'rootXYZ0'

sudo /usr/local/mysql/bin/mysqladmin -u root -p shutdown

Starting (and Auto-Starting) MySQL

Because the com.mysql.mysqld.plist link from hivelogic.com died, so I found another

echo "<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>com.mysql.mysqld</string>
<key>Program</key>
<string>/usr/local/mysql/bin/mysqld_safe</string>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>mysql</string>
<key>WorkingDirectory</key>
<string>/usr/local/mysql</string>
</dict>
</plist>" >> ~/com.mysql.mysqld.plist

sudo mv ~/com.mysql.mysqld.plist /Library/LaunchDaemons/com.mysql.mysqld.plist

sudo chown root /Library/LaunchDaemons/com.mysql.mysqld.plist

#start
sudo launchctl load -w /Library/LaunchDaemons/com.mysql.mysqld.plist

(#stop
sudo launchctl unload -w /Library/LaunchDaemons/com.mysql.mysqld.plist)

Gem install
gem list
gem uninstall mysql
env ARCHFLAGS="-arch i386" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config

Note: don't use sudo env ARCHFLAGS="-arch i386" gem install mysql... That will not work


Add path/alias to ~/.bash_profile
export PATH=$PATH:/usr/local/mysql/bin
export DYLD_LIBRARY_PATH=/usr/local/mysql/lib:$DYLD_LIBRARY_PATH
alias mysqlstart="sudo launchctl load -w /Library/LaunchDaemons/com.mysql.mysqld.plist"
alias mysqlstop="sudo launchctl unload -w /Library/LaunchDaemons/com.mysql.mysqld.plist"
alias mysqlstatus="ps aux | grep mysql | grep -v grep"

Launch app
cd ~/rails/myapp
ruby script/server

Wednesday, July 24, 2013

Horizontal List View with scrollbar

To display list of thumbnail images horizontally on the Android, two images per screen, we use Horizontal ListView from DevsmartLib, but the client would like to show up horizontal scrollbar and position indicator also.

Because the lib doesn't support those features, so we must implement them.

To display the position indicator when scrolling, I add function displayPosition() at the bottom of the onLayout() method:

 @Override   
 protected synchronized void onLayout(boolean changed, int left, int top,    
       int right, int bottom) {   
      ....   
      displayPosition();   
 }   
 private int childWidth=0;  
 private void displayPosition() {   
      String counter = "";  
      if (getChildCount()>0 && mAdapter.getCount()>2) {  
           if (childWidth==0) {  
                childWidth = getChildAt(0).getMeasuredWidth();  
           }  
           double positionX = (double)mCurrentX/childWidth;  
           double fractional = positionX % 1;  
           int position = 1 + (int)positionX;  
           if (fractional > 0.3) position++;  
           counter = String.format(" - %s/%s",position,mAdapter.getCount());  
      }  
      ((Activity) this.getContext()).setTitle("List"+counter);  
 }    
}
To display horizontal scrollbar, it's not easy because it's hard to find examples or good reference, have to google a lot!

Finally found out this, so just implement necessary functions:

 public HorizontalListView(Context context, AttributeSet attrs) {   
   TypedArray a = context.obtainStyledAttributes(R.styleable.View);   
   initializeScrollbars(a);   
   a.recycle();   
   setWillNotDraw(false);   
   setScrollbarFadingEnabled(false);   
   setHorizontalScrollBarEnabled(true);   
 }   
 @Override   
 protected int computeHorizontalScrollOffset() {   
      return this.mCurrentX;   
 }   
 @Override  
 protected int computeHorizontalScrollRange() {  
      return mAdapter.getCount()<3?0:(mAdapter.getCount()*childWidth);  
 }  

The shorten attrs.xml as below:

 <?xml version="1.0" encoding="utf-8"?>  
 <resources>  
 <declare-styleable name="View">  
 <attr name="android:scrollX"/>  
 <attr name="android:scrollY"/>  
 <attr name="android:scrollbarAlwaysDrawHorizontalTrack"/>  
 <attr name="android:scrollbarAlwaysDrawVerticalTrack"/>  
 <attr name="android:scrollbarDefaultDelayBeforeFade"/>  
 <attr name="android:scrollbarFadeDuration"/>  
 <attr name="android:scrollbarSize"/>  
 <attr name="android:scrollbarStyle"/>  
 <attr name="android:scrollbarThumbHorizontal"/>  
 <attr name="android:scrollbarThumbVertical"/>  
 <attr name="android:scrollbarTrackHorizontal"/>  
 <attr name="android:scrollbarTrackVertical"/>  
 <attr name="android:scrollbars"/>    
 </declare-styleable>  
 </resources>  

Note: It's nice to view code in blogger with codeformatter :). Thank you.

Saturday, July 20, 2013

Calculate Google Map zoom level on Android

On Android, Google Map API doesn't have function to export the map to image file directly. But the Google provides Static Maps API to render map into image via http request. An issue comes up when we need to show all locations on the map, so we have to supply the zoom level parameter.

If the map is rendering on the GoogleMap, it's easy to get the center and zoom of the boundary with the code:

 final LatLngBounds.Builder builder = new LatLngBounds.Builder();  
 for (LatLng latlng : list) {  
   builder.include(latlng);   
 }  
 mMap.setOnCameraChangeListener(new OnCameraChangeListener() {  
   @Override  
   public void onCameraChange(CameraPosition arg0) {  
     // Move camera.  
     mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 10));  
     // Remove listener to prevent position reset on camera move.  
     mMap.setOnCameraChangeListener(null);  
     LatLng center = mMap.getCameraPosition().target;  
     float zoom = mMap.getCameraPosition().zoom;  
   }  
 });  

But when we need to export by batch or from the server side, we won't load each map data onto the map view to execute above code. So I find a way to calculate them by formula.

The center point is simple to do:

 final LatLngBounds bounds = builder.build();  
 LatLng ne = bounds.northeast;  
 LatLng sw = bounds.southwest;  
 LatLng center = new LatLng((ne.latitude + sw.latitude)/2,  
             (ne.longitude + sw.longitude)/2);  

There are several ways to calculate the zoom level, but I find this code in Javascript by John S is more accurate (I convert them into java code)

 public static int getBoundsZoomLevel(LatLng northeast,LatLng southwest,   
                   int width, int height) {  
   final int GLOBE_WIDTH = 256; // a constant in Google's map projection  
   final int ZOOM_MAX = 21;  
   double latFraction = (latRad(northeast.latitude) - latRad(southwest.latitude)) / Math.PI;  
   double lngDiff = northeast.longitude - southwest.longitude;  
   double lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;  
   double latZoom = zoom(height, GLOBE_WIDTH, latFraction);  
   double lngZoom = zoom(width, GLOBE_WIDTH, lngFraction);  
   double zoom = Math.min(Math.min(latZoom, lngZoom),ZOOM_MAX);  
   return (int)(zoom);  
 }  
 private static double latRad(double lat) {  
   double sin = Math.sin(lat * Math.PI / 180);  
   double radX2 = Math.log((1 + sin) / (1 - sin)) / 2;  
   return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;  
 }  
 private static double zoom(double mapPx, double worldPx, double fraction) {  
   final double LN2 = .693147180559945309417;  
   return (Math.log(mapPx / worldPx / fraction) / LN2);  
 }  




Wednesday, July 17, 2013

Network Solutions down

Got up early in the morning, then checked my company emails on the iPhone and got connection error, it's around 6:15 AM. Didn't get any Nagios notification emails about the mail server problem. Open the laptop and can't SSH to the mail server and Nagios server either, that's weird. nslookup them, could not resolve IPs! Login into the Network Solutions which hosts our domain names, it was hanging forever, seems to be down. Got back to SSH to servers via IP addresses, they worked. That's why the Nagios was still working well. So checked around other name servers, some still cached while other not.

Went to the office at 8 and found just mail works with one of our Comcast DNS server, 75.75.76.76, other hosts came up lately. The http://networksolutions.com was completely down. It just came back up around 10:30. Some DNS servers like Comcast 75.75.75.75, Google 8.8.8.8, 8.8.4.4 still can't query our hosts.

Find out solution for this issue in the future. We'll definitely need secondary name servers from other providers!

Here is additional information about the Network Solutions's problem:
http://blogs.cisco.com/security/network-solutions-customer-site-compromises-and-ddos/
http://blogs.cisco.com/security/hijacking-of-dns-records-from-network-solutions/

Tuesday, July 16, 2013

ISAPI Tomcat redirector on IIS 8 of Windows Server 2012

A client wants to migrate a J2EE app running on Tomcat and IIS 6 of Windows 2003 to IIS 8 of Windows Server 2012.

This is the first time I have a chance to work with Windows Server 2012!

After login via RDP I noticed the GUI looks like Windows 8 as I had a little bit experience to play with it when buying and setup an Asus touchscreen laptop for my brother.

I don't have any problems to copy files between the old server to this server and setup Java, Tomcat, MySQL, PostgreSQL, PostGIS... Except when start the Tomcat 5.5, it throws errors because the Java is 64 bit, so download Java 32 bit and reinstall it.

And to start Tomcat, we need to copy %JAVA_HOME%\bin\msvcr71.dll to Tomcat\bin folder.

Another issue is setup the Tomcat to trust SSL certificate from a https website that the J2EE app has request connection to it. Just copy the old jssecacerts file to "%JAVA_HOME%\lib\security\

But I want to test it by the command line first, just hit the shortcut Windows PowerShell on the launch bar, it open the console window looks like the MS-Dos window, except blue background, then execute command:
java -Djavax.net.ssl.trustStore="%JAVA_HOME%\lib\security\jssecacerts" myTest

And got the error:

Exception in thread "main" java.lang.NoClassDefFoundError:/net/ssl/trustStore=%JAVA_HOME%\lib\security\jssecacerts
Caused by: java.lang.ClassNotFoundException: .net.ssl.trustStore=%JAVA_HOME%\lib\security\jssecacerts
....

Don't know why, check/test everything is fine, the command run well on the old server. Try to add class path for the net/ssl.... Didn't work! Crazy about this.

Finally I thought the Windows PowerShell is different MS-Dos, so try to find a way to exec cmd. Don't know where the MS-Dos shortcut is, have to google again. Then Search > Run, cmd, enter the above command, it worked!
(MS hide everything. Please switch back to old UI, this is server, not tablet!, I hate it so much when access via RDP and it's hard to open Start menu, drag/drop shortcuts to desktop, show all apps links..., finally when I wan't to logout, I can't find the menu -> have to google again!!!)

Then Tomcat & J2EE app works fine.

Then I start to install ISAPI Tomcat redirector on IIS. My old instruction for Windows 2003 is there, but the IIS 8 administration GUI is totally different.

The first thing to do is define the Tomcat ISAPI Filter. Wow, there's no ISAPI Filters menu for the website! Googling around and know that we need to install ISAPI extensions and ISAPI Filters first in the Add Roles and Features of Server Roles as below screenshot

Then back to the IIS Admin and select the server, we'll see the ISAPI and CGI Restrictions

Then open it and Add... with isapi_redirect.dll path, make sure it's 64 bit version!



Then select the website and configure ISAPI Filters for it



 Then create virtual directory named jakarta with physical path is the folder contains isapi_redirect.dll


Then allow Execute permission on it


Create jsp-examples virtual path points to any folder, like IIShome

Don't need to change the uriworkermap.properties and workers.properties.

That's it. Hope help someone to work with this issue.




Friday, July 12, 2013

Git clients and free private hosting repositories

When working with Git for puppet management and some private projects, I used eGit plugin in Geppetto and Eclipse but usually got frustrated [rejected - non-fast-forward] error when push code. Searched  around and tried some configuration but it didn't work well. Sometimes it even got wrong status between committed/pushed... So finally I switched to git command.

Until one day I found out free powerful Git and Mercurial client SourceTree that has neat GUI and easy to use.

Thought it's free we'll need to register it. Let's give it a shot, you'll like it soon.

Another issue is I used Assembla to host code free for some private projects in Git. After few months stop working with it, then backed to make a new Git project, I couldn't find the creating free Git repository link anymore. Search around and found out it stopped free plan for new projects!

So just find another one, Bitbucket is free for 5 users with issue tracker, wiki features, "clean UI", easier to use, and cool function is the import function so I could transfer source code from Assembla to Bitbucket. Very happy for this change.

Editra text editor

After over a year switching to use a MacBookPro I still missed some very useful free tools from Windows such as Notepad++, Winmerge, FreeCommander. I tried to use alternatives such as TextWrangler, and use WineBottler to build Notepad++, Winmerge, FreeCommander to run with Wine on Mac. Notepad++ and Winmerge work well except FreeCommander has problem with copy/paste.. So I find out DoubleCommander to replace FreeCommander although it neither has tree view or correct sort by folders.
But when working with Windows apps, I have to use Windows shortcuts like Ctr+C, Ctr+V instead MacOSx shortcuts and it isn't comfortable to remember to use correct shortcuts when switching between MacOSx apps & Windows apps.

Then I search around for a better text editor with projects manager or file browser. I don't like Komodo Edit because it's heavy. gedit is good with Favorites and File browser plugins but the Project Manager plugin didn't work. Fraise likes TextWrangler.

Finally I found Editra free editor with both features projects manager and file browser. Now its version is only 0.7.20 but it has a lot features - Toggle Comment, Code Folding, Bracket Highlight (doesn't work!?), Highlight Caret line, Bookmarks.., even better than Notepad++ (see more at http://editra.org/preview). The Find function is so convenient when it keep staying on the bottom of the window and continue to search when we repeat enter (I hate TextWrangler Find function when it closes the window right after the first enter - we have to use mouse click to keep it continue!). Some disadvantages are there no new .js file menu in the project context menu) although we can create a html file then rename it) or Code Browser plugin doesn't understand .js file. The text color schemes are not very good looking also, so we can spend sometime to customize it with Style Editor function.

Another good editor is Brackets Sprint editor with bunch of plugins.

But Editra is far more than I expected and I love it so much :)