Wednesday August 12, 2009
TOTD #92: Session Failover for Rails applications running on GlassFish
The GlassFish
High Availability
allows to setup a cluster of GlassFish instances and achieve highly
scalable architecture using in-memory session state replication. This
cluster can be very
easily created and tested using the "clusterjsp" sample
bundled with GlassFish. Here are some clustering related entries
published on this blog so far:
| ~/samples/jruby/session >~/tools/jruby/bin/jruby
script/generate controller home index JRuby limited openssl loaded. gem install jruby-openssl for full support. http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL exists app/controllers/ exists app/helpers/ create app/views/home exists test/functional/ create test/unit/helpers/ create app/controllers/home_controller.rb create test/functional/home_controller_test.rb create app/helpers/home_helper.rb create test/unit/helpers/home_helper_test.rb create app/views/home/index.html.erb |
| class HomeController < ApplicationController include Java def index @server_served = servlet_request.get_server_name @port = servlet_request.get_server_port @instance = java.lang.System.get_property "com.sun.aas.instanceName" @server_executed = java.net.InetAddress.get_local_host().get_host_name() @ip = java.net.InetAddress.get_local_host().get_host_address @session_id = servlet_request.session.get_id @session_created = servlet_request.session.get_creation_time @session_last_accessed = servlet_request.session.get_last_accessed_time @session_inactive = servlet_request.session.get_max_inactive_interval if (params[:name] != nil) servlet_request.session[params[:name]] = params[:value] end @session_values = "" value_names = servlet_request.session.get_attribute_names unless (value_names.has_more_elements) @session_values = "<br>No parameter entered for this request" else @session_values << "<UL>" while (value_names.has_more_elements) param = value_names.next_element unless (param.starts_with?("__")) value = servlet_request.session.get_attribute(param) @session_values << "<LI>" + param + " = " + value + "</LI>" end end @session_values << "</UL>" end end def adddata servlet_request.session.set_attribute(params[:name], params[:value]) render :action => "index" end def cleardata servlet_request.session.invalidate render :action => "index" end end |
| <h1>Home#index</h1> <p>Find me in app/views/home/index.html.erb</p> <B>HttpSession Information:</B> <UL> <LI>Served From Server: <b><%= @server_served %></b></LI> <LI>Server Port Number: <b><%= @port %></b></LI> <LI>Executed From Server: <b><%= @server_executed %></b></LI> <LI>Served From Server instance: <b><%= @instance %></b></LI> <LI>Executed Server IP Address: <b><%= @ip %></b></LI> <LI>Session ID: <b><%= @session_id %></b></LI> <LI>Session Created: <%= @session_created %></LI> <LI>Last Accessed: <%= @session_last_accessed %></LI> <LI>Session will go inactive in <b><%= @session_inactive %> seconds</b></LI> </UL> <BR> <% form_tag "/session/home/index" do %> <label for="name">Name of Session Attribute:</label> <%= text_field_tag :name, params[:name] %><br> <label for="value">Value of Session Attribute:</label> <%= text_field_tag :value, params[:value] %><br> <%= submit_tag "Add Session Data" %> <% end %> <% form_tag "/session/home/cleardata" do %> <%= submit_tag "Clear Session Data" %> <% end %> <% form_tag "/session/home/index" do %> <%= submit_tag "Reload Page" %> <% end %> <BR> <B>Data retrieved from the HttpSession: </B> <%= @session_values %> |
| ~/samples/jruby/session >~/tools/jruby/bin/jruby -S warble
war:webxml mkdir -p tmp/war/WEB-INF ~/samples/jruby/session >cp tmp/war/WEB-INF/web.xml config/ |
| <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> |
| <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> |
| <distributable/> |
| ~/samples/jruby/session >~/tools/jruby/bin/jruby -S warble mkdir -p tmp/war/WEB-INF/gems/specifications cp /Users/arungupta/tools/jruby-1.3.0/lib/ruby/gems/1.8/specifications/rails-2.3.2.gemspec tmp/war/WEB-INF/gems/specifications/rails-2.3.2.gemspec . . . mkdir -p tmp/war/WEB-INF cp config/web.xml tmp/war/WEB-INF jar cf session.war -C tmp/war . |
| ~/samples/jruby/session >asadmin deploy --target wines --port 5048 --availabilityenabled=true session.war |








Posted by Arun Gupta in General | Comments[2]
|
|
|
|
|
Tuesday June 16, 2009
TOTD #84: Using Apache + mod_proxy_balancer to load balance Ruby-on-Rails running on GlassFish
TOTD
#81 explained how to install/configure nginx for
load-balancing/front-ending a cluster of Rails application running on GlassFish
Gem. Another popular approach in the Rails community is to
use Apache HTTPD
+ mod_proxy_balancer.
A user asked the exact details of this
setup on the GlassFish
Gem Forum. This Tip
Of The Day (TOTD) will
clearly explain the steps.
| LoadModule proxy_balancer_module libexec/apache2/mod_proxy_balancer.so |
| <Proxy balancer://glassfishgem> BalancerMember http://localhost:3000 BalancerMember http://localhost:3001 BalancerMember http://localhost:3002 </Proxy> |
| ProxyPass / balancer://glassfishgem/ CustomLog /var/log/glassfishgem.log/apache_access_log combined |
| CustomLog /var/log/glassfishgem.log/apache_access_log combined |
| ~/tools/jruby/rails/runner >../../bin/jruby -S glassfish -e
production -c myapp Starting GlassFish server at: 10.0.177.178:3000 in production environment... Writing log messages to: /Users/arungupta/tools/jruby-1.3.0/rails/runner/log/production.log. Press Ctrl+C to stop. . . . ~/tools/jruby/rails/runner >../../bin/jruby -S glassfish -e production -c myapp -p 3001 Starting GlassFish server at: 10.0.177.178:3001 in production environment... Writing log messages to: /Users/arungupta/tools/jruby-1.3.0/rails/runner/log/production.log. Press Ctrl+C to stop. . . . ~/tools/jruby/rails/runner >../../bin/jruby -S glassfish -e production -c myapp -p 3002 Starting GlassFish server at: 10.0.177.178:3002 in production environment... Writing log messages to: /Users/arungupta/tools/jruby-1.3.0/rails/runner/log/production.log. Press Ctrl+C to stop. |
| ProxyPass /myapp/
balancer://glassfishgem/myapp/ |
| LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{BALANCER_WORKER_NAME}e\"" custom |
| CustomLog /var/log/glassfishgem.com/apache_access_log custom |
| ::1 - - [17/Jun/2009:10:53:53 -0700] "GET /runlogs
HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5;
en-US; rv:1.9.0.11) Gecko/2009060214 Firefox/3.0.11"
"http://localhost:3002" ::1 - - [17/Jun/2009:10:54:04 -0700] "GET /runlogs HTTP/1.1" 200 621 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.11) Gecko/2009060214 Firefox/3.0.11" "http://localhost:3000" ::1 - - [17/Jun/2009:10:54:05 -0700] "GET /runlogs HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.11) Gecko/2009060214 Firefox/3.0.11" "http://localhost:3001" |
Posted by Arun Gupta in web2.0 | Comments[4]
|
|
|
|
|
Thursday April 30, 2009
TOTD #81: How to use nginx to load balance a cluster of GlassFish Gem ?
![]() |
nginx (pronounced as "engine-ex") is an open-source and high-performance HTTP server. It provides the common features such as reverse proxying with caching, load balancing, modular architecture using filters (gzipping, chunked responses, etc), virtual servers, flexible configuration and much more. |
| ~/tools
> curl -L -O http://sysoev.ru/nginx/nginx-0.6.36.tar.gz ~/tools > tar -xzf nginx-0.6.36.tar.gz ~/tools > curl -L -O http://downloads.sourceforge.net/pcre/pcre-7.7.tar.gz ~/tools > tar -xzf pcre-7.7.tar.gz ~/tools/nginx-0.6.36 > ./configure --prefix=/usr/local/nginx --sbin-path=/usr/sbin --with-debug --with-http_ssl_module --with-pcre=../pcre-7.7 ~/tools/nginx-0.6.36 > make ~/tools/nginx-0.6.36 > sudo make install ~/tools/nginx-0.6.36 > which nginx /usr/sbin/nginx |

| ~/samples/jruby >~/tools/jruby/bin/jruby -S rails
runner ~/samples/jruby/runner >~/tools/jruby/bin/jruby script/generate scaffold runlog miles:float minutes:integer ~/samples/jruby/runner >sed s/'adapter: sqlite3'/'adapter: jdbcsqlite3'/ <config/database.yml >config/database.yml.new ~/samples/jruby/runner >mv config/database.yml.new config/database.yml ~/samples/jruby/runner >~/tools/jruby/bin/jruby -S rake db:migrate |
| ~/samples/jruby/runner >~/tools/jruby/bin/jruby -S
glassfish Starting GlassFish server at: 192.168.1.145:3000 in development environment... Writing log messages to: /Users/arungupta/samples/jruby/runner/log/development.log. Press Ctrl+C to stop. |
| ~/samples/jruby/runner >~/tools/jruby/bin/jruby -S
glassfish -p 3001 Starting GlassFish server at: 192.168.1.145:3001 in development environment... Writing log messages to: /Users/arungupta/samples/jruby/runner/log/development.log. Press Ctrl+C to stop. |
| ~/samples/jruby/runner >~/tools/jruby/bin/jruby -S
glassfish -p 3002 Starting GlassFish server at: 192.168.1.145:3002 in development environment... Writing log messages to: /Users/arungupta/samples/jruby/runner/log/development.log. Press Ctrl+C to stop. |
| upstream
glassfish { server 127.0.0.1:3000; server 127.0.0.1:3001; server 127.0.0.1:3002; } |

| proxy_pass http://glassfish; |

| sudo
kill -15 `cat /usr/local/nginx/logs/nginx.pid` sudo nginx |

| log_format
main '$remote_addr - [$upstream_addr]
$remote_user [$time_local] $request ' '"$status" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; |
| 127.0.0.1 - [127.0.0.1:3000]
- [29/Apr/2009:15:27:57 -0700] GET
/runlogs/ HTTP/1.1 "200" 3689 "-" "Mozilla/5.0 (Macintosh;
U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like
Gecko) Version/3.2.1 Safari/525.27.1" "-" 127.0.0.1 - [127.0.0.1:3001] - [29/Apr/2009:15:27:57 -0700] GET /favicon.ico HTTP/1.1 "200" 0 "http://localhost/runlogs/" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1" "-" 127.0.0.1 - [127.0.0.1:3002] - [29/Apr/2009:15:27:57 -0700] GET /stylesheets/scaffold.css?1240977992 HTTP/1.1 "200" 889 "http://localhost/runlogs/" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1" "-" |
| 127.0.0.1 - [127.0.0.1:3000]
- [29/Apr/2009:15:28:53 -0700] GET /runlogs/ HTTP/1.1 "200" 3689 "-"
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us)
AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1"
"-" 127.0.0.1 - [127.0.0.1:3002, 127.0.0.1:3000] - [29/Apr/2009:15:28:53 -0700] GET /favicon.ico HTTP/1.1 "200" 0 "http://localhost/runlogs/" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1" "-" 127.0.0.1 - [127.0.0.1:3001] - [29/Apr/2009:15:28:53 -0700] GET /stylesheets/scaffold.css?1240977992 HTTP/1.1 "200" 889 "http://localhost/runlogs/" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1" "-" |
Posted by Arun Gupta in web2.0 | Comments[8]
|
|
|
|
|
Thursday April 23, 2009
GlassFish asadmin CLI-driven Cluster Setup
Here is simple script that:
| echo A | java -Xmx256m -jar
~/Downloads/glassfish-installer-v2.1-b60e-darwin.jar -console cd $GLASSFISH_HOME chmod +x ./lib/ant/bin/ant ./lib/ant/bin/ant -f setup.xml cd $GLASSFISH_HOME echo 'AS_ADMIN_ADMINPASSWORD=adminadmin' > password echo 'AS_ADMIN_PASSWORD=adminadmin' >> password echo 'AS_ADMIN_MASTERPASSWORD=changeit' >> password ./bin/asadmin create-domain --user admin --passwordfile ./password --savelogin=true --portbase 5000 --interactive=false --profile cluster cloud ./bin/asadmin start-domain cloud ./bin/asadmin create-node-agent --user admin --port 5048 --interactive=false --passwordfile ./password cloud-nodeagent ./bin/asadmin start-node-agent --interactive=false --passwordfile ./password cloud-nodeagent ./bin/asadmin create-cluster --port 5048 wines ./bin/asadmin create-instance --port 5048 --nodeagent cloud-nodeagent --systemproperties HTTP_LISTENER_PORT=58080 --cluster wines cabernet ./bin/asadmin create-instance --port 5048 --nodeagent cloud-nodeagent --systemproperties HTTP_LISTENER_PORT=58081 --cluster wines merlot ./bin/asadmin deploy --target wines --port 5048 --availabilityenabled=true samples/quickstart/clusterjsp/clusterjsp.ear ./bin/asadmin start-cluster --port 5048 --interactive=false --passwordfile ./password wines |
Posted by Arun Gupta in General | Comments[5]
|
|
|
|
|
Thursday February 12, 2009
TOTD
#67 shows how to configure GlassFish High Availability using
Apache httpd + mod_jk on Mac OS X. Even though that's a standard and
supported configuration, there are several
advantages for replacing
Apache httpd with Sun Web Server and mod_jk with Load Balancer plugin
that comes with GlassFish.
This Tip Of The Day (TOTD) shows how
to configure Clustering and Load Balancing using GlassFish
v2.1, Sun
Web Server, Load
Balancer plugin on Windows Vista. This blog is using JDK 6 U7,
GlassFish v2.1 (cluster profile), Sun Web
Server 7 U4, and Load Balancer plug-in with Sun
GlassFish Enterprise
Server 2.1 Enterprise Profile (with HADB link).
Lets get started!

| <?xml version="1.0"
encoding="UTF-8"?> <!DOCTYPE loadbalancer PUBLIC "-//Sun Microsystems Inc.//DTD Sun Java System Application Server 9.1//EN" "file:///C:/Sun/WebServer7/https-LH-KRKZDW6CJE1V/config/sun-loadbalancer_1_2.dtd "> <loadbalancer> <cluster name="cluster1" policy="round-robin" policy-module=""> <instance name="instance1" enabled="true" disable-timeout-in-minutes="60" listeners="http://localhost:38080" weight="100"/> <instance name="instance2" enabled="true" disable-timeout-in-minutes="60" listeners="http://localhost:38081" weight="100"/> <web-module context-root="/clusterjsp" disable-timeout-in-minutes="30" enabled="true" error-url=""/> <health-checker interval-in-seconds="7" timeout-in-seconds="5" url="/"/> </cluster> <property name="response-timeout-in-seconds" value="120"/> <property name="reload-poll-interval-in-seconds" value="7"/> <property name="https-routing" value="false"/> <property name="require-monitor-data" value="false"/> <property name="active-healthcheck-enabled" value="false"/> <property name="number-healthcheck-retries" value="3"/> <property name="rewrite-location" value="true"/> </loadbalancer> |







Posted by Arun Gupta in Finance | Comments[4]
|
|
|
|
|
Friday January 30, 2009
TOTD #67: How to front-end a GlassFish Cluster with Apache + mod_jk on Mac OSX Leopard ?
GlassFish
provides support for High Availability by creating a cluster of server
instances and session state replication. This enhances the
scalability and availability of your application and is a critical
piece of decision making critieria when selecting an Application
Server. Clustering
in GlassFish Version 2 provides comprehensive introduction to
clustering, high availability and load balancing in GlassFish.
GlassFish provides out-of-the-box support for load-balancing HTTP(S),
JMS,
and RMI/IIOP
traffic and front-ended by Sun Java System Web Server, Apache Web
Server, and Microsoft IIS (more
details here) using the Load
Balancer plug-in. This plug-in however is not available for
Mac OS X and a popular technique used on that platform for front-ending
is to use Apache httpd
+ mod_jk.
This is exactly what this TOTD (Tip
Of The Day) is going to
describe.
This TOTD is going to explain how to front-end a 3-instance GlassFish
cluster with Apache httpd and mod_jk on Mac OS X.
This blog is using information from the following blogs:

| ~/samples/v2/clustering/glassfish/bin
>./asadmin
create-jvm-options --target cluster1
"-DjvmRoute=\${AJP_INSTANCE_NAME}" Command create-jvm-options executed successfully. ~/samples/v2/clustering/glassfish/bin >./asadmin create-jvm-options --target cluster1 "-Dcom.sun.enterprise.web.connector.enableJK=\${AJP_PORT}" Command create-jvm-options executed successfully. |
| ~/samples/v2/clustering/glassfish/bin
>./asadmin
create-system-properties --target instance1
AJP_INSTANCE_NAME=instance1 Command create-system-properties executed successfully. ~/samples/v2/clustering/glassfish/bin >./asadmin create-system-properties --target instance1 AJP_PORT=9090 Command create-system-properties executed successfully. ~/samples/v2/clustering/glassfish/bin >./asadmin create-system-properties --target instance2 AJP_INSTANCE_NAME=instance2 Command create-system-properties executed successfully. ~/samples/v2/clustering/glassfish/bin >./asadmin create-system-properties --target instance2 AJP_PORT=9091 Command create-system-properties executed successfully. ~/samples/v2/clustering/glassfish/bin >./asadmin create-system-properties --target instance3 AJP_INSTANCE_NAME=instance3 Command create-system-properties executed successfully. ~/samples/v2/clustering/glassfish/bin >./asadmin create-system-properties --target instance3 AJP_PORT=9092 Command create-system-properties executed successfully. |
| httpd: Syntax error on line 116 of /private/etc/apache2/httpd.conf: Cannot load /usr/libexec/apache2/mod_jk-1.2.25-httpd-2.2.4.so into server: dlopen(/usr/libexec/apache2/mod_jk-1.2.25-httpd-2.2.4.so, 10): no suitable image found. Did find:\n\t/usr/libexec/apache2/mod_jk-1.2.25-httpd-2.2.4.so: mach-o, but wrong architecture |
| ~/workspaces/tomcat-connectors-1.2.27-src/native >./configure --with-apxs=/usr/sbin/apxs |
| .
. . checking for target platform... unix no apache given no netscape given configure: creating ./config.status config.status: creating Makefile config.status: creating apache-1.3/Makefile config.status: creating apache-1.3/Makefile.apxs config.status: creating apache-2.0/Makefile config.status: creating apache-2.0/Makefile.apxs config.status: creating common/Makefile config.status: creating common/list.mk config.status: creating common/jk_types.h config.status: creating jni/Makefile config.status: creating common/portable.h config.status: executing depfiles commands |
| ~/workspaces/tomcat-connectors-1.2.27-src/native/apache-2.0 >sudo cp mod_jk.so /usr/libexec/apache2/ |
| LoadModule jk_module libexec/apache2/mod_jk-1.2.25-httpd-2.2.4.so |
| JkWorkersFile
/etc/apache2/worker.properties # Where to put jk logs JkLogFile /var/log/httpd/mod_jk.log # Set the jk log level [debug/error/info] JkLogLevel debug # Select the log format JkLogStampFormat "[%a %b %d %H:%M:%S %Y] " # JkRequestLogFormat set the request format JkRequestLogFormat "%w %V %T" # Send all jsp requests to GlassFish JkMount /*.jsp loadbalancer |
| sudo mkdir /var/log/httpd |
| #
Define 1 real worker using ajp13 worker.list=loadbalancer # Set properties for instance1 worker.instance1.type=ajp13 worker.instance1.host=localhost worker.instance1.port=9090 worker.instance1.lbfactor=50 worker.instance1.cachesize=10 worker.instance1.cache_timeout=600 worker.instance1.socket_keepalive=1 worker.instance1.socket_timeout=300 # Set properties for instance2 worker.instance2.type=ajp13 worker.instance2.host=localhost worker.instance2.port=9091 worker.instance2.lbfactor=50 worker.instance2.cachesize=10 worker.instance2.cache_timeout=600 worker.instance2.socket_keepalive=1 worker.instance2.socket_timeout=300 # Set properties for instance3 worker.instance3.type=ajp13 worker.instance3.host=localhost worker.instance3.port=9092 worker.instance3.lbfactor=50 worker.instance3.cachesize=10 worker.instance3.cache_timeout=600 worker.instance3.socket_keepalive=1 worker.instance3.socket_timeout=300 worker.loadbalancer.type=lb worker.loadbalancer.balance_workers=instance1,instance2,instance3 |
| ~/samples/v2/clustering/glassfish/lib
>cp
~/tools/apache-tomcat-5.5.27/server/lib/tomcat-ajp.jar . ~/samples/v2/clustering/glassfish/lib >cp ~/Downloads/commons-logging-1.1.1/commons-logging-1.1.1.jar . ~/samples/v2/clustering/glassfish/lib >cp ~/tools/commons-modeler-2.0.1/commons-modeler-2.0.1.jar . |
| #Listen 12.34.56.78:80 Listen 80 |
| sudo httpd |
| . . . [Thu Jan 29 11:14:16 2009] [warn] Init: Session Cache is not configured [hint: SSLSessionCache] [Thu Jan 29 11:14:16 2009] [warn] No JkShmFile defined in httpd.conf. Using default /usr/logs/jk-run time-status [Thu Jan 29 11:14:16 2009] [warn] No JkShmFile defined in httpd.conf. Using default /usr/logs/jk-run time-status [Thu Jan 29 11:14:16 2009] [notice] Digest: generating secret for digest authentication ... [Thu Jan 29 11:14:16 2009] [notice] Digest: done [Thu Jan 29 11:14:16 2009] [warn] pid file /private/var/run/httpd.pid overwritten -- Unclean shutdow n of previous Apache run? [Thu Jan 29 11:14:16 2009] [notice] Apache/2.2.9 (Unix) mod_ssl/2.2.9 OpenSSL/0.9.7l DAV/2 mod_jk/1. 2.27 configured -- resuming normal operations |




Posted by Arun Gupta in General | Comments[7]
|
|
|
|
|
Monday November 24, 2008
![]() |
GlassFish v2 allows you to configure cluster of multiple nodes/instances to meet various availability requirements, from the highly scalable service availability configuration to the business-critical, 99.999% service-and-data availability configuration. A cluster can be deployed using different toplogies with a choice of service/data availability, in-Memory/HADB, co-located/non-colocated and other factors. This new white paper explains reference configurations on that can be used for deploying business services. |
Posted by Arun Gupta in General | Comments[4]
|
|
|
|
|
Today's Page Hits: 257
Total # blog entries: 1007
| « December 2009 | ||||||
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
5 | ||||||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | ||
| Today | ||||||