/*
* 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.
*/
package org.apache.openjpa.trader.client;
import java.util.List;
import org.apache.openjpa.trader.client.event.ServiceEvent;
import org.apache.openjpa.trader.client.ui.ErrorDialog;
import org.apache.openjpa.trader.client.ui.ScrollableTable;
import org.apache.openjpa.trader.domain.Ask;
import org.apache.openjpa.trader.domain.Bid;
import org.apache.openjpa.trader.domain.Stock;
import org.apache.openjpa.trader.domain.Trade;
import org.apache.openjpa.trader.domain.Trader;
import org.apache.openjpa.trader.service.TradingService;
import org.cobogw.gwt.user.client.ui.RoundedPanel;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.GwtEvent.Type;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* The GWT module for OpenTrader.
*
* <br><b>Initialization</b>:
* This module entry point acts as the Application Controller.
* It initializes the widget components and lays them out. As it is often the case, some of
* the widgets require initialization data.
* Hence, this entry point establishes connection to remote {@link TradingService} via
* {@link TradingServiceAdapterAsync asynchronous}, client-side, proxy stub. The GWT
* framework provides that stub when supplied with the original interface class.
* <br>
* <b>Operation</b>:
* Once initialized, the widgets operate relatively independently throughout the rest of the
* operations. Each widget provides a view and invokes server functions (asynchronously via
* RPC) to update the view.
* <br>
* <b>Event Management</b>:
* The widgets communicate to other widgets via this module entry point i.e.
* if the server callback results from a view action requires to update another view, then the request
* is relayed through this entry point instead of one view calling the other directly.
* The GWT framework does provide the core infrastructure for
* DOM and user event propagation. This entry point reuses the same infrastructure with
* the application-defined specialized {@link ServiceEvent <em>service events</em>} to represent
* the <em>business</em> functions such as a new Ask or Bid request is placed, or a Trade
* committed etc.
* <br>
* <b>Session State Management</b>: One of the core advantages of GWT framework (apart from ease
* of development and out-of-the-box cross-browser compatibility), is that GWT allows the client to
* maintain its own state, thereby opening up the possibility of state-less (or at least less
* stateful and hence more scalable) servers. This client maintains its session state in the
* {@link ScrollableTable#getModel() data models} of the core widgets. The only <em>common,
* shared</em> state held by this Application Controller is the logged-in {@link Trader}.
*
*
* @author Pinaki Poddar
*
*/
public class OpenTrader implements EntryPoint, UncaughtExceptionHandler {
// Event management
private HandlerManager eventBus;
// The main widget components
private MarketDataPanel stockPanel; // the market prices
private TradeOrderWindow orderPanel; // Creates a Ask/Bid request
private TradingWindow tradePanel; // Issues a trade order
private ScrollableTable<Trade> soldTradePanel; // displays committed trades
private ScrollableTable<Trade> boughtTradePanel; // displays committed trades
private ServerLogPanel serverPanel; // displays server logs
// Server-State variables as Session Identifier
private Trader trader;
private String _serviceURI;
// The handle to the remote service.
private TradingServiceAdapterAsync tradingService;
// Resource bundle for images etc.
public static final OpenTradeImageBundle bundle = GWT.create(OpenTradeImageBundle.class);
/**
* ------------------------------------------------------------------------
* The entry point for GWT module.
* ------------------------------------------------------------------------
*/
public void onModuleLoad() {
GWT.setUncaughtExceptionHandler(this);
eventBus = new HandlerManager(this);
new LoginDialog(this).center();
}
/**
* Gets the handle to the remote service. The service handle is the asynchronous interface
* but it is created by the GWT framework from the synchronous interface class literal.
*/
public TradingServiceAdapterAsync getService() {
if (tradingService == null) {
tradingService = GWT.create(TradingServiceAdapter.class);
}
return tradingService;
}
/**
* Gets the name of the trader as the name of this session.
*/
public String getName() {
return trader == null ? "" : trader.getName();
}
/**
* Gets the trader who is running this session.
*/
public Trader getTrader() {
return trader;
}
/**
* Gets all the traded stocks traded by the service.
* The stocks are maintained by the {@link ScrollableTable#getModel() data model} of
* the Market Data Panel widget.
*/
public List<Stock> getTradedStocks() {
return stockPanel.getModel();
}
String getServiceURI() {
return _serviceURI;
}
/**
* Builds up the widgets once the login is complete i.e. the server has supplied
* the initialization data.
*
*/
void init(Trader trader, String uri, List<Stock> stocks) {
this.trader = trader;
_serviceURI = uri;
Window.setTitle(trader.getName());
int W = Window.getClientWidth();
int H = 900;//Window.getClientHeight();
int headerHeight = 05*H/100;
int westWidth = 25*W/100; int westHeight = 80*H/100;
int centerWidth = 60*W/100; int centerHeight = 80*H/100;
int footerHeight = 02*H/100;
Unit unit = Unit.PX;
stockPanel = new MarketDataPanel(this, westWidth-10, 40*westHeight/100);
int N = stocks.size();
for (int i = 0; i < N; i++) {
stockPanel.insert(stocks.get(i));
}
soldTradePanel = new TradeHistoryPanel(this, Ask.class, westWidth-10, 20*westHeight/100);
boughtTradePanel = new TradeHistoryPanel(this, Bid.class, westWidth-10, 20*westHeight/100);
serverPanel = new ServerLogPanel(this, centerWidth, 40*centerHeight/100);
tradePanel = new TradingWindow(this, centerWidth, 40*centerHeight/100);
orderPanel = new TradeOrderWindow(this,centerWidth, 10*centerHeight/100);
FlowPanel west = new FlowPanel();
west.setSize((westWidth-10)+"px", westHeight+"px");
west.add(decorate(stockPanel));
west.add(new HTML("<p>"));
west.add(decorate(soldTradePanel));
west.add(new HTML("<p>"));
west.add(decorate(boughtTradePanel));
FlowPanel center = new FlowPanel();
center.setSize(centerWidth+"px", centerHeight+"px");
center.add(decorate(orderPanel));
center.add(new HTML("<p>"));
center.add(decorate(tradePanel));
center.add(new HTML("<p>"));
center.add(decorate(serverPanel));
DockLayoutPanel main = new DockLayoutPanel(unit);
main.addNorth(createHeader(), headerHeight);
main.addSouth(createFooter(), footerHeight);
main.addWest(west, westWidth);
main.add(center);
RootLayoutPanel.get().add(main);
main.animate(500);
setUpHelp();
stockPanel.startStockWatcher();
tradePanel.startTradableRefresher();
}
/**
* Decorates an widget by wrapping in a rounded panel (that seems to be cool thing nowadays).
*/
Widget decorate(Widget w) {
RoundedPanel rp = new RoundedPanel(w,RoundedPanel.ALL, 2);
rp.setBorderColor("#005B9A");
return rp;
}
/**
* Sets up a help page for each of the main widgets.
*
* @see ScrollableTable#addHelp(String)
*/
void setUpHelp() {
stockPanel.addHelp("help/MarketData.html");
tradePanel.addHelp("help/Trade.html");
orderPanel.addHelp("help/TradeOrder.html");
soldTradePanel.addHelp("help/CommittedTrade.html");
boughtTradePanel.addHelp("help/CommittedTrade.html");
serverPanel.addHelp("help/Logging.html");
}
/**
* Creates a header panel. Uses the image resources for a logo and a banner text.
*/
Widget createHeader() {
HorizontalPanel panel = new HorizontalPanel();
Image logo = new Image(bundle.logo());
HTML banner = new HTML("OpenTrader");
banner.setStylePrimaryName("header");
panel.add(logo);
panel.add(banner);
return panel;
}
/**
* Creates a footer panel.
*/
Widget createFooter() {
HorizontalPanel panel = new HorizontalPanel();
Label footer = new HTML("Built with OpenJPA/Slice and GWT");
footer.setStylePrimaryName("footer");
panel.add(footer);
return panel;
}
/**
* ---------------------------------------------------------------------------------
* Error Handling
* ---------------------------------------------------------------------------------
*/
/**
* Catches any uncaught exception and pops up a {@link ErrorDialog error dialog}.
*/
public void onUncaughtException(Throwable t) {
handleError(t);
}
/**
* Pops up a modal {@link ErrorDialog error dialog} with the given error.
*/
void handleError(Throwable t) {
t.printStackTrace();
ErrorDialog.showError(t);
}
/**
* ---------------------------------------------------------------------------------
* Service Event Handling
* ---------------------------------------------------------------------------------
*/
/**
* Registers a event with its handler. This mediator pattern facilitates communication
* between the component widgets without them being aware of each other.
* <br>
* GWT framework supports this patter out-of-the-box. This application reuses the
* framework for a set of {@link ServiceEvent service events}.
*/
public <H extends EventHandler> void registerHandler(Type<H> eventType, H handler) {
eventBus.addHandler(eventType, handler);
}
/**
* Fires a event to the registered handlers.
*
* @param event can be any GwtEvent but used here for specialized {@link ServiceEvent service events}.
*/
public void fireEvent(GwtEvent<? extends EventHandler> event) {
eventBus.fireEvent(event);
}
}