//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// .NET StockTrader Sample WCF Application for Benchmarking, Performance Analysis and Design Considerations for Service-Oriented Applications
// Note that for ClosedOrders alert control we use in-page script to generate our repeating table rows in the HTML.
// vs. using databound Repeater controls as we do in the AccountOrders (control), Portfolio (page), and MarketSummary (control).
// The choice of display method is up to the architect; Repeaters and GridViews have many features you do not
// get with in-page script; there is however a performance tradeoff; so we opted to use
// in-page script vs. Repeaters/Gridviews here becuase this control is embedded in every authenticated
// page.
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;
using System.Web.Security;
using Trade.BusinessServiceClient;
using Trade.StockTraderWebApplicationModelClasses;
using Trade.StockTraderWebApplicationSettings;
namespace Trade.Web
{
///
/// Checks for closed orders, and displays alert if any.
///
public partial class ClosedOrders : System.Web.UI.UserControl
{
public List closedOrderData;
protected override void OnLoad(EventArgs e)
{
//configlink.HRef = "http://" + HttpContext.Current.Server.MachineName + "/" + Settings.PAGE_PATH_CONFIG;
//Here we are going to use an absolute expiration and the .NET object cache to enable
//the application to optionally not execute the order alert query on every page; rather just check every
//n seconds (this setting is adjustable in Web.config). Executing the alert control
//logic on every page is not the best design: users can stand to get their completion alerts after 60
//seconds vs. right away, plus we are going to invalidate the cache entry
//anyway on an order being placed, so they will only have to wait if they place the order from
//another browser. The reduction on database queries is substantial (and impact on perf)
//simply by making this choice. They will STILL get an order alert within 60 seconds of
//browsing even if another program (such as the async Order Processor Service or any program) completes
//the order outside of the scope of the Web application.
//Order alerts and stock market summary/mkt index are the only two places in this app that really
//make sense to cache. All other data elements such as account info/balances, stock prices,
//holdings, orders etc. are not good candidates for caching in our opinion. These data elements
//should always reflect what is actually in the database. This is becuase other systems besides
//StockTrader would quite likely be changing this data in the real world, so invalidating the cache within StockTrader app
//(ala WebSphere Trade 6.1) does more harm than good--since the middle tier is completely unaware of
//what other applications may have done to change the database information being cached.
//On the other hand, order alerts and market summaries are excellent choices for caching:
//data here can be safely be refreshed every 30, 60 seconds (or more) without impacting data
//integrity or alarming a user with an inconsistent value.
//The .NET cache for this control will only be used if the Web.config setting
//"CheckOrderAlertsOnEveryRequest" = false. For benchmark comparisons, its important this be true
//if measuring .NET perf against WebSphere or other product if those products are not also caching data.
//In our published data, we used the "true" setting so the control is executed on every
//requested page, and alerts come up immediately as opposed to slightly delayed.
string userid = HttpContext.Current.User.Identity.Name;
if (userid == "")
{
HttpCookie authcookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authcookie == null)
{
FormsAuthentication.SignOut();
Response.Redirect(Settings.PAGE_LOGIN);
}
FormsAuthenticationTicket ticket = (FormsAuthenticationTicket)FormsAuthentication.Decrypt(authcookie.Value);
userid = ticket.Name;
}
if (Settings.CHECK_ORDER_ALERT_EVERY_REQUEST || Cache[Settings.CACHE_KEY_CLOSED_ORDERSALERT + userid] == null)
{
//Either the setting in web.config is set for checking on every page, or
//the timeout on our cache has expired. So we must invoke our BSL layer
//now to check for closed orders.
BSLClient businessServicesClient = new BSLClient();
closedOrderData = businessServicesClient.getClosedOrders(userid);
//We are not interested in actually caching any data here: after all, users only get notified
//via an alert 1 time per order. Rather, we are using the cache as a convenient way to
//ensure alert checks only happen based on our desired frequency.
if (!Settings.CHECK_ORDER_ALERT_EVERY_REQUEST)
Cache.Insert(Settings.CACHE_KEY_CLOSED_ORDERSALERT + userid, userid, null, System.DateTime.UtcNow.AddSeconds(Settings.ORDER_ALERT_CHECK_FREQUENCY), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
}
}
}
}