/** * Mad-Advertisement * Copyright (C) 2011 Thorsten Marx <thmarx@gmx.net> * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package net.mad.ads.server.utils.selection; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.google.common.collect.Collections2; import net.mad.ads.common.util.Strings; import net.mad.ads.db.db.request.AdRequest; import net.mad.ads.db.definition.BannerDefinition; import net.mad.ads.db.definition.impl.banner.flash.FlashBannerDefinition; import net.mad.ads.db.definition.impl.banner.image.ImageBannerDefinition; import net.mad.ads.db.enums.BannerType; import net.mad.ads.server.utils.RuntimeContext; import net.mad.ads.server.utils.context.AdContext; import net.mad.ads.server.utils.filter.ClickExpirationFilter; import net.mad.ads.server.utils.filter.DuplicatBannerFilter; import net.mad.ads.server.utils.filter.FlashImageFallbackBannerFilter; import net.mad.ads.server.utils.filter.FlashVersionBannerFilter; import net.mad.ads.server.utils.filter.ViewExpirationFilter; import net.mad.ads.server.utils.request.RequestHelper; import net.mad.ads.server.utils.selection.impl.ImpressionPercentageSingleBannerSelector; import net.mad.ads.server.utils.selection.impl.RandomSingleBannerSelector; /** * Der BannerProvider führt die Suche nach Bannern aus und Filtert das Ergebnis nach bestimmten Kriterien * * @author tmarx * */ public final class BannerProvider { private static final BannerProvider INSTANCE = new BannerProvider(); // private static final BannerSelector SELECTOR = new RandomSingleBannerSelector(); private static final BannerSelector SELECTOR = new ImpressionPercentageSingleBannerSelector(); private BannerProvider () { } public static final BannerProvider getInstance () { return INSTANCE; } /** * Liefert ein Banner * * @param request * @return */ public BannerDefinition getBanner (AdContext context, HttpServletRequest request) { try { // Type String type = (String)request.getParameter(RequestHelper.type); if (type == null || type.equals("")) { type = "1"; } BannerType btype = BannerType.forType(Integer.parseInt(type)); AdRequest adr = RequestHelper.getAdRequest(context, request); // Laden der Banner Collection<BannerDefinition> result = handleProducts(context, adr, request); if (result == null) { result = RuntimeContext.getAdDB().search(adr); result = commonFilter(context, result); } if (btype.equals(BannerType.FLASH)) { result = handleFlash(context, result, adr, request); } List<BannerDefinition> processedResult = new ArrayList<BannerDefinition>(); processedResult.addAll(result); /* * Aus den restlichen Bannern eins auswählen * * Aktuell wird dies zufällig gemacht! */ BannerDefinition banner = SELECTOR.selectBanner(processedResult, context); return banner; } catch (Exception e) { e.printStackTrace(); } return null; } /** * Behandlung von Produkte * * 1. Ladend der Produkte * 2. sind Produkte vorhanden, werden diese verwendet * 3. Wurde auf dieser Seite schon ein Produkt eingebunden, werden die passenden Banner für dieses Produkt verwendet * * @param context * @param adr * @param request * @return * @throws IOException */ private Collection<BannerDefinition> handleProducts (AdContext context, AdRequest adr, HttpServletRequest request) throws IOException { try { adr.setProducts(true); Collection<BannerDefinition> result = RuntimeContext.getAdDB().search(adr); if (result == null || result.isEmpty()) { return null; } // wird schon ein Produkt angezeigt, dann verwenden wir genau dieses Banner for (BannerDefinition banner : result) { if (RuntimeContext.getRequestBanners().containsKey("prod" + context.getRequestid() + "_" + banner.getProduct())) { Collection<BannerDefinition> result2 = new ArrayList<BannerDefinition>(); result2.add(banner); return result2; } } result = commonFilter(context, result); // ansonten alle für die weiter Verwendung wählen return result; } finally { adr.setProducts(false); } } private Collection<BannerDefinition> handleFlash (AdContext context, Collection<BannerDefinition> result, AdRequest adr, HttpServletRequest request) throws IOException { /* * Bei Flashbanner gibt es folgende Fallback-Lösung * 1. Kein FLashbanner, dann laden wir ImageBanner * 2. Flashbanner mit falscher Version * 1. Fallback-Image verwenden * 2. kein Fallback-Image vorhanden * -> Imagebanner neuladen * * Diese Implementierung hat den Nachteil, dass Banner mit Fallback-Bild aber einer falschen * Flash-Version seltener angezeigt werden als Banner mit einer passenden Flash-Version. * Grund dafür ist die Bevorzugung des Flashbanners gegeüber der Fallback-Banner. * TODO: andere Lösung erarbeiten */ if (result.isEmpty()) { // Fallback auf ImageBanner adr.getTypes().remove(BannerType.FLASH); adr.getTypes().add(BannerType.IMAGE); result = RuntimeContext.getAdDB().search(adr); result = commonFilter(context, result); } else { String flash = (String)request.getParameter(RequestHelper.flash); if (!Strings.isEmpty(flash)) { // Alle Banner die die Version erfüllen Collection<BannerDefinition> resultVersion = (Collection<BannerDefinition>) Collections2.filter(result, new FlashVersionBannerFilter(Integer.parseInt(flash))); if (!resultVersion.isEmpty()) { // Flashbanner mit passender Version result = resultVersion; } else { Collection<BannerDefinition> resultImageFallback = (Collection<BannerDefinition>) Collections2.filter(result, new FlashImageFallbackBannerFilter()); if (!resultImageFallback.isEmpty()) { /* * Fallback auf Flashbanner mit Bilder * * Um Problem mit dem Rendern zu vermeiden werden hier neue Imagebanner erzeugt */ Collection<BannerDefinition> imageBanners = new ArrayList<BannerDefinition>(); for (BannerDefinition bdf : resultImageFallback) { FlashBannerDefinition fbdf = (FlashBannerDefinition)bdf; ImageBannerDefinition ibdf = new ImageBannerDefinition(); ibdf.setFormat(bdf.getFormat()); ibdf.setId(bdf.getId()); ibdf.setImageUrl(fbdf.getFallbackImageUrl()); ibdf.setLinkTarget(fbdf.getLinkTarget()); ibdf.setTargetUrl(bdf.getTargetUrl()); imageBanners.add(ibdf); } result = imageBanners; } else { // Fallback auf ImageBanner adr.getTypes().remove(BannerType.FLASH); adr.getTypes().add(BannerType.IMAGE); result = RuntimeContext.getAdDB().search(adr); result = commonFilter(context, result); } } } else { // Fallback auf ImageBanner um sicher zu gehen adr.getTypes().remove(BannerType.FLASH); adr.getTypes().add(BannerType.IMAGE); result = RuntimeContext.getAdDB().search(adr); result = commonFilter(context, result); } } return result; } /** * führt die Standard-Filter aus um die Auswahl der Banner nach diesen Kriterien einzuschränken * * @param context * @param result * @return */ private Collection<BannerDefinition> commonFilter (AdContext context, Collection<BannerDefinition> result) { /* * Filtern der Banner deren maximale Anzahl an Clicks schon erreicht wurden */ result = (Collection<BannerDefinition>) Collections2.filter(result, new ClickExpirationFilter()); /* * Filtern der Banner deren maximale Anzahl an Impressions schon erreicht wurden */ result = (Collection<BannerDefinition>) Collections2.filter(result, new ViewExpirationFilter()); /* * Filter für das Filtern doppelter Banner */ result = (Collection<BannerDefinition>) Collections2.filter(result, new DuplicatBannerFilter(context.getRequestid())); return result; } }