/*
* Copyright (c) 2008-2017 the original author or authors.
*
* Licensed 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.webtide.demo.auction;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletContext;
import org.cometd.bayeux.Message;
import org.cometd.bayeux.client.ClientSessionChannel;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ConfigurableServerChannel;
import org.cometd.bayeux.server.ServerChannel;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.oort.Oort;
import org.cometd.oort.Seti;
import org.cometd.oort.SetiServlet;
import org.cometd.server.AbstractService;
import org.cometd.server.authorizer.GrantAuthorizer;
import org.webtide.demo.auction.dao.AuctionDao;
import org.webtide.demo.auction.dao.BidderDao;
import org.webtide.demo.auction.dao.CategoryDao;
public class AuctionService extends AbstractService implements ClientSessionChannel.MessageListener, BayeuxServer.ChannelListener, BayeuxServer.SubscriptionListener {
public static final String AUCTION_ROOT = "/auction/";
private final AuctionDao _auctionDao = new AuctionDao();
private final BidderDao _bidderDao = new BidderDao();
private final CategoryDao _categoryDao = new CategoryDao();
private final AtomicInteger _bidders = new AtomicInteger(0);
private Oort _oort;
private Seti _seti;
public AuctionService(ServletContext context) {
super((BayeuxServer)context.getAttribute(BayeuxServer.ATTRIBUTE), "oortion");
_oort = (Oort)context.getAttribute(Oort.OORT_ATTRIBUTE);
if (_oort == null) {
throw new RuntimeException("Missing " + Oort.OORT_ATTRIBUTE + " from " + ServletContext.class.getSimpleName() + "; " +
"is an Oort servlet declared in web.xml ?");
}
_seti = (Seti)context.getAttribute(Seti.SETI_ATTRIBUTE);
if (_seti == null) {
throw new RuntimeException("Missing " + Seti.SETI_ATTRIBUTE + " from " + ServletContext.class.getSimpleName() + "; " +
"is " + SetiServlet.class.getSimpleName() + " declared in web.xml ?");
}
_oort.observeChannel(AUCTION_ROOT + "**");
getBayeux().addListener(this);
setSeeOwnPublishes(false);
getBayeux().createChannelIfAbsent("/service" + AUCTION_ROOT + "*", new ConfigurableServerChannel.Initializer() {
@Override
public void configureChannel(ConfigurableServerChannel channel) {
channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
}
});
getBayeux().createChannelIfAbsent(AUCTION_ROOT + "*", new ConfigurableServerChannel.Initializer() {
@Override
public void configureChannel(ConfigurableServerChannel channel) {
channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
}
});
addService(AUCTION_ROOT + "*", "bids");
addService("/service" + AUCTION_ROOT + "bid", "bid");
addService("/service" + AUCTION_ROOT + "bidder", "bidder");
addService("/service" + AUCTION_ROOT + "search", "search");
addService("/service" + AUCTION_ROOT + "category", "category");
addService("/service" + AUCTION_ROOT + "categories", "categories");
}
public synchronized void bids(ServerSession source, ServerMessage message) {
// TODO Other half of the non atomic bid hack when used in Oort
Map<String, Object> bidMap = message.getDataAsMap();
Integer itemId = ((Number)bidMap.get("itemId")).intValue();
Double amount = Double.parseDouble(bidMap.get("amount").toString());
Map<String, Object> bidderMap = (Map<String, Object>)bidMap.get("bidder");
String username = (String)bidderMap.get("username");
Bidder bidder = _bidderDao.getBidder(username);
if (bidder == null) {
bidder = new Bidder();
bidder.setUsername(username);
bidder.setName((String)bidderMap.get("name"));
_bidderDao.addBidder(bidder);
bidder = _bidderDao.getBidder(username);
}
Bid bid = new Bid();
bid.setItemId(itemId);
bid.setAmount(amount);
bid.setBidder(bidder);
Bid highest = _auctionDao.getHighestBid(itemId);
if (highest == null || amount > highest.getAmount()) {
_auctionDao.saveAuctionBid(bid);
}
}
public synchronized void bid(ServerSession source, ServerMessage message) {
try {
Map<String, Object> bidMap = message.getDataAsMap();
Integer itemId = ((Number)bidMap.get("itemId")).intValue();
Double amount = Double.parseDouble(bidMap.get("amount").toString());
String username = (String)bidMap.get("username");
Bidder bidder = _bidderDao.getBidder(username);
if (bidder != null) {
// TODO This is a horrible race because there is no clusterwide DB for
// atomic determination of the highest bid.
// live with it! it's a demo!!!!
Bid highest = _auctionDao.getHighestBid(itemId);
if (highest == null || amount > highest.getAmount()) {
Bid bid = new Bid();
bid.setItemId(itemId);
bid.setAmount(amount);
bid.setBidder(bidder);
_auctionDao.saveAuctionBid(bid);
getBayeux().getChannel(AUCTION_ROOT + "item" + itemId).publish(getServerSession(), bid);
}
}
} catch (NumberFormatException e) {
}
}
public Bidder bidder(ServerSession source, ServerMessage message) {
String bidder = (String)message.getData();
Integer id = _bidders.incrementAndGet();
// TODO this is not atomic, but will do for the demo
String username = bidder.toLowerCase().replace(" ", "");
while (_bidderDao.getBidder(username) != null) {
username = bidder.toLowerCase().replace(" ", "") + "-" + _bidders.incrementAndGet();
}
Bidder b = new Bidder();
b.setName(bidder);
b.setUsername(username);
_bidderDao.addBidder(b);
_seti.associate(b.getUsername(), source);
return b;
}
public List<Item> search(ServerSession source, ServerMessage message) {
return _categoryDao.findItems((String)message.getData());
}
public List<Item> category(ServerSession source, ServerMessage message) {
Number categoryId = (Number)message.getData();
return _categoryDao.getItemsInCategory(categoryId.intValue());
}
public List<Category> categories(ServerSession source, ServerMessage message) {
return _categoryDao.getAllCategories();
}
@Override
public void subscribed(ServerSession session, ServerChannel channel, ServerMessage message) {
if (!session.isLocalSession() && channel.getId().startsWith(AUCTION_ROOT + "item")) {
String itemIdS = channel.getId().substring((AUCTION_ROOT + "item").length());
if (itemIdS.indexOf('/') < 0) {
Integer itemId = Integer.decode(itemIdS);
Bid highest = _auctionDao.getHighestBid(itemId);
if (highest != null) {
session.deliver(getServerSession(), channel.getId(), highest);
}
}
}
}
@Override
public void unsubscribed(ServerSession session, ServerChannel channel, ServerMessage message) {
}
@Override
public void channelAdded(ServerChannel channel) {
if (channel.getId().startsWith(AUCTION_ROOT + "item")) {
getLocalSession().getChannel(channel.getId()).subscribe(this);
}
}
@Override
public void channelRemoved(String channelId) {
}
@Override
public void onMessage(ClientSessionChannel channel, Message message) {
}
@Override
public void configureChannel(ConfigurableServerChannel channel) {
}
}