tag:blogger.com,1999:blog-41466189756017304872024-02-03T05:17:13.540+06:00Hunting treasures of Software Engineering..Muhammad Arifur Rahman's technical thoughtsMuhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.comBlogger19125tag:blogger.com,1999:blog-4146618975601730487.post-42713125482095321382019-10-20T19:18:00.000+06:002019-10-20T19:29:56.910+06:00The "Compute" method of the DataTable classThe "<a href="https://docs.microsoft.com/en-us/dotnet/api/system.data.datatable.compute?view=netframework-4.8" target="_blank">Compute</a>" method of the System.Data.DataTable class of .NET framework is a useful function for doing further calculation. Specially getting rows of data from Database or other sources and then executing further calculation in the server memories(RAM). <br />
<br />
<b> public object Compute(string expression, string filter)</b><br />
<br />
It Computes the given expression like SUM or Count, on the current rows with fileter if needs.The filter is to limit the rows.The "filter" is like "WHERE" portion in <a href="https://en.wikipedia.org/wiki/SQL" target="_blank">SQL</a><br />
<br />
Example Code <b>C#</b><br />
<br />
<b>string output = dtData.Compute("Sum(ItemQuantity)","ItemQuality ='HIGH'").ToString();</b>Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-85426228752240381512019-10-19T19:38:00.001+06:002019-10-20T19:33:39.926+06:00Output data in JSON formatFrom the server, we may need to push data to variety of clients in <a href="https://en.wikipedia.org/wiki/JSON" target="_blank">JSON</a> (JavaScript Object Notation) format.As an example, the server is a web server and type of the application is a web services.The client may be a PHP/ASP Net/[XYZ...] application or Mobile app.To generate a JSON data from Data, we can use the "<a href="https://docs.microsoft.com/en-us/dotnet/api/system.web.script.serialization.javascriptserializer?view=netframework-4.8" target="_blank"><b>JavaScriptSerializer</b></a>" method from the library of <b>"System.Web.Script.Serialization</b>" of <b>.NET</b> framework.Here is the code example.<br />
<br />
C#<br />
=== <br />
using System;<br />
using System.Collections.Generic;<br />
using System.Web.Script.Serialization;<br />
using System.Data;<br />
using System.Text;<br />
namespace TESTConsoleApplicatio<br />
{<br />
public class JSONCLASS<br />
{<br />
public static string GetJSONData(DataTable dt)<br />
{<br />
<br />
StringBuilder sb = new StringBuilder();<br />
<br />
List<dictionary object="" string="">> result = new List<dictionary object="" string="">>();<br /> <br /> foreach (DataRow dr in dt.Rows)<br /> {<br /> Dictionary<string object=""> drow = new Dictionary<string object="">();<br /> for (int i = 0; i < dt.Columns.Count; i++)<br /> {<br /> <br /> drow.Add(dt.Columns[i].ColumnName, dr[i]);<br /> }<br /> result.Add(drow);<br /> }<br /><br /><br /> <b> JavaScriptSerializer js = new JavaScriptSerializer(); <br /> string jsonString = js.Serialize(result);<br /> </b> //OUTPUT JSON<br /> //{"product":[{\"ID\":1001,\"name\":\"Book 1\",\"category\":\"Category 1\"},<br /> // {\"ID\":2001,\"name\":\"Book 2\",\"category\":\"Category 2\"},<br /> // {\"ID\":1002,\"name\":\"Book 22\",\"category\":\"Category 1\"} ]} <br /><br /> return "{\"" + dt.TableName + "\":" + jsonString + "}"; <br /><br /> }<br /><br /> }<br />}</string></string></dictionary></dictionary><br />
<br />
========<br />
INPUT/OUTPUT<br />
<br />
using System;<br />
using System.Collections.Generic;<br />
using System.Linq;<br />
using System.Text;<br />
using System.Threading.Tasks;<br />
using System.Data;<br />
<br />
namespace TESTConsoleApplicatio<br />
{<br />
class Program<br />
{<br />
static void Main(string[] args)<br />
{<br />
//INPUT DATA.May be we can get data from database or other sources<br />
<br />
DataTable dt = new DataTable("product");<br />
dt.Columns.Add("ID",typeof( int)); <br />
dt.Columns.Add("name");<br />
dt.Columns.Add("category");<br />
<br />
DataRow rw = dt.NewRow();<br />
rw["ID"] = 1001;<br />
rw["name"] = "Book 1";<br />
rw["category"] = "Category 1";<br />
dt.Rows.Add(rw);<br />
<br />
rw = dt.NewRow();<br />
rw["ID"] = 2001;<br />
rw["name"] = "Book 2";<br />
rw["category"] = "Category 2";<br />
dt.Rows.Add(rw);<br />
<br />
rw = dt.NewRow();<br />
rw["ID"] = 1002;<br />
rw["name"] = "Book 22";<br />
rw["category"] = "Category 1";<br />
dt.Rows.Add(rw);<br />
<br />
<br />
<br />
<b>string jsonOutput = JSONCLASS.GetJSONData(dt);</b><br />
<b> </b><br />
<b> </b><b> //We can out put data as a web services </b><br />
Console.Write(jsonOutput);<br />
Console.ReadLine();<br />
<br />
<br />
<br />
<br />
<br />
<br />
}<br />
}<br />
}Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-48044728849690719962012-07-31T11:54:00.002+06:002012-07-31T11:54:42.121+06:00Photoshop and Illustrator are bosom friends of a Web developers and designers.The Adobe’s two great tools are Photoshop and Illustrator in my opinion. Especially after I have used these tools for a web develop project. I had needed to have a new logo,some image based rounded shaped buttons with nice colors and fonts. In the past I had a very little knowledge on these two great tools. Being a web developer I thought these tools are mainly for graphic designers. I used to search at the Internet to get and download these elements (like rounded buttons, image logo’s, rounded shaped box e.g.) specially free versions and used those. But these elements were mismatching with the proposed web site’s color matching, font matching, shape of height and widths. And to code (CSS, HTML,...) on these elements to make right shapes takes a lengthy process and some time projects are delayed. Later on I had read some articles and books on web development related topics and they suggested to use some popular tools. I went to the path of their and the result is very much positive.
<br><br>
The conclusion is that web developers need not only have experience on HTML/XHTML, CSS, Client script( JavaScript and helping tool like AJAX,JQuery/Prototype,..) , one of the Server script (Asp .Net/Php/Ruby on rails/Groovy/Perl/...) are not enough, to be a real master they need to be a web designer also. And for that become a good friend of Photoshop, Illustrator like these tools.
Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-70779354117383440082010-05-18T17:52:00.004+06:002012-10-02T13:51:30.372+06:00Creating printable version of a web page<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIPd7eNmCjPRTmlafwLSQ90vAyg4jMEQLNjBbR5VUN0WcqaDNsAnh_l18Z_0eOinz6OmxBii9vl-Wq34SrK9DAhz7uz3uynEWu4xrboyL5FgJw8JFFOIg6eBvkU7E9ivLJ8jsFKZDGZzI/s1600/oki_ML3410_1.jpg" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5472583377568147538" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIPd7eNmCjPRTmlafwLSQ90vAyg4jMEQLNjBbR5VUN0WcqaDNsAnh_l18Z_0eOinz6OmxBii9vl-Wq34SrK9DAhz7uz3uynEWu4xrboyL5FgJw8JFFOIg6eBvkU7E9ivLJ8jsFKZDGZzI/s320/oki_ML3410_1.jpg" style="cursor: hand; cursor: pointer; display: block; height: 257px; margin: 0px auto 10px; text-align: center; width: 320px;" /></a>
<link href='http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css' rel='stylesheet' type='text/css'/>
<br />
In web based applications, printing is a common requirement.User can print a web page by using the printing utilities of the browser.This may leave users at a surprised state because most of the time the format of the printed web page is messed up.For this reason a "Printer friendly" version of the page is provided. There are many ways to create a printer friendly version using server-side scripting. A separate request for printer friendly page is sent to the server and the server responses with a well formatted printable page. While these ways are completely workable, an elegant and client based approach is the use of only CSS to provide a printable version.<br />
<br />
To accomplish this, we need to follow the next simple processes.<br />
<br />
1. Create a seperate CSS file that controls the appearence of the page's elements for printable version.Let's call it "printable.css".<br />
2. Include the CSS into the page that will be printed with the following syntax.<br />
<div style="font-size: 9pt; margin-left: 10px;">
<br />
<link rel="stylesheet" href="--CSS folder path--" media="print" />
</div>
<br />
The important part here is the "media" attribute. When the value of this attribute is set to "print", the browser automatically uses this CSS file to format the page while printing.<br />
<br />
Let's see a quick example. The layout of our page could be the following:<br />
<div style="font-size: 9pt; margin-left: 10px;">
<br />
<html><br />
<head><br />
<link rel="stylesheet" href="<CSS folder path>/printable.css" type="text/css" media="print" /><br />
</head><br />
<body><br />
<div id="header"><br />
<!- HTML elements for header(logo,banner) --><br />
</div><br />
<br />
<div id="top_menu"><br />
<!- HTML elements for top menu goes here --><br />
</div><br />
<br />
<div id="main_content"><br />
<!- HTML elements for page content goes here --><br />
</div><br />
<br />
<div id="footer"><br />
<!- HTML elements for footer goes here --><br />
</div><br />
</body><br />
</html></div>
<br />
While printing this page, we would probably want to print only the main content and escape the printing of the header, menu or footer. We can accomplish this by using the "printable.css" as follows:<br />
<div style="font-size: 9pt; margin-left: 10px;">
<br />
#header,#top_menu, #footer {display:none}</div>
<br />
We can add a link or button to anywhere in the page above to provide printing functionality.<br />
<div style="font-size: 9pt; margin-left: 10px;">
<br />
<a href="#" onclick="window.print(); return false"> Print </a></div>
<br />
When user clicks on the "Print" link, the browser uses the "printable.css" and hides all portions except the content of the "main_content" element while printing.The result is a formatted printer friendly version of the page.We can fine tune the "printable.css" to take more control over the printable version.Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-47984146773765495562010-03-09T14:45:00.004+06:002010-03-09T16:48:42.327+06:00Loading seed data with Seed-Fu plugin<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz9Kk5K54Vy9F6oyLYBQA3ok5cY_dFiEzOK_V7hNS6FKBOyNADb0-Sc-qyq02T16W2UIjmiFJpB_q72rY808yXfCzY3wyrS8AihB9wfcZkkMYvYLQTYriDrf7SksCZ0jrQXCGXnS6Cw1k/s1600-h/seed.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 225px; height: 245px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz9Kk5K54Vy9F6oyLYBQA3ok5cY_dFiEzOK_V7hNS6FKBOyNADb0-Sc-qyq02T16W2UIjmiFJpB_q72rY808yXfCzY3wyrS8AihB9wfcZkkMYvYLQTYriDrf7SksCZ0jrQXCGXnS6Cw1k/s320/seed.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446584217884860418" /></a><br /><br />Loding seed (default) data for lookup database tables is common to the most applications.Rails provide different mechanisms to accomplish this task. I have found the implementation with a plugin called seed-fu very simple and easy to implement.<br /><br />Seed-Fu is based around loading ruby scripts located in db/fixtures via a Rake task.We just place the ruby files with the code to load seed data into the db/fixtures folder followed by executing the "db:seed" rake command .The plug in handles the rest.<br /><br />Looking into the details of one of the seed data file will present a clearer view.We have a model named "Role" and want to populate seed data for this.We place a file named "roles.rb" into the "db\fixture" folder.The file name can be anything but "roles.rb" but we stick to the convention here.The file content as follows:<br /><br /> <div style="margin-left:10px;font-size:9pt"><br /># # db/fixtures/roles.rb <br /># # put as many seeds as you like in <br /><br />Role.seed(:name) do |s|<br /> s.name ='Admin'<br /> s.description = 'Administrator of the system'<br />end<br /><br />Role.seed(:name) do |s|<br /><br /> s.name ='Developer'<br /> s.description = 'Developer of a project'<br />end<br /><br />Role.seed(:name) do |s|<br /> s.name ='Product Owner'<br /> s.description = 'Product owner of a product'<br />end<br /></div><br /><br />We want to add 3 roles (Admin,Developer and Product owner) so we put 3 "Role.seed" block and populate the "name" and "description" attributes.When we run "db:seed", the roles database table will be populated accordingly.<br /><br />What will happen if we add some more seed data files later and run "db:seed" rake command? Will the role data will be duplicated? It would normally but we have taken a guard :)<br /><br />Look at the parameter ":name" after the "Role.seed" method call in the above file.The "Role.seed" method takes a comma separated list of symbols that represent some of the key attribute names of the corresponding model ("Role" here).Before inserting a new row, seed-fu checks whether a row with the key values already exists.If a row exists, no data is inserted.<br /><br />In the above example we have provided ":name" as the key column/attribute name.Before inserting the first "Role.seed" block,seed-fu conducts a search in the "roles" table for a row with the value "Admin" in "name" field.If a row exist, it skips the block(duplicate row not added) and try to proceed with the same rule for the next blocks.<br /><br />Once you have set up your fixtures, it’s simple to run them:<br /> <div style="margin-left:10px;font-size:9pt"><br />rake db:seed <br /><br />or<br /><br />rake db:seed RAILS_ENV=<development| test | production><br /><br /></div><br />Make sure to populate the database schema with "db:migrate" or "db:schema:load" before you run the "db:seed" command.<br /><br />You can download the latest version of seed-fu plugin from <a href="http://github.com/mbleigh/seed-fu" target="_blank" >GitHub</a> .Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com1tag:blogger.com,1999:blog-4146618975601730487.post-12663178030508452562010-02-05T23:10:00.001+06:002010-03-09T16:33:52.322+06:00Rails cache expiration with sweepers<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEskAY1AMfzsz0ekYAZL1YmGEGvqcyX9pF5sqRCx2fmCWjyuSfsKKZTDxMZm5DxnIcLQ7Amb2GjrEkS1PGF0EnElA1jh8SEs_lMV3gI8unpLffJHR1AYPhTdMZkywiHAWCXzxS9G-FPZk/s1600-h/sweepshot.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 247px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEskAY1AMfzsz0ekYAZL1YmGEGvqcyX9pF5sqRCx2fmCWjyuSfsKKZTDxMZm5DxnIcLQ7Amb2GjrEkS1PGF0EnElA1jh8SEs_lMV3gI8unpLffJHR1AYPhTdMZkywiHAWCXzxS9G-FPZk/s320/sweepshot.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446580333762445746" /></a><br /><br />Sweepers enable expiring or deleting the rails cached items from a centralized place.This exempt us from the pain of writing cache expiration codes scattered throughout the code base by providing an implementation of the observer pattern.Sweeper classes observe one or more models that contributes to the cache data and provide options to expire the related cached items when any changes occur in those models(created,updated or deleted).<br /> <br />Let's understand the process with an example.We have a model named "SystemMessage" that provides messages posted by the system administrator.In the main layout we display the count of total system messages.Since several views are rendered inside this layout, we need to hit the database every time to get the count.To avoid this we decide to cache the count during the first database hit.We cache the count in the following code placed inside a method of the application_controller.<br /><br /> <div style="margin-left:10px;font-size:9pt">Rails.cache.fetch("systen_message_count") {SystemMessage.count} </div><br /><br />The code above hits the database to fetch the count at the very first access and cache it with the key "systen_message_count". Subsequent calls to the method returns the count from the cache until it is expired.<br /><br />Now we need to expire and reload the count when a new "SystemMessage" is created or an existing one is deleted.Instead of putting the cache expiration code in different places inside controllers and models, we can put it inside a single sweeper class and react to the changes in the "SystemMessage" model(creation or deletion).Here is the code placed inside the "<rails root>\app\models\cache\system_messages_sweeper.rb" file.<br /><div style="margin-left:10px;font-size:9pt"><br />class SystemMessagesSweeper < ActionController::Caching::Sweeper<br /><div style="margin-left:15px"><br /> observe SystemMessage<br /><br /> def after_create(record)<br /> expire_system_message_cache(record)<br /> end<br /> <br /> def after_destroy(record)<br /> expire_system_message_cache(record)<br /> end<br /><br /> private<br /><br /> def expire_system_message_cache(record)<br /> Rails.cache.delete("systen_message_count") <br /> end<br /></div><br />end<br /></div><br />In the code above, we observe the "SystemMessage" model and expire the system_message_count" cache item when a new record is created or deleted.So the count<br />will be loaded again from the database at the next hit and will be cached subsequently.<br /><br />The sweeper class needs to be loaded by the rails.So we put the following code into the environment.rb file<br /> <div style="margin-left:10px;font-size:9pt"><br /> config.load_paths += %W( #{RAILS_ROOT}/app/models/cache )<br /> </div><br /> <br />The sweeper has to be added to the controller that will use it. The following code is added to the "SystemMessagesController" class.<br /> <div style="margin-left:10px;font-size:9pt"><br /> cache_sweeper :system_messages_sweeper, :only => [ :create,destroy ] <br /></div><br /> <br />Rails cache sweepers have played an important role in improving the performance of <a href="http://www.scrumpad.com" target="_blank" >ScrumPad</a>, which is a popular agile/scrum project management and collaboration tool.Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-56135704357570012872009-11-18T14:03:00.004+07:002010-03-09T16:49:48.428+06:00Load default lookup data with Rails fixture<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgitd6BxvvZ0SaXld_7IUzAuAyZinjQ52kCR9MsgzO2PdQPTTM1cl3ZLopPX4AuWOEAFemQ0yyPuR8PkumP6bIe9V9iOhDdC64dWTC7gbIsv3ZDzMZ98YqdafICuABx5nLYBfK6YbG0vTY/s1600-h/fixture.gif"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 170px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgitd6BxvvZ0SaXld_7IUzAuAyZinjQ52kCR9MsgzO2PdQPTTM1cl3ZLopPX4AuWOEAFemQ0yyPuR8PkumP6bIe9V9iOhDdC64dWTC7gbIsv3ZDzMZ98YqdafICuABx5nLYBfK6YbG0vTY/s320/fixture.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5446584479336785970" /></a><br /><br />The Rails <a href=http://ar.rubyonrails.org/classes/Fixtures.html “ target=”_blank“ >Fixture</a> is a great feature to load sample test data before running unit, functional or integration tests. While it is mostly used in loading test data, it can be a handy tool to load default lookup data for the staging or production database. We can use the “create_fixtures” class method of “Fixture” class.<br /><br />The following code snippet loads the data from the “countries.yml” file into the “countries” table.<br /><i><br />require 'active_record/fixtures'<br /><br />class CountryData <br /> def load()<br /> fixtures_folder = File.join(RAILS_ROOT, 'test', 'fixtures')<br /> fixtures.create_fixtures(fixtures_folder , " countries " )<br /> end<br />end<br /></i><br /><br />In this case,the “countries.yml” file must be present in the “<RAILS_ROOT>\test\fixtures” folder.<br /><br />If we need to load data from all the “.yml” files of a particular folder and don’t want to hard code the names of the individual files as the above code snippet, the following code will do.<br /><i><br />require 'active_record/fixtures'<br /><br />class LoadDefaultData <br /> def load()<br /> fixt_folder = File.join(RAILS_ROOT, 'test', 'fixtures')<br /> fixtures = Dir[File.join( fixt_folder, '*.yml')].map {|f| <br /> File.basename(f, '.yml') } <br /> Fixtures.create_fixtures(fixt_folder, fixtures) <br /> end<br />end<br /></i>Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com2tag:blogger.com,1999:blog-4146618975601730487.post-31703747348618345502009-07-26T17:31:00.007+07:002010-03-09T17:03:26.418+06:00Mocks and Stubs in ROR with Mocha<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig-sW5Q0wdT1yV1FMXyg9X6aUTsKQnmZ7NZtU1hMsTS81WXT1vq5zeKXASibCkMZtkgglryBA1N8N3gRC8SfPXrnURaTIZsQJWk3gFEuaYtvshVCEkdu9oGoLKFL7miSEGwFpyeO0x1SY/s1600-h/mock.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 214px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig-sW5Q0wdT1yV1FMXyg9X6aUTsKQnmZ7NZtU1hMsTS81WXT1vq5zeKXASibCkMZtkgglryBA1N8N3gRC8SfPXrnURaTIZsQJWk3gFEuaYtvshVCEkdu9oGoLKFL7miSEGwFpyeO0x1SY/s320/mock.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446587992783547778" /></a><br /><br />Mock objects and stubs are very useful strategies in Test Driven Development(TDD).They play a great role in increasing the effectiveness and speed of the unit tests.<br /><br />Life is easier when we write unit tests for methods that returns in-process immediate results.Like adding two integers and returning the result.Things get complicated when the method interacts with applications in another process and involve in a blocking call (connecting to database,calling external web service methods,sending mail through a mail server).<br /><br />For a variety of reasons, interacting with external resources can make out automated tests a lot harder to write, debug, and understand.It also takes a significant amount of time to run the tests.To extend unit test coverage and overcome this limitation,an alternative best practice is to use mock objects, stubs, or other fake objects in place of the external resources that initiate an inter process communication. <br /><br />The terms "Mock" and "Stub" are used interchangeably in most cases and thought as same.But there are subtle difference in the usage of these two.Martin Fowler explains the difference in <a href="http://www.martinfowler.com/articles/mocksArentStubs.html" target="_blank">this</a> renowned article.<br /><br />In my understanding so far,Mocks and Stubs are similar in nature but Mocks does more in the form of "Interaction based testing". <br /><br />Stub is a class where the method definitions imitates original methods of a class that is involved in calling external resources.The imitated methods returns hard coded known result.So the stub methods return expected outputs against a set of known inputs without involving call to external resources as the original class methods do.<br /><br />For example,if the original "execute_DML" method involves initiating a connection to database,executing the SQL query against it and returning a true/false, the stub "execute_DML" method just returns true or false depending on the input.<br /><br />Mocks does the same as Stubs, but it considers the interaction between classes.We use mock objects to record and verify the interaction.In the above example the mock "execute_DML" method not only returns true/false but also verifies that the method calls the appropriate methods to initiate the connection and sending the SQL query to database.In this case, the connection and query execution methods are also mocked to avoid external resource calls.<br /><br />In mock methods, if we set an expectation to call a method of a particular object only once and in reality the method is called twice, the test result is considered a failure even if the final outcome of the method is satisfied.This is different from stubs.<br /><br />In <a href="http://www.scrumpad.com" target="_blank">ScrumPad</a> project, we use mocks and stubs in the above mentioned scenarios.We use an excellent framework<span style="font-weight:bold;"><a href="http://mocha.rubyforge.org" target="_blank"> Mocha</a></span> for this.<br /><br />Mocha is a library for mocking and stubbing that provides a unified, simple and readable syntax.Let me provide some examples:<br /><br /><br />We can mock a "class" method.suppose we have a method "add_to_cart".The original definition is the following,<br /><br /><span style="font-weight:bold;"><span style="font-style:italic;">def add_to_cart(product_id)<br /> product = Product.find(product_id)<br /><br /> if (product != nil)<br /> Cart.add(product) <br /> end<br /> <br /> return true<br />end</span></span><br /><br />The call to "Product.find" involves a database interaction.We can use mock methods to avoid this.<br /><br /><span style="font-style:italic;"><br /><span style="font-weight:bold;">require 'test/unit'<br />require 'mocha'<br /><br />class MiscExampleTest < Test::Unit::TestCase<br /> <br /> def test_mocking_a_class_method<br /> product = Product.new<br /> Product.expects(:find).with(1).returns(product)<br /> assert_equal add_to_cart(1), true<br /> end<br />end</span></span><br /><br />In the above case,we have set an expectation in the "add_to_cart" method on "Product" class.Inside the "add_to_cart" method, there must be a call to the "find" method of "Product" class.If the call is omitted,the test will fail even if the final assertion is passed (e.g. the return value of the method "add_to_cart" is true).As we have mocked the "find" class method of the "Product" active record class,there will be no actual database interaction.<br /><br />We can also set how many times a particular method should be invoked.For example if we set the expectation in the above example like this:<br /><br /><span style="font-style:italic;"><span style="font-weight:bold;">Product.expects(:find).once.with(1).returns(product)</span></span><br /><br />This will raise error if we call the "find" method more than once inside the "add_to_cart" method.<br /><br />We can set expectation on "instance" methods in stead of "class" methods.For example<br /><span style="font-style:italic;"><span style="font-weight:bold;"><br />Product.any_instance.expects(:find).with(1).returns(product)</span></span><br /><br />Using Stubs instead of mocks is very similar in mocha.For example:<br /> <br /><span style="font-style:italic;"><span style="font-weight:bold;"><br /> def test_stubbing_an_instance_method_on_all_instances_of_a_class<br /> recepient_token = "124399A_@@44"<br /> PaymentService.any_instance.stubs(:create_recipient_token).<br /> returns(recepient_token)<br /><br /> transaction_response = TransactionResponse.new()<br /> transaction_response.stubs(:status).<br /> returns(SUCCESS)<br /><br /> PaymentEngine.handle_payments()<br /> end</span></span><br /><br />In the above example, we are using stubs to return predefined results from the imitated methods.<br /><br />Inside the "handle_payments" method,calls to "create_recipient_token" method of "PaymentService" web service will not invoke the web service in reality but will just return the hard-coded string.<br /><br />Similarly calls to the "status" method of "TransactionResponse" class will always return "Success".<br /><br />In both cases we are not setting any interaction based expectations as we did with mocks (the "expects" keyword in mocha).We are just freezing the external resource invocation by returning the known result with "Stubs".<br /><br />The simplicity of "mocha" and usage of these two extremely powerful tool helping us a lot in increasing the test code coverage of <a href="http://www.scrumpad.com" target="_blank">ScrumPad</a>.Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com2tag:blogger.com,1999:blog-4146618975601730487.post-66393303163571335482009-05-14T12:00:00.011+06:002010-03-10T09:47:10.972+06:00How to embed images into XML files<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAU6uzq7VbXydlpBxR98oOHGlLxUaSkIi3TYdl36XjKAFSOfB7ysyb_mt0HyEOpxlNtVfn_8ETPUyAJryVcvCF5jtJz2-RTKlMDMtqbT7e2x9Mrss_DsT50ViR_W4Cl_jFBbwZz84Xk-I/s1600-h/embed.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 135px; height: 117px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAU6uzq7VbXydlpBxR98oOHGlLxUaSkIi3TYdl36XjKAFSOfB7ysyb_mt0HyEOpxlNtVfn_8ETPUyAJryVcvCF5jtJz2-RTKlMDMtqbT7e2x9Mrss_DsT50ViR_W4Cl_jFBbwZz84Xk-I/s320/embed.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446846681537913010" /></a><br /><br />The title may sound confusing.We all know XML as plain text formats while images as binary files.So how can we embed images inside a XML file?Is it some kind of links to the external image files like the web pages?Not exactly.The image is completely embedded inside the nodes of XML in text format as base64 string.<br /><span style="font-weight: bold;"><br />The Base64 Format:</span><br /><br />Base64 converts binary data to plain text using 64 case-sensitive, printable ASCII characters: A-Z, a-z, 0-9, plus sign (+) and forward slash (/), and may be terminated with 0-2 "padding" characters represented by the equal sign (=). For example, the eight-byte binary data in hex "35 71 4d 8e 4c 5f db 42″ converts to Base64 text as "NXFNjkxf20I=".<br /><br />To accomplish this, we need some kind of encoder application that converts the image into the base64 format and embed the text into XML.To extract the image from the XML,we need another application that reads the base64 string,decodes it into the image again.In .NET Framework class library(FCL), we have convenient components in the "System.Convert" namespace to perform this encoding and decoding.Let's look at an example to understand the process.<br /><br /><span style="font-weight: bold;">The Base64 Encoder:</span><br /><br /> <span style="font-weight: bold;"><span style="font-style: italic;">static void Main()<br /> {<br /> string base64FormattedImage= string.empty;<br /> string imageFilePath = @"C:\Images\nature.jpg";<br /> base64FormattedImage= EncodeToBase64FromImage(imageFilePath);<br /> <br /> if (!string.IsNullOrEmpty(base64String))<br /> {<br /> string destinationXMLFile = @"C:\XML\NewsContent.xml";<br /> WriteToXML(base64FormattedImage,destinationXMLFile);<br /> }<br /> }<br /><br /> private string EncodeToBase64FromImage(string imageFilePath)<br /> {<br /> System.IO.FileStream inFile;<br /> byte[] binaryData = null;<br /> string base64String=string.Empty;<br /><br /> try<br /> {<br /> inFile = new System.IO.FileStream(imageFilePath,<br /> System.IO.FileMode.Open,<br /> System.IO.FileAccess.Read);<br /> binaryData = new Byte[inFile.Length];<br /> long bytesRead = inFile.Read(binaryData, 0,<br /> (int)inFile.Length);<br /> inFile.Close();<br /> }<br /> catch (System.Exception exp)<br /> {<br /> // Error creating stream or reading from it.<br /> System.Console.WriteLine("{0}", exp.Message);<br /> <br /> }<br /> // Convert the binary input into Base64 Encoded output. <br /> try<br /> {<br /> base64String =<br /> System.Convert.ToBase64String(binaryData,<br /> 0,<br /> binaryData.Length);<br /> }<br /> catch (System.ArgumentNullException)<br /> {<br /> System.Console.WriteLine("Binary data array is null.");<br /> <br /> }<br /><br /> return base64String;<br /> }<br /><br /> private void WriteToXML(string base64FormattedImage,string destinationXMLFile)<br /> {<br /> XmlDocument doc = new XmlDocument();<br /> doc.Load(destinationXMLFile);<br /> XmlNode nd = doc.SelectSingleNode("/contents/content/Images").FirstChild;<br /> nd.InnerText = base64FormattedImage;<br /> doc.Save(destinationXMLFile);<br /> doc = null;<br /> }<br /></span><br /></span><br />The encoder reads an image file from the disk,converts it into Byte array and calls the "System.Convert.ToBase64String" method to get the base64 string from the Byte array.Then main method passes the base64 string to WriteToXML method to save into an XML file.<br /><br />The XML file looks like this:<br /><br /><span style="font-weight: bold;"><?xml version="1.0" encoding="utf-8"?><br /><contents><br /><content><br /> <ID>1</ID><br /> <Title>The title</Title><br /> <Body><br /> Content goes here.. <br /> </Body><br /> <Images><br /> <Image ID="1">/9j/4AAQSkZJRgABAgEAYABgAAD/4RBY ......</Image><br /> </Images><br /></content><br /></contents></span><br /><br /><span style="font-weight: bold;">The Base64 Decoder:</span><br /><br /><span style="font-weight: bold;"><span style="font-style: italic;">static void Main()<br />{<br />string sourceXMLFilePath= @"C:\XML\NewsContent.xml";<br />string destinationImageFilePath = @"C:\Images\nature.jpg";<br />DecodeFromBase64ToImage(sourceXMLFilePath, destinationImageFilePath);<br />}<br /><br />private void DecodeFromBase64ToImage(string sourceXMLFilePath, string destinationImageFilePath)<br /> {<br /> XmlDocument doc = new XmlDocument();<br /> doc.Load(sourceXMLFilePath);<br /> XmlNode nd = doc.SelectSingleNode("/contents/content/Images").FirstChild;<br /> string base64String = nd.InnerText;<br /><br /> byte[] binaryData;<br /> try<br /> {<br /> binaryData =<br /> System.Convert.FromBase64String(base64String);<br /> }<br /> catch (System.ArgumentNullException)<br /> {<br /> System.Console.WriteLine("Base 64 string is null.");<br /> return;<br /> }<br /> catch (System.FormatException)<br /> {<br /> System.Console.WriteLine("Base 64 string length is not " +<br /> "4 or is not an even multiple of 4.");<br /> return;<br /> }<br /><br /> // Write out the decoded data.<br /> System.IO.FileStream outFile;<br /> <br /> try<br /> {<br /> outFile = new System.IO.FileStream(destinationImageFilePath,<br /> System.IO.FileMode.Create,<br /> System.IO.FileAccess.Write);<br /> outFile.Write(binaryData, 0, binaryData.Length);<br /> outFile.Close();<br /> }<br /> catch (System.Exception exp)<br /> {<br /> // Error creating stream or writing to it.<br /> System.Console.WriteLine("{0}", exp.Message);<br /> }<br /> }<br /></span><br /></span><br />The decoder parses the XML document,retrieves the base64 string,converts it into Byte Array by calling the "System.Convert.FromBase64String" method.It then saves the Byte Array as an image file into the disk.<br /><br />This approach is widely used in the online news media industry.The news media companies exchange articles and images among themselves in XML format.The schema of the XML usually follows the popular <span style="font-weight: bold;">NITF(News Industry Text Format)</span> schema or DTD.You can find more about NITF <a href="http://www.iptc.org/cms/site/index.html?channel=CH0107" target="_blank">Here</a>.Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com2tag:blogger.com,1999:blog-4146618975601730487.post-58911303873060644102009-05-04T16:49:00.007+06:002010-03-10T09:49:50.966+06:00Creating a Private Ruby Gem Server<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj91-ct2kIBYs1EVqPyg_lOYsIj2OzR8oWkeskr2gLFEcyGCjZkzlOZKPjKH7NgGGl_5EOCLCl2Hi6ZcC7ALQqu6HuFJBqNai7XjKX8324xIJpCHQ1QEYsRQ7-uGFWj5PXf-jouRKW0I3E/s1600-h/private1.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 116px; height: 87px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj91-ct2kIBYs1EVqPyg_lOYsIj2OzR8oWkeskr2gLFEcyGCjZkzlOZKPjKH7NgGGl_5EOCLCl2Hi6ZcC7ALQqu6HuFJBqNai7XjKX8324xIJpCHQ1QEYsRQ7-uGFWj5PXf-jouRKW0I3E/s320/private1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446847372582396802" /></a><br /><br />While installing a new ruby gem in a machine,we normally use the <span style="font-weight:bold;"><span style="font-style:italic;">gem install <gem name></span></span> command.This command downloads the latest version of a particular gem.The problem is that ROR gems are not always backward compatible.So if a ROR application was developed using a particular version of a gem (Ex:0.1.1) and the latest version of the gem in the internet (normally rubyforge.org) is 0.1.2,problem may arise if the version 0.1.2 is not backward compatible with version 0.1.1.This may cause a serious trouble when the application is deployed into the web server and the wrong version of the gem is installed.Most probably the application will not function as expected.<br /><br />A solution to this problem is to deploy the exact version of all the gems those were used during development.One way to implement this is hosting the correct version of the gems into a web server(thus call it a private gem server).I have tested this option in a Apache web server.The steps are as follows:<br /><br />1.Create a directory for hosting gems on the public files area of the web server.Let's refer is as <span style="font-weight:bold;"><Base Directory></span>.<br /><br /> <span style="font-weight:bold;">ssh user@web.server<br /> cd /var/www<br /> mkdir my_gem_server<span style="font-style:italic;"></span></span><br /><br />2.Create a sub directory called <span style="font-weight:bold;">"gems"</span> under the <span style="font-weight:bold;"><Base Directory></span>.The name of the sub directory must be <span style="font-weight:bold;">"gems"</span> by convention.<br /><br /> <span style="font-weight:bold;"> ssh user@web.server<br /> cd /var/www/my_gem_server<br /> mkdir gems<span style="font-style:italic;"></span></span><br /><br />3.Copy all the necessary gems from the development machine into the /var/www/my_gem_server/gems sub directory.<br /><br />4.<span style="font-weight:bold;">Generate the gem index:</span><br /><br />gem comes with a command generate_index which generates all of the files necessary for serving gems over HTTP.Run this command into the <span style="font-weight:bold;"><Base Directory> </span>(/var/www/my_gem_server).<br /><br /><span style="font-weight:bold;">gem generate_index -d /var/www/my_gem_server<span style="font-style:italic;"></span></span><br /><br />Now the private gem server is ready to serve the gems for download and install.<br /><br />To install the private gems into the target web server where the ROR application will be deployed,log in and run the following command.<br /><br /><span style="font-weight:bold;">gem install <gem name> --source http://<private gem server>/<Base Directory><span style="font-style:italic;"></span></span><br /><br />The correct version of the gems will be installed and thus the application integrity will be maintained.We need to rerun the <span style="font-weight:bold;"><span style="font-style:italic;">gem generate_index command</span></span> each time we add or remove a gem from the private gem server.Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-39113981703077243632009-04-15T17:52:00.009+06:002010-03-10T09:51:57.856+06:00Replicate the SQL IN keyword in LINQ to SQL<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoPLxq5N8CwG45zJyOeSRIV2aUkl0smU8xwNywQn3khNygERcCJ8yVegieV7Mqw__BN7sXAfJFXQIDqNfpIOGio8Gn27mow8SYOPE4P8GsgHjCsekmbOd8N6DVnN6DQZQeBkp6uHjeSHQ/s1600-h/replicate.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 110px; height: 106px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoPLxq5N8CwG45zJyOeSRIV2aUkl0smU8xwNywQn3khNygERcCJ8yVegieV7Mqw__BN7sXAfJFXQIDqNfpIOGio8Gn27mow8SYOPE4P8GsgHjCsekmbOd8N6DVnN6DQZQeBkp6uHjeSHQ/s320/replicate.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446847916905061234" /></a><br /><br />LINQ To SQL has made the life of .NET developers easier by replicating most of the familiar SQL keywords (SELECT,FROM,WHERE,JOIN..) within the scope of the language.It is easier to construct a LINQ to SQL expression for the developers who are used to in raw SQL syntax against different databases.One of the important SQL keyword is "IN" which is used frequently in the "WHERE" clause of SQL. <br /><br />For example:<br /><br /><span style="font-weight:bold;">SELECT CustomerID,Name FROM Customers WHERE City IN ('Dhaka','Rajshahi','Chittagong','Khulna')</span><br /><br />While constructing this query in LINQ to SQL, the usual solution may come to the mind like this:<br /><br /><span style="font-weight:bold;">C#<br /><br />string listOfCity = "Dhaka,Rajshahi,Chittagong,Khulna";<br /><br />MyDataContext context = new MyDataContext ();<br /><br />var result = from cust in context.Customers<br /> where cust.City in listOfCity <br /> select cust.CustomerID,cust.Name;<br /><br />return result.ToList();</span><br /><br />Actually the above expression does not work and with a little<br />twist we can make it up and running.The correct code is:<br /><br /><span style="font-weight:bold;">List<string> listOfCity = new List<string>(); <br /><br />listOfCity.Add("Dhaka"); <br />listOfCity.Add("Rajshahi"); <br />listOfCity.Add("Chittagong"); <br />listOfCity.Add("Khulna"); <br /><br />/* We can also use array of string as the following<br /><br /> string[] listOfCity = { "Dhaka","Rajshahi","Chittagong","Khulna" }; */<br /><br />MyDataContext context = new MyDataContext ();<br /><br />var result = from cust in context.Customers<br /> where listOfCity.Contains(cust.City) <br /> select cust.CustomerID,cust.Name ;<br /><br />return result.ToList();</span><br /><br />The trick is that we define a generic list of string or an array of string.Then we use the <span style="font-weight:bold;"><List>.Contains(<Database column name>)</span> syntax to replicate the SQL "IN" syntax in LINQ To SQL.Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com1tag:blogger.com,1999:blog-4146618975601730487.post-70558992587735240392009-04-06T19:00:00.004+06:002010-03-10T09:54:03.363+06:00Decision making in SSIS depending upon condition<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyI31MUMhYO03_sVyfcGxsBetJcRkCPjkL9AtdkxLub8CRgrQISMJ_QH5IAvaN6NW9N7wPdT5-7TJCITdVW2vd5jDVcGyVRklUF_RnyO69oZPKSospp3E6f0QvVlG85DbFxgdD8bzyrTQ/s1600-h/decision.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 112px; height: 111px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyI31MUMhYO03_sVyfcGxsBetJcRkCPjkL9AtdkxLub8CRgrQISMJ_QH5IAvaN6NW9N7wPdT5-7TJCITdVW2vd5jDVcGyVRklUF_RnyO69oZPKSospp3E6f0QvVlG85DbFxgdD8bzyrTQ/s320/decision.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446848460090135106" /></a><br /><br />SSIS(SQL Server Integration Service) is not much flexible in the case of decision making based on a condition. In many cases you feel the need of a “Condition” block that can be used in the control flow to decide the alternative paths. For example if you want to copy a file named “AnyFile.txt” into a folder named “Current” if the file <span style="font-weight:bold;">does not exist</span> in a folder named “Previous”, otherwise move the file to a folder named “Archive”, there is no direct way to test the <span style="font-weight:bold;">“If File Exists in Previous Folder”</span> condition. One possible solution is to use the “Script Task”. The steps are as follows:<br /><br />1. Declare a package level Boolean variable named “IsFileExistsInPrevious” for example.<br /><br />2. Create a script task and pass the “IsFileExistsInPrevious” variable as “ReadWriteVariable” into it. Pass other “ReadOnly” variables that contain the name of the file if necessary.<br /><br />3. Using the components in the .NET “System.IO” namespace, check the existence of the file in the “Previous” folder. If the file exists, set the value of the “IsFileExistsInPrevious” variables to “True”. Otherwise set it to “False”.<br /><br /><span style="font-weight:bold;">VB .NET Script:<span style="font-style:italic;"></span></span><br /><br /> <span style="font-weight:bold;">Dim srcFilePath As String = "<The full path of the file to check>"<br /><br /> If File.Exists(srcFilePath ) Then<br /> Dts.Variables("IsFileExistsInPrevious").Value = True<br /> Else<br /> Dts.Variables("IsFileExistsInPrevious").Value = False<br /><br /> End If<br /> Dts.TaskResult = Dts.Results.Success</span><br /><br />4. Declare two “File System” tasks that copies files to the “Current” or “Archive” folders depending on the result of the previous “Script Task”. Drag the “Precedence Constraint” line from the previous “Script Task” to any of the following “File System” tasks. <br /><br />5. Right click the “Precedence Constraint” line and Select “Edit”. In the “Precedence Constraint Editor” window, Select the value of “Evaluation Operation” to “Expression”. Type the following into the “Expression” box.<br /><br /> <span style="font-weight:bold;"> @[User::IsFileExistsInPrevious]==false</span><br /><br />6. Drag the “Precedence Constraint line to the “File System” task that copies the file to the “Current” folder.<br /><br />7. Right click the “Precedence Constraint” line again and Select “Edit”. In the “Precedence Constraint Editor” window, Select the value of “Evaluation Operation” to “Expression”. Type the following into the “Expression” box.<br /><br /> <span style="font-weight:bold;"> @[User::IsFileExistsInPrevious]==true</span><br /> <br />8. Drag this “Precedence Constraint line to the “File System” task that copies the file to the “Archive” folder.<br /><br />The key point to note here is the usage of the “Precedence Constraint” editor.The “Expression” option enables to select alternative paths depending on condition (the value of a user variable “IsFileExistsInPrevious” in this case).Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com2tag:blogger.com,1999:blog-4146618975601730487.post-66576780865600432582009-03-31T16:39:00.003+06:002010-03-10T09:55:18.611+06:00Setting the FTP password from external configuration sources in a SSIS package<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWIiyuUm5cFvyogOXK4Q1pK7Ofp5XkFDrByLcJzVtqZrA6Xyl8ifn1XDxrUoyCNK5qZ3Fryw5aEPMhyphenhyphen8kjQwnAy6Jn9x6GxWzKz4USLEYteVODRO9CpXjt81_lle16LgwLiGdlO_rPsiQ/s1600-h/ftppass.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 135px; height: 79px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWIiyuUm5cFvyogOXK4Q1pK7Ofp5XkFDrByLcJzVtqZrA6Xyl8ifn1XDxrUoyCNK5qZ3Fryw5aEPMhyphenhyphen8kjQwnAy6Jn9x6GxWzKz4USLEYteVODRO9CpXjt81_lle16LgwLiGdlO_rPsiQ/s320/ftppass.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446848671028301922" /></a><br /><br />SSIS(SQL Server Integration Service) is a great ETL(Extract Transform Load) tool from Microsoft.It has many useful features for integrating data from different sources.One of the handy feature is the "FTP Task" that enables downloading files from a remote FTP address. The "FTP Task" uses a "FTP Connection Manager" component that stores the FTP credentials(URL,User,Password..). Instead of hard coding the credentials into the component, we can use “Expressions” feature of SSIS to set those properties from configuration files,registry entries or environment variables. So we can set the FTP URL,port,user name from the configuration sources.Unfortunately and surprisingly there is no option in the “Expressions” list to set the FTP password.The “Server Password” option just does not appear in the “Expression” List.One work around of this problem is to use the “Script Task” component and set the “Server Password” property of the “FTP Connection Manager” component. The VB .NET code (SQL 2005 only supports VB .NET) snippet below does the following:<br /><br />1.Accesses the “FTP Connection Manager” component.<br />2.Sets the “ServerPassword” property with the value of a package variable(“FTPPassword” here).The package variable can be set from any external configuration sources supported by SSIS(XML file,Environment Variable,Registry..).<br /><br /><span style="font-weight:bold;"><br />Imports System<br />Imports System.Data<br />Imports System.Math<br />Imports Microsoft.SqlServer.Dts.Runtime<br /><br />Public Class ScriptMain<br /><br /> Public Sub Main()<br /><br /> Dim FTPConnectionManager As ConnectionManager<br /><br /><br /> FTPConnectionManager = Dts.Connections("FTP_Conn")<br /><br /> <br /> FTPConnectionManager.Properties("ServerPassword").SetValue(FTPConnectionManager, <br /> Dts.Variables("FTPPassword").Value)<br /><br /> Dts.TaskResult = Dts.Results.Success<br /><br /> End Sub<br /><br />End Class<br /><span style="font-style:italic;"></span></span><br /><br />Note:The “Script Task” component must execute before the “FTP Task” component .Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com1tag:blogger.com,1999:blog-4146618975601730487.post-13616462562878893982009-03-16T22:27:00.003+06:002010-03-10T09:56:58.173+06:00Adding a serial number field to the ASP .NET GridView<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYjyE_89HoXt3TjDAZS76W08opPX2fRuJi6Qh_SevImX8pCbezOnWFTeTEVeiiWJpVjoluWfiXPdn-MwKE3RJc7sYScG-4Vc8Jnn3vpymSpVSNUPtBWxOafmPC21dZkoRBpSoLm5zyYTU/s1600-h/queue1.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 104px; height: 120px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYjyE_89HoXt3TjDAZS76W08opPX2fRuJi6Qh_SevImX8pCbezOnWFTeTEVeiiWJpVjoluWfiXPdn-MwKE3RJc7sYScG-4Vc8Jnn3vpymSpVSNUPtBWxOafmPC21dZkoRBpSoLm5zyYTU/s320/queue1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446849211405814514" /></a><br /><br />I was developing a page that displays some data from database in a ASP .NET GridView control through LINQ. The grid required to display a serial number field as the first column of each row.Since i was binding the result of a LINQ call(List<SomeObject>) to the grid, i had to add a new field in the "Entity/Model" Class itself and populate the data somehow before binding it to the Grid. I was not willing to add a "Serial #" field into the class only to display a serial number and was looking for a solution at the View Layer.Then i found this handy solution built into the "GridView" control itself.<br /><br /><asp:gridview id="myGridView" runat="server" autogeneratecolumns="False"><br /> <br /> <columns><br /> <asp:templatefield headertext="#" width="15"><br /> <itemtemplate><br /> <b><%# Container.DataItemIndex + 1 %></b><br /> </itemtemplate><br /> </asp:templatefield><br /> </columns><br /></asp:gridview><br /><br />The "Container.DataItemIndex" property does the trick and provides an index for each row item in the GridView. Since the index starts from 0, I only had to increase it by 1 to display a serial number to every row without altering the Data Access Layer. Quite useful isn't it ?Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-36765953688671793532009-02-18T19:50:00.003+06:002010-03-10T09:57:43.496+06:00Passing security credentials to C# 2.0 Web Service using SOAP Header<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0RKotE1GFZh-R1DBIo19gDV1HFuSWhfcRZSo1NxrG7TYv0ijTy4w6ASM86ZeMfEC01apIfS4dCIE-8C3aDl-A4TFBcgwBbi9iaQpISIpl3pM_WmJafsBcM2XLkb-9PkF-3gkc_ULTcHE/s1600-h/security.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 122px; height: 139px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0RKotE1GFZh-R1DBIo19gDV1HFuSWhfcRZSo1NxrG7TYv0ijTy4w6ASM86ZeMfEC01apIfS4dCIE-8C3aDl-A4TFBcgwBbi9iaQpISIpl3pM_WmJafsBcM2XLkb-9PkF-3gkc_ULTcHE/s320/security.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446849398268397570" /></a><br /><br />In a project ,we had a web service that required the client to pass security credentials (user id and password) while calling some of the web methods. First we thought to add two additional parameters (user id and password) to every web method that required authentication. This strategy looked very repetitive and made the method parameter list longer. After exploring for a while we discovered a nice solution of passing credentials through SOAP header. To implement this we need to perform the following steps:<br /><br /><span style="font-weight: bold; font-style: italic;">Web Service:</span><br /><br />1. Inherit a class from the “System.Web.Services.Protocols.SoapHeader” class that contains the security credential related fields. Put this class into the same file that contains the web service class.<br /><br /><span style="font-weight: bold;">public class SecurityInfo : SoapHeader</span><br /><span style="font-weight: bold;">{</span><br /><span style="font-weight: bold;"> public string UserID;</span><br /><span style="font-weight: bold;"> public string Password;</span><br /><span style="font-weight: bold;">}</span><br /><br />2. Add a public field into the web service class of the type “SecurityInfo”.<br /><br /> <span style="font-weight: bold;">public SecurityInfo SecurityCredentials = new SecurityInfo();</span><br /><br />3. Add a “SoapHeader” attribute to the web method that requires access to security credentials passed by the client application. The parameter name of the “SoapHeader” attribute must be same as the public field declared in the above step (“SecurityCredentials” in this case).<br /><br /><br /> [WebMethod(Description = "A method that requires authentication")]<br /> <span style="font-weight: bold;">[SoapHeader("SecurityCredentials")]</span><br /> public void PerformASensitiveTask()<br /> {<br /> //Call the authentication method here with the security credential.<br /> <br /> Authenticate(SecurityCredentials);<br /><br /> }<br /><br /> [WebMethod(Description = "Another method that requires<br /> authentication")]<br /> <span style="font-weight: bold;">[SoapHeader("SecurityCredentials")]</span><br /> public void PerformAnotherSensitiveTask()<br /> {<br /> //Call the authentication method here with the security credential.<br /> Authenticate(SecurityCredentials);<br /><br /> }<br /><br /><br /> private void Authenticate(<span style="font-weight: bold;">SecurityInfo credentials</span>)<br /> {<br />// validation logic goes here. An exception is thrown if not validated.<br /> string userId = <span style="font-weight: bold;">credentials.UserID;</span><br /> string password = <span style="font-weight: bold;">credentials.Password;</span><br /> //.....<br /> }<br /><br /><br />So the final web service code looks like this:<br /><br /><br />public class TestService : System.Web.Services.WebService<br />{<br /> public SecurityInfo SecurityCredentials = new SecurityInfo();<br /> public TestService()<br /> {<br />}<br /> <br />[WebMethod(Description = "A method that requires authentication")]<br /> [SoapHeader("SecurityCredentials")]<br /> public void PerformASensitiveTask()<br /> {<br /> //Call the authentication method here with the security credential.<br /> Authenticate(SecurityCredentials);<br /> }<br /><br /> [WebMethod(Description = "Another method that requires authentication")]<br /> [SoapHeader("SecurityCredentials")]<br /> public void PerformAnotherSensitiveTask()<br /> {<br /> //Call the authentication method here with the security credential.<br /> Authenticate(SecurityCredentials);<br /> }<br /><br /> private void Authenticate(SecurityInfo credentials)<br /> {<br /> // validation logic goes here. An exception is thrown if not validated.<br /> string userId = credentials.UserID;<br /> string password = credentials.Password;<br /> //.....<br /> }<br /><br />}<br /><br />public class SecurityInfo : SoapHeader<br />{<br /> public string UserID;<br /> public string Password;<br />}<br /><br /><br /><span style="font-weight: bold; font-style: italic;">Web Service Consumer (Console App):</span><br /><br />After adding a web reference to the web service, Visual Studio automatically adds a “SecurityInfoValue” property to the consumer-side proxy. The security credential class “SecurityInfo” is also exposed to the client application.We just need to instantiate an object of this class,set the credentials and set it into the “SecurityInfoValue” property of the client side proxy class of the web service.We can call multiple web methods after that with the same instance in the current scope.All the web methods will have access to the security credentials.The client code looks like this:<br /><br /><br /><br /> static void Main(string[] args)<br /> {<br /> TestWebService.TestService srvc = new<br /> ServiceTest.TestWebService.TestService();<br /><br /> <span style="font-weight: bold;">TestWebService.SecurityInfo sec = new</span><br /><span style="font-weight: bold;"> TestWebService.SecurityInfo();</span><br /><br /> <span style="font-weight: bold;">sec.UserID = "ID";</span><br /><span style="font-weight: bold;"> sec.Password = "Password";</span><br /><span style="font-weight: bold;"> srvc.SecurityInfoValue = sec;</span><br /><br /> srvc.PerformASensitiveTask();<br /><br /> srvc.PerformAnotherSensitiveTask();<br /><br /> }<br /><br /><br />I think using “Soap Header” to pass credentials to multiple web methods of a web service is really an elegant solution.Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-36908776570621847202009-02-06T00:13:00.003+06:002010-03-10T10:03:59.455+06:00When to apply OOAD concepts and Design Patterns?<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghxRPg8vUSXjJbLDv3Kcl10IjznzNRBfKKFD5Qt2jswCg7Fwi71fTig7WoTODrsHNKuTeAbf_vHiXQ113xMNj174KwgcSC421447F4KlqwCJB9iIcYeHLsuspbRqgqCsFVadDAnI46UpY/s1600-h/design1.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 107px; height: 143px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghxRPg8vUSXjJbLDv3Kcl10IjznzNRBfKKFD5Qt2jswCg7Fwi71fTig7WoTODrsHNKuTeAbf_vHiXQ113xMNj174KwgcSC421447F4KlqwCJB9iIcYeHLsuspbRqgqCsFVadDAnI46UpY/s320/design1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446849833565169266" /></a><br /><br />I strongly believe that the concepts like OOAD (Object Oriented Analysis and Design) and Design Patterns contribute to make an application well designed and flexible in a very positive way. But over the years I have seen myself and some of my colleagues struggling to decide where to start first in the software design phase when a new project arrives. <br /><br /><span style="font-weight:bold;">Should we list all the memorized design patterns first and search in the requirement list for the scope to apply those? </span><br /><br /><span style="font-weight:bold;">Should we think of the basic OO concepts like encapsulation, polymorphism or inheritance (“is-a” or “has-a”) and start designing the classes according to those at the first place ?</span><br /><br />Often many developers loose the way on their inability to prioritize these processes and thinks too much of theoretical Analysis and Design without concentrating on solving the problem on hand first. I have also faced this problem which is popularly termed as “Analysis Paralysis” in the Software Analysis and Design Books. <br /><br />I have found an excellent guideline of prioritizing these processes in the book “Head First Object Oriented Analysis and Design” by O’Reilly publication. The guideline suggests that:<br /><br /><span style="font-weight:bold;">1. We should concentrate on solving the problem on hand first with the best possible algorithm and make sure that the software does exactly what the customer expects from it. In a nutshell, if the customer does not recognize the validity of the software, all hard work on Analysis, design, patterns, best practices, latest and greatest technologies gone in vain my friend.<br /><br />2. After making the software workable, we should apply basic OO principles to make the software flexible by eliminating the duplicate codes and improving a bad design.<br /><br />3. Finally we should strive for applying proven design patterns so that the software is easy to extend and reuse.</span><br /><br />I just wanted to share these principles and priorities with you and believe that remembering the priorities will be helpful to choose the right path while designing and developing software.Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-67793310604631347092009-01-08T18:38:00.003+06:002010-03-10T10:00:56.871+06:00Tracking the completion of a file upload or copy using .NET FileSystemWatcher component<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSZCyckmp6dyhWZK4_jPK_qyHn6ryPHuvi8EW6J6k1ToFFC2Nn2POcUxGydPxTUlT_lvdbqntzLz9AMp4snHQq3YhzCUFGb8vjRceTHMGfidqxnaI28c-O4iiNw96ZcQhwgGYFVEK_RyI/s1600-h/watcher.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 99px; height: 116px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSZCyckmp6dyhWZK4_jPK_qyHn6ryPHuvi8EW6J6k1ToFFC2Nn2POcUxGydPxTUlT_lvdbqntzLz9AMp4snHQq3YhzCUFGb8vjRceTHMGfidqxnaI28c-O4iiNw96ZcQhwgGYFVEK_RyI/s320/watcher.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446850241358343202" /></a><br /><br />I had an assignment to process the files uploaded by users into a FTP location.Files used be removed removed after the processing was complete.I had developed a .NET Windows Service in the FTP server machine to watch the FTP directory for the uploaded files.The service used to track the files as soon uploaded and start processing.I had used the .NET "FileSystemWatcher" component to monitor the directory.The problem was that,as soon the file was created in the directory(the upload by user had just started), the service used to start processing it.Normally it takes a while for the completion of file upload specially if the size is larger.So the service was processing incomplete files before the upload is complete.After exploring the property,methods and events of the extremely useful "FileSystemWatcher" component, i found the solution to track when the file is completely uploaded.<br /><br />To mark the upload completion, a temporary file with a ".flag" extension used to be created that matches exactly the name of the uploaded file.For example if the file name is "Data.zip", the name of the temporary file would be "Data.zip.flag".Any valid and unused extension other than ".flag" could have been used here.The service code is now changed to look for the files with ".flag" extension to understand that the file is fully uploaded or copied.It then extracts the file name from the ".flag" file name and starts the normal processing.<br /><br />Here is the code:<br /><br />static void Main(string[] args)<br />{<br /> FileSystemWatcher watcher = new FileSystemWatcher();<br /> watcher.Path = "C:\\Temp";<br /> watcher.Filter = "*.zip";<br /> watcher.NotifyFilter = (NotifyFilters.LastWrite);<br /> watcher.Changed += new FileSystemEventHandler(watcher_Changed);<br /> watcher.EnableRaisingEvents = true;<br /> Console.ReadLine();<br />}<br /><br />static void watcher_Changed(object sender, FileSystemEventArgs e)<br />{<br /> FileInfo fi = new FileInfo(e.FullPath);<br /> <br /> if (fi.Length > 0 && !File.Exists(e.FullPath + ".flag"))<br /> {<br /> <br /> File.Create(e.FullPath + ".flag");<br /><br /> }<br /> <br />}<br /><br />The "path" property of "FileSystemWatcher" component is used to mention the directory to watch for and the "filter" property is used to mention file name or file types.But the crucial property for this particular task is the "NotifyFilter" property that is set to "NotifyFilters.LastWrite" here. This will enable the component to notify when the final byte of the file is written to disk.<br /><br />The "changed" event of the component is handled to get notified when the file upload is complete.Here we have checked the file size to be sure that it is not zero and also checking whether the temporary file exists.If the file has a size(greater than zero) and temporary file does not exist, we are creating the temporary file.The same event is fired multiple times by .NET Framework.So the checking is being done to create the temporary file only once.Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-36702272447010975532009-01-07T12:42:00.004+06:002010-03-10T10:03:01.288+06:00Retrieve the name of the currently executing method inside the same method.<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiox5V_wHFUjIdnd7YwlqS7n4qOtZoiZRRStE1C-xoG-nE5iWz9Y8dPHE1mr5Y8uNrexQVdNtdTer26c-3VU1EyK64oPmMHdKKChn9Mbl1_Rygu4Pr2x5QwUSkteIhhRvoVDlT36s45JK8/s1600-h/running.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 130px; height: 130px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiox5V_wHFUjIdnd7YwlqS7n4qOtZoiZRRStE1C-xoG-nE5iWz9Y8dPHE1mr5Y8uNrexQVdNtdTer26c-3VU1EyK64oPmMHdKKChn9Mbl1_Rygu4Pr2x5QwUSkteIhhRvoVDlT36s45JK8/s320/running.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446850775088811554" /></a><br /><br />In one of my projects, the requirement was to log the error information inside a method if an exception occurs. I needed to record the current method name along with the error message. The code was like this:<br />public void DoSomething()<br />{<br /> try<br /> {<br /> //The method code goes here.<br /> }<br /> catch (Exception ex)<br /> {<br /> <span style="font-weight:bold;">WriteLog(<span style="font-style:italic;">"DoSomething"</span>, ex.ToString());</span><br /> //The first parameter of WriteLog method is the name of the current method.<br /> }<br />}<br /><br />I was not happy with the way I had to hard code the name of the current method(“DoSomething” here). This way I had to hard code the names of every method where I needed to log the error information.This required to change every method name in the lines of log writing if i had to change the method name.So I was looking for a built in feature that will provide me the current method name.I searched for a while and found a handy solution in the “System.Diagnostics” namespace of the .NET Framework.Here is the code to automatically get the name of the current method.<br /><br />public void DoSomething()<br />{<br /> try<br /> {<br /> //The method code goes here.<br /> }<br /> catch (Exception ex)<br /> {<br /> <span style="font-weight:bold;">string currentMethodName = new <br /> System.Diagnostics.StackTrace().GetFrame(0).GetMethod().Name;</span><br /> //The "GetMethod" method returns a "MethodBase" object.With <br /> //this we can access the name of the method and <br /> //other valuable information like access <br /> //modifier,parameters,return types e.tc.//<br /><br /> <span style="font-weight:bold;"> WriteLog(<span style="font-style:italic;">currentMethodName</span>, ex.ToString());</span><br /> }<br /> }<br />I found the API provided by the “System.Diagnostics” namespace really interesting and useful.Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0tag:blogger.com,1999:blog-4146618975601730487.post-18067770762906746592008-12-20T18:50:00.002+06:002010-03-10T10:03:59.465+06:00Using the ASP .NET "_doPostBack()" method reference in Javascript<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipTWJaMSvN1DnPv7yot21bSPeTsia_I8-WGhSP4-6nK2xkE-t8dBF5MQmmEBiiGyRdu517kMd9RUH2PVIhkilyX49U6E7CaJLC6BQ1mzT3f9VP7aoIcDQjKztwlt3Zwoc1TaQMXTJPfag/s1600-h/post.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 119px; height: 119px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipTWJaMSvN1DnPv7yot21bSPeTsia_I8-WGhSP4-6nK2xkE-t8dBF5MQmmEBiiGyRdu517kMd9RUH2PVIhkilyX49U6E7CaJLC6BQ1mzT3f9VP7aoIcDQjKztwlt3Zwoc1TaQMXTJPfag/s320/post.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446850495075206994" /></a><br /><br />I was assigned to do a task that will need to invoke a javascript method that will perform some client side control state changes and finally submit the page with the "_doPostBack" javascript function. This "_doPostBack" function template is auto generated by the ASP .NET framework and is called when a post back to the server is required as a result of the interaction between the user with the ASP .NET Server Controls(Ex:button click, drop down list selection changes) .<br /><br />I had first gone for the easy solution and hard coded the function call like this:<br /><br /><script><br /> function DoSomeTask()<br />{<br /> //Do some client side tasks here.<br /> __doPostBack('<control>','');//posts the page back<br />}<br /></script><br /><br />I then thought should i rely on the hard coded "__doPostBack" method call? What if This function is not generated by ASP .NET under certain conditions? What if the name of the function differs in different browsers? What if Microsoft replaces the function name in the next version of ASP .NET (this application was developed with 1.1) ?<br /><br />So i searched on the net and found a nice solution. At the server side ,the "GetPostBackClientHyperlink" method of the "Page" object returns the correct script block that does the post back for the ASP .NET server controls. The syntax is:<br /><br />Page.GetPostBackClientHyperlink(<control>,<argument>);<br /><br />So i changed the previous javascript block as:<br /><br /><script><br />function DoSomeTask()<br />{<br /> //Do some client side tasks here.<br /> <%= GetDoPostBackMethodSignature() %><br /> <br />}<br /></script><br /><br />Where the "GetDoPostBackMethodSignature()" method is declared at the code behind page as:<br /><br />protected string GetDoPostBackMethodSignature()<br />{<br /> return Page.GetPostBackClientHyperlink(<Server Control Instance >,string.Empty);<br /><br />}<br /><br />Now there is no confustion whether the generated function is named "__doPostBack" or "_sueBillGates".<br /><br />I trust ASP .NET fully(have no other alternative at the moment though:) ) and sure that my client side codes will not complain about not finding the "_doPostBack" function.<br /><br />Cheers!Muhammad Arifur Rahmanhttp://www.blogger.com/profile/03881402082962256352noreply@blogger.com0