Saturday, May 7, 2011

[Gd] Dev Channel Update

| More

Google Chrome Releases: Dev Channel Update

The Chrome Dev channel has been updated to 12.0.742.30 for all platforms.  This release contains an updated version of Flash.

If you find new issues, please let us know by filing a bug. Want to change to another Chrome release channel? Find out how.

Jason Kersey
Google Chrome
URL: http://googlechromereleases.blogspot.com/2011/05/dev-channel-update_06.html

[Gd] Beta and Stable Channel Update

| More

Google Chrome Releases: Beta and Stable Channel Update

The Beta and Stable channels have been updated to 11.0.696.65 for the Macintosh, Windows, Linux and Chrome Frame platforms

The following bugs were fixed:
  • After deleting bookmarks on the Bookmark managers, the bookmark bar doesn't display properly with existing bookmarks. (Issue 80580).
  • About Google Chrome window shows unknown channel for 11.0.696.57 (Issue 80683).
  • Chrome/Mac seems to clobber focus when uploading attachments to Gmail with the flash-based uploader (Issue 77172).
  • Also included is an updated version of Flash Player 10.2.
If you find new issues, please let us know by filing a bug.

Want to change to another Chrome release channel? Find out how.

Karen Grunberg
Google Chrome
URL: http://googlechromereleases.blogspot.com/2011/05/beta-and-stable-channel-update.html

[Gd] Chrome Dev Channel Update

| More

Google Chrome Releases: Chrome Dev Channel Update

The Chrome Dev channel has been updated to 12.0.742.21 for all platforms. This release contains fixes for a number of UI, performance, and stability issues.  It also contains an update for Mac [bug 80602] that fixes garbage being displayed when the accelerated compositor was active.  This primarily affected 3D CSS transforms and WebGL content. The full list of changes is available from the SVN revision log. If you find new issues, please let us know by filing a bug. Want to change to another Chrome release channel? Find out how.

Jason Kersey
Google Chrome
URL: http://googlechromereleases.blogspot.com/2011/05/chrome-dev-channel-update.html

[Gd] Royal Wedding Bells in the Cloud

| More

Google App Engine Blog: Royal Wedding Bells in the Cloud

As the centuries pass, waves of change wash over us - the invention of the steam engine, photography, space travel, the Internet. We may think these inventions change everything. But some things stay the same. Like our world-wide romantic fascination with a Princess finding her Prince, and their First Royal Kiss.









And so it was only fitting that the highest traffic levels ever on Google App Engine occurred at the moment of the first (and second!) kiss between The Duke and Duchess of Cambridge.




The Royal Household announced in March, that the Royal Wedding would have its own official web site, and it would be hosted on Google App Engine. The site was a great team effort between The Royal Household, Accenture, who built the site, the web design agency Reading Room who led on the design and creative work, and the Google App Engine team.




The Official Royal Wedding web site generated over 2,000 requests per second, serving 15 million page views from 5.6 million visitors on the day of the wedding. Despite this load, the web site ran smoothly, serving off Google’s shared platform without disturbing the other 200,000+ apps, which collectively serve over 1.5 billion views per day.




However, from a load perspective, the really busy Google App Engine app was the YouTube Royal Channel site. This app's traffic peaked at 32,000 requests per second (!) - with an additional 10,000 requests per second during the 10 seconds around the kiss(es).




Whilst others were watching for a glimpse of The Dress, we were watching for a cloud platform non-event: the Google App Engine platform continued to operate normally. Both our wishes came true.




If you’re planning an event of similar scale yourself, we’d love to hear about it.




Our congratulations to the Royal Couple, and best wishes for the future!




Peter S Magnusson

Engineering Director

On behalf of the App Engine and Google team

URL: http://googleappengine.blogspot.com/2011/05/royal-wedding-bells-in-cloud.html

[Gd] Who's at Google I/O: Elastic Path

| More

Google App Engine Blog: Who's at Google I/O: Elastic Path

This post is part of Who's at Google I/O, a series of guest blog posts written by developers who are appearing in the Developer Sandbox at Google I/O. It's also cross-posted to the Google Code blog which has similar posts for all sorts of Google developer products.



Elastic Path develops a very flexible enterprise ecommerce platform. Many global brands rely on the Elastic Path platform to power their ecommerce solutions.



Many ecommerce sites are actually complex web applications. Catalog management, shopping cart functionality, promotion engine, order fulfillment, and backend integrations are just some of the challenges involved in running a full-fledged online store.



Since 2008, our Java-based platform has been the ecommerce backbone of a couple of online stores that are being migrated to run on App Engine. Like many complex web applications, these stores used to run in a multi-server environment (Apache Tomcat with a MySQL database) hosted in a colocation center.






As the diagram above shows, our goal is to have Elastic Path running entirely on the App Engine cloud. The storefronts have already been migrated, and the database and remaining parts of the Elastic Path platform will be fully on the cloud soon.



Why are we doing this? There are many benefits to being on App Engine:


  • Increased security

  • Easier deployments and operations

  • Scalability

  • Cost-effectiveness

  • Built-in monitoring


We can only undergo this migration because App Engine supports enterprise-grade Java applications, and because Elastic Path is very flexible.



Our migration’s high-level approach was to move everything except the persistence layer onto App Engine, and then resolve issues with the technical limitations such as the class whitelist and request length. We also had to modify some third-party libraries to work around App Engine’s restrictions on operations such as class loading, threads, and sockets.



We didn’t migrate the persistence layer because Elastic Path uses a relational database; converting our entire object graph to the Datastore is not feasible now. We are working closely with Google on alternatives. In the interim, we are still using a MySQL database and have kept our persistence layer running within a Tomcat application in the colo. We implemented a creative solution: the non-persistence layers of Elastic Path run on App Engine and communicate with the Tomcat-hosted persistence services via Spring Remoting. The back-and-forth remoting was expensive and impacted the performance of our application so we implemented some data caching. For this, we turned to App Engine’s Memcache, which improved performance by an order of magnitude (less than 2 seconds average response times vs. 2 minutes or more without Memcache).



Other App Engine technologies we use heavily include AppStats for performance tuning, URL Fetch for the Spring Remoting described above, and the fantastic Maven GAE plugin that we use for packaging and automated deployments. As we continue to push our platform up to the cloud, we hope to utilize more of App Engine’s cool features. If you’d like to learn more about Elastic Path, how we are migrating our Java platform to run on the cloud, and how you might be able to migrate your application to App Engine, drop by our booth in the App Engine section of the Developer Sandbox. See you there!



Come see Elastic Path Software in the Developer Sandbox at Google I/O on May 10-11.



Eddie Chan is an ecommerce developer at Elastic Path Software in beautiful Vancouver, Canada. He and his brilliant team work closely with Google and are currently focused on migrating existing online stores to App Engine.



Posted by Scott Knaster, Editor

URL: http://googleappengine.blogspot.com/2011/05/whos-at-google-io-elastic-path.html

[Gd] Who's at Google I/O: Mojo Helpdesk

| More

Google App Engine Blog: Who's at Google I/O: Mojo Helpdesk

This post is part of Who's at Google I/O, a series of guest blog posts written by developers who are appearing in the Developer Sandbox at Google I/O. It's also cross-posted to the Google Code blog which has similar posts for all sorts of Google developer products.



Mojo Helpdesk from Metadot is an RDBMS-based Rails application for ticket tracking and management that can handle millions of tickets. We are migrating this application to run on Google App Engine (GAE), Java, and Google Web Toolkit (GWT). We were motivated to make this move because of the application’s need for scalability in data management and request handling, the benefits from access to GAE’s services and administrative tools, and GWT’s support for easy development of a rich application front-end.



In this post, we focus on GAE and share some techniques that have been useful in the migration process.



Task failure management



Our application makes heavy use of the Task Queue service, and must detect and manage tasks that are being retried multiple times but aren’t succeeding. To do this, we extended Deferred, which allows easy task definition and deployment. We defined a new Task abstraction, which implements an extended Deferrable and requires that every Task implement an onFailure method. Our extension of Deferred then terminates a Task permanently if it exceeds a threshold on retries, and calls its onFailure method.



This allows permanent task failure to be reliably exposed as an application-level event, and handled appropriately. (Similar techniques could be used to extend the new official Deferred API).







From the existing Mojo Helpdesk: a view of a user’s assigned tickets.



Appengine-mapreduce



Mojo Helpdesk needs to run many types of batch jobs, and appengine-mapreduce is of great utility. However, we often want to map over a filtered subset of Datastore entities, and our map implementations are JDO-based (to enforce consistent application semantics), so we don’t need low-level Entities prefetched.
 So, we made two extensions to the mapper libraries. First, we support the specification of filters on the mapper’s Datastore sharding and fetch queries, so that a job need not iterate over all the entities of a Kind. Second, our mapper fetch does a keys-only Datastore query; only the keys are provided to the map method, then the full data objects are obtained via JDO. These changes let us run large JDO-based mapreduce jobs with much greater efficiency.



Supporting transaction semantics



The Datastore supports transactions only on entities in the same entity group. Often, operations on multiple entities must be performed atomically, but grouping is infeasible due to the contention that would result. We make heavy use of transactional tasks to circumvent this restriction. (If a task is launched within a transaction, it will be run if and only if the transaction commits). A group of activities performed in this manner – the initiating method and its transactional tasks – can be viewed as a “transactional unit” with shared semantics.



We have made this concept explicit by creating a framework to support definition, invocation, and automatic logging of transactional units. (The Task abstraction above is used to identify cases where a transactional task does not succeed). All Datastore-related application actions – both in RPC methods and "offline" activities like mapreduce – use this framework. This approach has helped to make our application robust, by enforcing application-wide consistency in transaction semantics, and in the process, standardizing the events and logging which feed the app’s workflow systems.







From the existing Mojo Helpdesk: a view of the unassigned tickets for a work group.



Entity Design



To support join-like functionality, we can exploit multi-valued Entity properties (list properties) and the query support they provide. For example, a Ticket includes a list of associated Tag IDs, and Tag objects include a list of Ticket IDs they’re used with. This lets us very efficiently fetch, for example, all Tickets tagged with a conjunction of keywords, or any Tags that a set of tickets has in common. (We have found the use of "index entities" to be effective in this context). We also store derived counts and categorizations in order to sidestep Datastore restrictions on query formulation.



These patterns have helped us build an app whose components run efficiently and robustly, interacting in a loosely coupled manner.



Come see Mojo Helpdesk in the Developer Sandbox at Google I/O on May 10-11.



Amy (@amygdala) has recently co-authored (with Daniel Guermeur) a book on Google App Engine and GWT application development. She has worked at several startups, in academia, and in industrial R&D labs; consults and does technical training and course development in web technologies; and is a contributor to the @thinkupapp open source project.



Posted by Scott Knaster, Editor

URL: http://googleappengine.blogspot.com/2011/05/whos-at-google-io-mojo-helpdesk.html

[Gd] How Google Tests Software - A Break for Q&A

| More

Google Testing Blog: How Google Tests Software - A Break for Q&A

By James Whittaker

New material for the this series is coming more slowly. I am beginning to get into areas where I want to start posting screen shots of internal Google tools and describe how our infrastructure works. This is material that takes longer to develop and also requires some scrutiny before being published externally. So in the meantime, I am pausing to answer some of the questions you've posted in the comments.

I am going to start with Lilia (because she likes Neil Young mainly, but also because she can run further than me and those two things combine to impress me to no small end) who asks about SET-SWE conversion and vice-versa and which I have seen the most. There is also the broader question of whether there is a ceiling on the SET career path.

SETs and SWEs are on the same pay scale and virtually the same job ladder. Both roles are essentially 100% coding roles with the former writing test code and the latter doing feature development. From a coding perspective the skill set is a dead match. From a testing perspective we expect a lot more from SETs. But the overlap on coding makes SETs a great fit for SWE positions and vice versa. Personally I think it is a very healthy situation to have conversions. Since I have both roles reporting to me I can speak from first hand experience that many of my best coders are former SETs and some of my best testers are former SWEs. Each is excellent training ground for the other. On my specific team I am even on the conversions from one role to the other. But I suspect that Google-wide there are more SETs who become SWEs.

Why convert in the first place? Well at Google it isn't for the money. It also isn't for the prestige as we have a lot more SWEs than SETs and it is a lot harder to standout. The scarcity of our SETs creates somewhat of a mystique about these folk. Who are these rare creatures who keep our code bases healthy and make our development process run so smoothly? Actually, most SWEs care more about making the SETs happy so they continue doing what they do. Why would any dev team force a conversion of a great developer from SET to SWE when finding a suitable SET replacement is so much harder than adding another feature developer? SWEs ain't that stupid.

Now pausing before I take another hit of the corp kool-aid, let me be honest and say that there are far more senior SWEs than SETs. Percentage wise we test folk are more outnumbered at the top of the org than at the middle and bottom. But keep in mind that developers have had a large head start on us. We have developers who have been at Google since our founding and testers ... well ... less time than that.

Where do TEs fit into this mix? TE is an even newer role than SET but already we have a number climbing to the Staff ranks and pushing on the senior most positions in the company. There is no ceiling, but the journey to the top takes some time.

Raghev among others has asked about the career path and whether remaining an IC (individual contributor) is an option over becoming a manager. I have mixed feelings about answering this. As a manager myself, I see the role as one with much honor and yet I hear in your collective voices a hint of why do I have to become a manager? Ok, I admit, Dilbert is funny.

For me, being a manager is a chance to impart some of my experience and old-guy judgement on less experienced but more technically gifted ICs. The combination of an experienced manager's vision and an ICs technical skill can be a fighting force of incredible power. And yet, why should someone who does not want to manage be forced to do so in order to continue their career advancement?

Well, fortunately, Google does not make us choose. Our managers are expected to have IC tasks they perform. They are expected to be engaged technically and lead as opposed to just manage. And our ICs are expected to have influence beyond their personal work area. When you get to the senior/staff positions here you are a leader, period. Some leaders lead more than they manage and some leaders manage more than they lead.

But either way, the view from the top means that a lot of people are looking to you for direction ... whether you manage them or not.
URL: http://googletesting.blogspot.com/2011/05/how-google-tests-software-break-for-q.html

Friday, May 6, 2011

[Gd] Moving accessibility forward on Android

| More

The official Google Code blog: Moving accessibility forward on Android


By Eduard Sánchez of Code Factory

This post is part of Who's at Google I/O, a series of guest blog posts written by developers who are appearing in the Developer Sandbox at Google I/O.


For the last 8 years we at Code Factory have been making software that helps the blind and the visually impaired access their mobile phones. We’ve created this software for several different platforms. Last year we decided it was time to start doing something for the Android platform, due to its growing popularity and variety of devices.

From our past experience, developing a screen reader for a new platform required a lot of work, hacks, and investigation. Almost none of the previous platforms we supported implemented any sort of Accessibility API that we could use. Android, we thought, would be no exception to this rule. We were very wrong.

Starting at version 1.6, the Android operating system comes with a built-in Accessibility API that makes our application a lot easier to develop. All you do is create a service, which implements the AccessibilityService interface, declare it in your manifest and voilà! The system will start sending events, such as button presses, list navigation, focus changes, etc. to your service. You then convert this information to voice using a Text-to-Speech engine, and you have a screen reader.

The Accessibility API is not yet as complete as what you can find on a desktop PC, but it's good enough to provide the users with basic user interface navigation, and we have no doubt that, as the Android platform evolves, so will the built-in Accessibility API.

We also wanted our application to go beyond a screen reader and provide an intuitive, easy-to-use UI that allowed the blind and visually impaired access to most of the phone's functionality, such as messaging, web browsing, contact management, and so on.


We were pleased to see that we could do this Android. The existing set of UI controls, such as buttons and lists, can be overridden in order to provide custom functionality, such as speaking the text of the control. This made it possible for us to keep the user interface of our application consistent with Android, while at the same time providing the speech feedback that our users require.

By intercepting touch events within our application and using the gesture detectors that Android provides to developers, we were also able to make the touch screen accessible to our users, so they can use gestures like swipes to move through items of lists, or double-taps to activate items.

We really like how much we can accomplish with Android with so little code. Want to let a blind person create an SMS or email using voice? Simply use the SpeechRecognizer class. Want blind users who are walking on the street to know their exact location? Just use the LocationManager and Geocoder classes to give their exact street name and number.

Android lets us do a lot in a very efficient way. It wraps a whole bunch of cool technology into well-defined classes and interfaces. And if at any given time you need to know how something works behind the scenes, you just take a look at the source code, which is freely available to everyone.

We just can't wait to do more on this platform.


Come see Code Factory in the Developer Sandbox at Google I/O on May 10-11.

A pioneer in assistive technology for mobile phones, Eduard Sánchez is the brain behind all Code Factory software applications. His greatest satisfaction is to use his passion for programming to make a positive difference in the lives of people with disabilities.

Posted by Scott Knaster, Editor
URL: http://googlecode.blogspot.com/2011/05/moving-accessibility-forward-on-android.html

[Gd] NET-A-PORTER.COM brings fashion and commerce to Google TV

| More

The official Google Code blog: NET-A-PORTER.COM brings fashion and commerce to Google TV


By Daniels Lee of the Google TV Developer Relations Team

NET-A-PORTER.COM is a premium online luxury fashion retailer and is the first of its kind to present commerce on Google TV. They developed a 10-foot web app called NET-A-PORTER TV which offers a unique way to view and shop fashion directly from your living room. In this post, I'll briefly describe the user experience followed by some technical points shared by the NET-A-PORTER Labs team.

NET-A-PORTER TV users can watch a variety of videos featuring designer runway shows, interviews, and additional feature clips. Users can also browse their collection of luxury name brand items from premium designers like Phillip Lim, Alexander Wang, Michael Kors, Erdem, and others. When you watch runway videos, a vertical product carousel is displayed showing items matching the runway model's outfit. The product carousel dynamically updates and stays in sync with the video. While browsing, you can select to view more details about any specific item and add it to your shopping cart. When you’re done, you can complete your transaction on the original site.



Contextually matching merchandise items based on video playback is not only compelling to users but also makes online shopping a bit more more interactive and enjoyable. This adds a nice touch which facilitates a low-pressure and casual experience, great for the living room context.

The NET-A-PORTER Labs team split the web app into two major sections, video and user interface. For video, they used a lightweight JavaScript MVC wrapper around a chrome-less Brightcove player. The model fetches video data, the view manages video playback, and the controller ties the two together. For user interface, they relied on jQuery to navigate the DOM and make their app keyboard navigable. The team created a custom event handling system which captures key events and calls handler functions corresponding to a specific set of elements. They also used CSS3 transitions to animate vertical scrolling of the product carousel.



To take a deeper technical dive into the inner workings of NET-A-PORTER TV, be sure to check out their article written by James Christian and Scott Seaward from the NET-A-PORTER Labs team. This article describes everything from the initial concept to interface design and even to video production.

We thank the team at NET-A-PORTER.COM for their continued dedication, support, and persistence in improving their Google TV web app. We look forward to seeing what they come up with next.

Daniels Lee is a longtime Developer Programs Engineer, fostering relations with several developer communities since joining the the team in August 2006. He openly confesses his love for JavaScript and recognizes its profound ability to make the web more interactive.

Posted by Scott Knaster, Editor
URL: http://googlecode.blogspot.com/2011/05/net-portercom-brings-fashion-and.html

[Gd] Flash support in Instant Previews

| More

Official Google Webmaster Central Blog: Flash support in Instant Previews

Webmaster level: All

With Instant Previews, users can see a snapshot of a search result before clicking on it. We’ve made a number of improvements to the feature since its introduction last November, and if you own a site, one of the most relevant changes for you is that Instant Previews now supports Flash.



An Instant Preview with rich content rendered


In most cases, when the preview for a page is generated through our regular crawl, we will now render a snapshot of any Flash components on the page. This will replace the "puzzle piece" icon that previously appeared to indicate Flash components, and should improve the accuracy of the previews.

However, for pages that are fetched on demand by the "Google Web Preview" user-agent, we will generate a preview without Flash in order to minimize latency. In these cases the preview will appear as if the page were visited by someone using a browser without Flash enabled, and "Install Flash" messages may appear in the preview, depending on how your website handles users without Flash.

To improve your previews for these on-demand renders, here are some guidelines for using Flash on your site:
  • Make sure that your site has a reasonable, seamless experience for visitors without Flash. This may involve creating HTML-only equivalents for your Flash-based content that will automatically be shown to visitors who can't view Flash. Providing a good experience for this case will improve your preview and make your visitors happier.

  • If Flash components are rendering but appear as loading screens instead of actual content, try reducing the loading time for the component. This makes it more likely we'll render it properly.

  • If you have Flash videos on your site, consider submitting a Video Sitemap which helps us to generate thumbnails for your videos in Instant Previews.

  • If most of the page is rendering properly but you still see puzzle pieces appearing for some smaller components, these may be fixed in future crawls of your page.
If you have additional questions, please feel free to post them in our Webmaster Help Forum.

As always, we'll keep you updated as we continue to make improvements to Instant Previews.

Posted by Raj Krishnan, Product Manager
URL: http://googlewebmastercentral.blogspot.com/2011/05/flash-support-in-instant-previews.html

Thursday, May 5, 2011

[Gd] Security advisory to websites using OpenID Attribute Exchange

| More

The official Google Code blog: Security advisory to websites using OpenID Attribute Exchange


By Mayank Upadhyay, Google Security Team

A group of security researchers recently identified a flaw in how some OpenID relying parties implement Attribute Exchange (AX) that could cause an authentication bypass vulnerability. Google is a strong supporter of federated login on the web and would like to help spread awareness of this issue to websites that are OpenID relying parties in order to protect the users of those websites. This issue primarily impacts websites that act as relying parties using the OpenID4Java library.

The researchers determined that the affected sites were not confirming that certain information passed through AX was properly signed. If the site was only using AX to receive information like the user’s self-asserted gender, then this issue would be minor. However, if it was being used to receive security-sensitive information that only the identity provider should assert, then the consequences could be worse.

A specific scenario identified involves a website that accepts an unsigned AX attribute for email address, and then logs the user in to a local account on that website associated with the email address. When a website asks Google’s OpenID provider (IDP) for someone’s email address, we always sign it in a way that cannot be replaced by an attacker. However, many websites do not ask for email addresses for privacy reasons among others, and so it is a perfectly legitimate response for the IDP to not include this attribute by default. An attacker could forge an OpenID request that doesn’t ask for the user’s email address, and then insert an unsigned email address into the IDPs response. If the attacker relays this response to a website that doesn’t notice that this attribute is unsigned, the website may be tricked into logging the attacker in to any local account.

The researchers contacted the primary websites they identified with this vulnerability, and those sites have already deployed a fix. Similarly, Google and other OpenID Foundation members have worked to identify many other websites that were impacted and have helped them deploy a fix. There are no known cases of this attack being exploited at this point in time.

A detailed explanation of the use of claimed IDs and email addresses can be found in Google’s OpenID best practices.

Google would like to thank security researchers Rui Wang, Shuo Chen and XiaoFeng Wang for reporting their findings. The OpenID Foundation has also done a similar blog post on the issue.

Action Required:
  1. If you are an OpenID relying party, then you should read the Suggested Fix section below to see if this vulnerability might apply to you, and what to do about it.
  2. If you are an application developer that uses OpenID relying party services from someone else, like your container provider or some network intermediary, please read the Suggested Fix section to see if your service is listed there. Otherwise, you should check with that entity to make sure they are not susceptible to this issue.

Suggested Fix:

As a first step, we recommend modifying vulnerable relying parties to accept AX attribute values only when signed, irrespective of how those attributes might get used.

During our investigation we confirmed that apps using the OpenID4Java library, with or without the Step2 wrapper, are prone to accepting unsigned AX attributes. OpenID4Java has been patched with the fix in version 0.9.6.662 (19th April, 2011).

Kay Framework was known to be vulnerable and has since been patched. Users should upgrade to version 1.0.2 or later. Note that Google App Engine developers that use its built-in OpenID support do not need to do anything.

Other libraries may have the same issue, although we do not believe that the default usage of OpenID services and libraries from Janrain, Ping Identity and DotNetOpenAuth are susceptible to this attack. However, the defaults may be overridden and you should double check your code for that.

We also suggest reviewing your usage of email addresses retrieved via OpenID to ensure that adequate safeguards are in place. A detailed explanation of the use of claimed IDs and email addresses can be found in our OpenID best practices published for Apps Marketplace developers that also apply to relying parties in general.


Mayank Upadhyay works on authentication and identity problems on the Google Security Team.
His previous experience includes similar work at Sun Microsystems and some startups in the WiFi security space.


Posted by Scott Knaster, Editor
URL: http://googlecode.blogspot.com/2011/05/security-advisory-to-websites-using.html

[Gd] Elastic Path: commerce in the cloud

| More

The official Google Code blog: Elastic Path: commerce in the cloud


By Eddie Chan, Software Engineer at Elastic Path Software

This post is part of Who's at Google I/O, a series of guest blog posts written by developers who are appearing in the Developer Sandbox at Google I/O.


Elastic Path develops a very flexible enterprise ecommerce platform. Many global brands rely on the Elastic Path platform to power their ecommerce solutions.

Many ecommerce sites are actually complex web applications. Catalog management, shopping cart functionality, promotion engine, order fulfillment, and backend integrations are just some of the challenges involved in running a full-fledged online store.

Since 2008, our Java-based platform has been the ecommerce backbone of a couple of online stores that are being migrated to run on App Engine. Like many complex web applications, these stores used to run in a multi-server environment (Apache Tomcat with a MySQL database) hosted in a colocation center.


As the diagram above shows, our goal is to have Elastic Path running entirely on the App Engine cloud. The storefronts have already been migrated, and the database and remaining parts of the Elastic Path platform will be fully on the cloud soon.

Why are we doing this? There are many benefits to being on App Engine:
  • Increased security
  • Easier deployments and operations
  • Scalability
  • Cost-effectiveness
  • Built-in monitoring
We can only undergo this migration because App Engine supports enterprise-grade Java applications, and because Elastic Path is very flexible.

Our migration’s high-level approach was to move everything except the persistence layer onto App Engine, and then resolve issues with the technical limitations such as the class whitelist and request length. We also had to modify some third-party libraries to work around App Engine’s restrictions on operations such as class loading, threads, and sockets.

We didn’t migrate the persistence layer because Elastic Path uses a relational database; converting our entire object graph to the Datastore is not feasible now. We are working closely with Google on alternatives. In the interim, we are still using a MySQL database and have kept our persistence layer running within a Tomcat application in the colo. We implemented a creative solution: the non-persistence layers of Elastic Path run on App Engine and communicate with the Tomcat-hosted persistence services via Spring Remoting. The back-and-forth remoting was expensive and impacted the performance of our application so we implemented some data caching. For this, we turned to App Engine’s Memcache, which improved performance by an order of magnitude (less than 2 seconds average response times vs. 2 minutes or more without Memcache).

Other App Engine technologies we use heavily include AppStats for performance tuning, URL Fetch for the Spring Remoting described above, and the fantastic Maven GAE plugin that we use for packaging and automated deployments. As we continue to push our platform up to the cloud, we hope to utilize more of App Engine’s cool features. If you’d like to learn more about Elastic Path, how we are migrating our Java platform to run on the cloud, and how you might be able to migrate your application to App Engine, drop by our booth in the App Engine section of the Developer Sandbox. See you there!


Come see Elastic Path Software in the Developer Sandbox at Google I/O on May 10-11.

Eddie Chan is an ecommerce developer at Elastic Path Software in beautiful Vancouver, Canada. He and his brilliant team work closely with Google and are currently focused on migrating existing online stores to App Engine.

Posted by Scott Knaster, Editor
URL: http://googlecode.blogspot.com/2011/05/elastic-path-commerce-in-cloud.html

[Gd] Designing large applications for Google App Engine

| More

The official Google Code blog: Designing large applications for Google App Engine


By Amy Unruh, Developer, Author, Consultant

This post is part of Who's at Google I/O, a series of guest blog posts written by developers who are appearing in the Developer Sandbox at Google I/O.


Mojo Helpdesk from Metadot is an RDBMS-based Rails application for ticket tracking and management that can handle millions of tickets. We are migrating this application to run on Google App Engine (GAE), Java, and Google Web Toolkit (GWT). We were motivated to make this move because of the application’s need for scalability in data management and request handling, the benefits from access to GAE’s services and administrative tools, and GWT’s support for easy development of a rich application front-end.

In this post, we focus on GAE and share some techniques that have been useful in the migration process.

Task failure management

Our application makes heavy use of the Task Queue service, and must detect and manage tasks that are being retried multiple times but aren’t succeeding. To do this, we extended Deferred, which allows easy task definition and deployment. We defined a new Task abstraction, which implements an extended Deferrable and requires that every Task implement an onFailure method. Our extension of Deferred then terminates a Task permanently if it exceeds a threshold on retries, and calls its onFailure method.

This allows permanent task failure to be reliably exposed as an application-level event, and handled appropriately. (Similar techniques could be used to extend the new official Deferred API).

From the existing Mojo Helpdesk: a view of a user’s assigned tickets.

Appengine-mapreduce

Mojo Helpdesk needs to run many types of batch jobs, and appengine-mapreduce is of great utility. However, we often want to map over a filtered subset of Datastore entities, and our map implementations are JDO-based (to enforce consistent application semantics), so we don’t need low-level Entities prefetched.
 So, we made two extensions to the mapper libraries. First, we support the specification of filters on the mapper’s Datastore sharding and fetch queries, so that a job need not iterate over all the entities of a Kind. Second, our mapper fetch does a keys-only Datastore query; only the keys are provided to the map method, then the full data objects are obtained via JDO. These changes let us run large JDO-based mapreduce jobs with much greater efficiency.

Supporting transaction semantics

The Datastore supports transactions only on entities in the same entity group. Often, operations on multiple entities must be performed atomically, but grouping is infeasible due to the contention that would result. We make heavy use of transactional tasks to circumvent this restriction. (If a task is launched within a transaction, it will be run if and only if the transaction commits). A group of activities performed in this manner – the initiating method and its transactional tasks – can be viewed as a “transactional unit” with shared semantics.

We have made this concept explicit by creating a framework to support definition, invocation, and automatic logging of transactional units. (The Task abstraction above is used to identify cases where a transactional task does not succeed). All Datastore-related application actions – both in RPC methods and "offline" activities like mapreduce – use this framework. This approach has helped to make our application robust, by enforcing application-wide consistency in transaction semantics, and in the process, standardizing the events and logging which feed the app’s workflow systems.

From the existing Mojo Helpdesk: a view of the unassigned tickets for a work group.

Entity Design

To support join-like functionality, we can exploit multi-valued Entity properties (list properties) and the query support they provide. For example, a Ticket includes a list of associated Tag IDs, and Tag objects include a list of Ticket IDs they’re used with. This lets us very efficiently fetch, for example, all Tickets tagged with a conjunction of keywords, or any Tags that a set of tickets has in common. (We have found the use of "index entities" to be effective in this context). We also store derived counts and categorizations in order to sidestep Datastore restrictions on query formulation.

These patterns have helped us build an app whose components run efficiently and robustly, interacting in a loosely coupled manner.


Come see Mojo Helpdesk in the Developer Sandbox at Google I/O on May 10-11.

Amy (@amygdala) has recently co-authored (with Daniel Guermeur) a book on Google App Engine and GWT application development. She has worked at several startups, in academia, and in industrial R&D labs; consults and does technical training and course development in web technologies; and is a contributor to the @thinkupapp open source project.

Posted by Scott Knaster, Editor
URL: http://googlecode.blogspot.com/2011/05/designing-large-applications-for-google.html

[Gd] Measure page load time with Google Analytics

| More

The official Google Code blog: Measure page load time with Google Analytics

Zhiheng
Phil
By Zhiheng Wang, Make the Web Faster Team, and Phil Mui, Google Analytics Team

At Google, we’re passionate about speed and making the web faster, and we’re glad to see that many website owners share the same idea. A faster web is better for both users and businesses. A slow-loading landing page not only impacts your conversion rate, but can also impact AdWords Landing Page Quality and ranking in Google search.

To improve the performance of your pages, you first need to measure and diagnose the speed of a page, which can be a difficult task. Furthermore, even with page speed measurements, it’s critical to look at page speed in the context of other web analytics data.

Therefore, we are thrilled to announce the availability of the Site Speed report in Google Analytics. With the Site Speed report you can measure the page load time across your site right within your Google Analytics account.


Uses for the Site Speed report

With the Site Speed report, not only will you be able to monitor the speed of your pages, you can also analyze it along with other analytics data, such as:
  • Content: Which landing pages are slowest?
  • Traffic sources: Which campaigns correspond to faster page loads overall?
  • Visitor: How does page load time vary across geographies?
  • Technology: Does your site load faster or slower for different browsers?

Setting up the Site Speed report

For now, page speed measurement is turned off by default, so you’ll only see 0s in the Site Speed report until you’ve enabled it. To start measuring site speed, you need to make a small change to your Analytics tracking code. We have detailed instructions in the Site Speed article in the Analytics Help Center. Once you’ve updated your tracking code, a small sample of pageviews will be used to calculate the page load time.

Bringing the Site Speed report into Google Analytics is an important step of the Make the Web Faster effort, and we look forward to your feedback on Site Speed.


Zhiheng Wang spends most of his time at work building stuff so others can serve the web better. He spends the rest of his time at home fixing stuff so his family can surf the web better.

Phil Mui is the Group Product Manager of Google Analytics and has been leading its development since its early days. He has a Ph.D. from MIT and a M.Phil. from Oxford where he was a Marshall Scholar.

Posted by Scott Knaster, Editor
URL: http://googlecode.blogspot.com/2011/05/measure-page-load-time-with-google.html

[Gd] Updating JavaScript Benchmarks for Modern Browsers

| More

Chromium Blog: Updating JavaScript Benchmarks for Modern Browsers

Benchmarks are incredibly important for influencing the direction of JavaScript engines. Over the past two years, JavaScript has gotten dramatically faster across all major browsers, particularly in areas that popular benchmarks measure. As it turns out, browser developers are a competitive bunch. However, as JS engines get faster, benchmarks must evolve in order to keep pushing them in the right direction.

We’ve been constantly updating our V8 benchmark suite to force us to get faster in areas that are important to web developers. We’ve fixed bugs and added new tests for regular expressions and memory management. We’re very focused on JavaScript performance, so we scrutinize our benchmark carefully to make sure that it’s as useful a measuring stick as possible.

The two other widely cited JS benchmarks are SunSpider from Apple, and Kraken, a new benchmark from Mozilla.

SunSpider was one of the first suites of tests, first appearing in 2007. It’s done a lot to help improve JS engines, but many of the tests in the suite are less relevant to the web in 2011. Even for the more relevant tests, JavaScript has gotten so fast that many finish in only a few milliseconds. This just isn’t long enough to figure out which engine is faster--a golf cart and a Tesla will finish a 10-foot race in nearly the same time.

To make the benchmark more meaningful, we’ve experimented by making the race longer by running each of the tests in SunSpider 50 times consecutively. While repeating a trivial test many times isn’t a great solution, it does provide an opportunity for some optimization. With this change, the results begin to reflect Chrome’s true performance. It’s more than 30% faster on the SunSpider suite overall and up to 4x faster on some of the individual tests.

Kraken, a more modern benchmark, is in better shape. Unfortunately, the canonical version of the benchmark has not been updated to reflect all the latest changes which address at least one important bug. As a result, the benchmark is less useful and has even (mis)led us to spend time making some irrelevant optimizations in Chrome. To help us focus on the right things, we’re now testing the latest version of Kraken built directly from Mozilla’s source code repository.

We’re posting a modified version of SunSpider and the latest version of Kraken to make it easy to run the benchmarks in your own browser and compare results.

Posted by Mike Belshe, Software Engineer
URL: http://blog.chromium.org/2011/05/updating-javascript-benchmarks-for.html

[Gd] Conversion Tracking in v201101

| More

AdWords API Blog: Conversion Tracking in v201101


With the release of v201101 we introduce the ConversionTrackerService to manage conversion tracking via the AdWords API. This new service lets you create, retrieve, modify and remove AdWordsConversionTracker objects, which hold all the information about a specific conversion type that resides in your account, including ID, name, status, category, and others, but more importantly the conversion code snippet you need to put on your website in order to fully implement conversion tracking. The main objective of this post is to show the basic operations that can be performed by using this service.

Creation

Let’s start by creating a "PURCHASE" AdWordsConversionTracker that specifies the one line site stats logo, HTML markup in the code snippet, and that your website is using HTTPS:


// Create AdWords conversion.
AdWordsConversionTracker adWordsConversionTracker =
    new AdWordsConversionTracker();
adWordsConversionTracker.setName("A purchase conversion tracker");
adWordsConversionTracker.setCategory(ConversionTrackerCategory.PURCHASE);
adWordsConversionTracker.setMarkupLanguage(
    AdWordsConversionTrackerMarkupLanguage.HTML);
adWordsConversionTracker.setHttpProtocol(
    AdWordsConversionTrackerHttpProtocol.HTTPS);
adWordsConversionTracker.setTextFormat(
    AdWordsConversionTrackerTextFormat.ONE_LINE);
adWordsConversionTracker.setUserRevenueValue("<%= shoppingCartTotal %>");

// Create operations.
ConversionTrackerOperation operation = new ConversionTrackerOperation();
operation.setOperator(Operator.ADD);
operation.setOperand(adWordsConversionTracker);
ConversionTrackerOperation[] operations =
    new ConversionTrackerOperation[] {operation};
// Add conversion tracker.
conversionTrackerService.mutate(operations);


AdWordsConversionTracker offers a set of properties that control what the conversion code snippet will look like. Let’s go over some of them:

markupLanguage: Determines the markup language of the code snippet. In most cases this will be HTML but CHTML, XHTML and WML are also supported, mostly for mobile devices that are not fully HTML capable.

httpProtocol: The protocol used by your web page, either HTTP or HTTPS. If this setting is incorrectly specified your visitors will experience warnings like "Page includes other resources which are not secure".

textFormat, conversionPageLanguage and backgroundColor: We encourage you to use visible text for conversion tracking when using AdWords conversion tracking technology. For this purpose two textFormat options are offered: ONE_LINE and TWO_LINE. conversionPageLanguage and backgroundColor control the language in which the text will be displayed and what background color will be used, respectively. If you decide to not use Google's site logo, you can specify HIDDEN for the textFormat. In this case, setting conversionPageLanguage and backgroundColor has no effects.

userRevenueValue: This value can be either fixed or dynamically set. For specific examples on how to set this value based on your server-side technology refer to the AdWords Conversion Tracking Setup Guide. Examples of its use would be a user shopping cart total value for a "PURCHASE" conversion or a fixed weight value you give to a specific "LEAD" conversion. This value is only meaningful to you in reports and it has no effect in AdWords costs. The conversion values can then be reported back to you using the reporting capabilities of the API by including fields such as ConversionValue in your reports.

Retrieval

As with most of our services, ConversionTrackerService offers a get method for you to retrieve ConversionTracker objects. This operation is based on generic selectors, so you can filter, sort and paginate results as desired.

The following example shows you how to retrieve the Id, Name and Category of all your ConversionTracker objects.


// Create selector.
Selector selector = new Selector();
selector.setFields(new String[] {"Name", "Status", "Category"});
selector.setOrdering(new OrderBy[] {new OrderBy("Name",
    SortOrder.ASCENDING)});

// Get all conversion trackers.
ConversionTrackerPage page = service.get(selector);

// Display conversion trackers.
if (page != null && page.getEntries() != null) {
  for (ConversionTracker conversionTracker : page.getEntries()) {
    if (conversionTracker instanceof AdWordsConversionTracker) {
      AdWordsConversionTracker awConversionTracker =
          (AdWordsConversionTracker) conversionTracker;
      System.out.printf("Conversion tracker with id \"%d\", " +
          "name \"%s\", status \"%s\", category \"%s\" and " +
          "snippet \"%s\" was found.\n", awConversionTracker.getId(),
          awConversionTracker.getName(),
          awConversionTracker.getStatus(),
          awConversionTracker.getCategory(),
          awConversionTracker.getSnippet());
    }
  }
}


Notice that even though the Snippet field was not requested in the selector, it will always be returned in your results. It contains the code you need to put in your web page to start capturing conversions. We recommend that you put this code as close as possible to the footer of the page.

Update

As you can create and retrieve ConversionTracker objects, it is also possible to update most of their properties using the service mutate operation. One of the most common operations is to disable a conversion tracker, hence stop capturing conversions even if the code snippet still resides in your website. The following code shows how to disable a conversion tracker.


long conversionId =
    Long.parseLong("CONVERSION_ID_YOU_WANT_TO_DISABLE");

// Create conversion tracker with updated status and name.
AdWordsConversionTracker adWordsConversionTracker =
    new AdWordsConversionTracker();
adWordsConversionTracker.setId(conversionId);
adWordsConversionTracker.setStatus(ConversionTrackerStatus.DISABLED);

// Create operations.
ConversionTrackerOperation operation = 

    new ConversionTrackerOperation();
operation.setOperand(adWordsConversionTracker);
operation.setOperator(Operator.SET);

ConversionTrackerOperation[] operations =
    new ConversionTrackerOperation[] {operation};

// Update conversion tracker.
ConversionTrackerReturnValue result = service.mutate(operations);


Reports

Most of the API reports contain conversion fields such as Conversions, ConversionValue, CostPerConversion, and others that you can query. Two conversion fields that allow you to segment conversion stats in your report are ConversionCategoryName and ConversionTypeName. Use ConversionCategoryName to group by conversion category (i.e., PURCHASE, LEAD, ...) and ConversionTypeName to group by ConversionTracker name, which is unique for each account. Keep in mind these two fields cannot be combined with non-conversion fields such as Impressions or Clicks.

User lists and conversions

A special mention goes to RemarketingUserList objects which are managed via the UserListService. This kind of list has a close relationship with conversions since every list is associated with at least one AdWords ConversionTracker. To retrieve the associated ConversionTracker objects, a RemarketingUserList contains a list of UserListConversionType objects which hold the IDs of the associated conversion trackers. Hence you can use those IDs to get, via the ConversionTrackerService, the code snippets required to fully enable your remarketing list. To learn more about user lists and remarketing in the AdWords API read the post on Remarketing.

All code examples in this post were developed using the AdWords API Java Client Library, but we also offer support for other popular languages. To learn more about our client libraries, visit our code site. As always, feel free to contact us via the AdWords API Forum.

  David Torres, AdWords API Team
URL: http://adwordsapi.blogspot.com/2011/05/conversion-tracking-in-v201101.html

[Gd] GWT and the Google Plugin for Eclipse 2.3: Final Release Now Available!

| More

Google Web Toolkit Blog: GWT and the Google Plugin for Eclipse 2.3: Final Release Now Available!


A few weeks ago we announced a Beta release of 2.3 that makes it easier to utilize Google’s Cloud within Eclipse. Since then we’ve been hard at work closing out issues and adding polish, and today we’re happy to announce that the final releases of GWT and the Google Plugin for Eclipse (GPE) are now available.




To get started, download GWT/GPE 2.3 here.




The key features in this release include:





For more info about each feature, click on the associated link above or check out the original blog post. We’ve also put together the following Intro to the Google Plugin for Eclipse screencast that walks you through all of the new features in GPE 2.3.










If you have any feedback, we’d love to hear it and the GWT Group is the right place to submit it.




Post by Chris Ramsdale, cramsdale@google.com

URL: http://googlewebtoolkit.blogspot.com/2011/05/gwt-and-google-plugin-for-eclipse-23.html

[Gd] Website Security for Webmasters

| More

Official Google Webmaster Central Blog: Website Security for Webmasters

Webmaster level: Intermediate to Advanced

Users are taught to protect themselves from malicious programs by installing sophisticated antivirus software, but often they may also entrust their private information to websites like yours, in which case it’s important to protect their data. It’s also very important to protect your own data; if you have an online store, you don’t want to be robbed.

Over the years companies and webmasters have learned—often the hard way—that web application security is not a joke; we’ve seen user passwords leaked due to SQL injection attacks, cookies stolen with XSS, and websites taken over by hackers due to negligent input validation.

Today we’ll show you some examples of how a web application can be exploited so you can learn from them; for this we’ll use Gruyere, an intentionally vulnerable application we use for security training internally, too. Do not probe others’ websites for vulnerabilities without permission as it may be perceived as hacking; but you’re welcome—nay, encouraged—to run tests on Gruyere.


Client state manipulation - What will happen if I alter the URL?

Let’s say you have an image hosting site and you’re using a PHP script to display the images users have uploaded:

http://www.example.com/showimage.php?imgloc=/garyillyes/kitten.jpg

So what will the application do if I alter the URL to something like this and userpasswords.txt is an actual file?

http://www.example.com/showimage.php?imgloc=/../../userpasswords.txt

Will I get the content of userpasswords.txt?

Another example of client state manipulation is when form fields are not validated. For instance, let’s say you have this form:



It seems that the username of the submitter is stored in a hidden input field. Well, that’s great! Does that mean that if I change the value of that field to another username, I can submit the form as that user? It may very well happen; the user input is apparently not authenticated with, for example, a token which can be verified on the server.
Imagine the situation if that form were part of your shopping cart and I modified the price of a $1000 item to $1, and then placed the order.

Protecting your application against this kind of attack is not easy; take a look at the third part of Gruyere to learn a few tips about how to defend your app.

Cross-site scripting (XSS) - User input can’t be trusted



A simple, harmless URL:
http://google-gruyere.appspot.com/611788451095/%3Cscript%3Ealert('0wn3d')%3C/script%3E
But is it truly harmless? If I decode the percent-encoded characters, I get:
<script>alert('0wn3d')</script>

Gruyere, just like many sites with custom error pages, is designed to include the path component in the HTML page. This can introduce security bugs, like XSS, as it introduces user input directly into the rendered HTML page of the web application. You might say, “It’s just an alert box, so what?” The thing is, if I can inject an alert box, I can most likely inject something else, too, and maybe steal your cookies which I could use to sign in to your site as you.

Another example is when the stored user input isn’t sanitized. Let’s say I write a comment on your blog; the comment is simple:
<a href=”javascript:alert(‘0wn3d’)”>Click here to see a kitten</a>

If other users click on my innocent link, I have their cookies:



You can learn how to find XSS vulnerabilities in your own web app and how to fix them in the second part of Gruyere; or, if you’re an advanced developer, take a look at the automatic escaping features in template systems we blogged about on our Online Security blog.

Cross-site request forgery (XSRF) - Should I trust requests from evil.com?

Oops, a broken picture. It can’t be dangerous--it’s broken, after all--which means that the URL of the image returns a 404 or it’s just malformed. Is that true in all of the cases?

No, it’s not! You can specify any URL as an image source, regardless of its content type. It can be an HTML page, a JavaScript file, or some other potentially malicious resource. In this case the image source was a simple page’s URL:



That page will only work if I’m logged in and I have some cookies set. Since I was actually logged in to the application, when the browser tried to fetch the image by accessing the image source URL, it also deleted my first snippet. This doesn’t sound particularly dangerous, but if I’m a bit familiar with the app, I could also invoke a URL which deletes a user’s profile or lets admins grant permissions for other users.

To protect your app against XSRF you should not allow state changing actions to be called via GET; the POST method was invented for this kind of state-changing request. This change alone may have mitigated the above attack, but usually it's not enough and you need to include an unpredictable value in all state changing requests to prevent XSRF. Please head to Gruyere if you want to learn more about XSRF.

Cross-site script inclusion (XSSI) - All your script are belong to us

Many sites today can dynamically update a page's content via asynchronous JavaScript requests that return JSON data. Sometimes, JSON can contain sensitive data, and if the correct precautions are not in place, it may be possible for an attacker to steal this sensitive information.

Let’s imagine the following scenario: I have created a standard HTML page and send you the link; since you trust me, you visit the link I sent you. The page contains only a few lines:
<script>function _feed(s) {alert("Your private snippet is: " + s['private_snippet']);}</script><script src="http://google-gruyere.appspot.com/611788451095/feed.gtl"></script>


Since you’re signed in to Gruyere and you have a private snippet, you’ll see an alert box on my page informing you about the contents of your snippet. As always, if I managed to fire up an alert box, I can do whatever else I want; in this case it was a simple snippet, but it could have been your biggest secret, too.

It’s not too hard to defend your app against XSSI, but it still requires careful thinking. You can use tokens as explained in the XSRF section, set your script to answer only POST requests, or simply start the JSON response with ‘\n’ to make sure the script is not executable.

SQL Injection - Still think user input is safe?

What will happen if I try to sign in to your app with a username like
JohnDoe’; DROP TABLE members;--

While this specific example won’t expose user data, it can cause great headaches because it has the potential to completely remove the SQL table where your app stores information about members.

Generally, you can protect your app from SQL injection with proactive thinking and input validation. First, are you sure the SQL user needs to have permission to execute “DROP TABLE members”? Wouldn’t it be enough to grant only SELECT rights? By setting the SQL user’s permissions carefully, you can avoid painful experiences and lots of troubles. You might also want to configure error reporting in such way that the database and its tables’ names aren’t exposed in the case of a failed query.
Second, as we learned in the XSS case, never trust user input: what looks like a login form to you, looks like a potential doorway to an attacker. Always sanitize and quotesafe the input that will be stored in a database, and whenever possible make use of statements generally referred to as prepared or parametrized statements available in most database programming interfaces.

Knowing how web applications can be exploited is the first step in understanding how to defend them. In light of this, we encourage you to take the Gruyere course, take other web security courses from the Google Code University and check out skipfish if you're looking for an automated web application security testing tool. If you have more questions please post them in our Webmaster Help Forum.

Written by Gary Illyes, Webmaster Trends Analyst
URL: http://googlewebmastercentral.blogspot.com/2011/05/website-security-for-webmasters.html