Feb 13

PrimeFaces Article on InfoQ

InfoQ has posted an article about PrimeFaces, featuring an interview with us. We’ve really liked the following analysis in the article;

Among the components one can also find implementations for image comparison, tag cloud generation and even an OS X styled stack. One reason for such diversity, according to Prime Teknoloji (the company behind PrimeFaces), is that the firm is not a software vendor. It is a consulting company actually using PrimeFaces for its clients.

This is a major characteristic of PrimeFaces that sets it apart from the competition, the company argues, since Prime Teknoloji acts both as the producer of the library and as the consumer when it acts on behalf of its clients. This has a heavy impact on core development since important bugs are fixed as soon as possible. Keeping the library lightweight and usable is also a major goal for PrimeFaces as any deviations will be instantly visible to Prime Teknoloji clients.

This is totally correct, we are a consulting firm that make use of JSF and Java EE for consulting, trainings and software development. We had some serious troubles with other JSF libraries in the past and decided to create lightweight and easy to use library in 2009, now known as PrimeFaces. So the main difference is; PrimeFaces is created by application developers not software vendor developers. We enjoy using PrimeFaces in production and glad that PrimeFaces Community is also so enjoying our work as well!

Visit InfoQ for the full article!

Posted in PrimeFaces | 1 Comment
Feb 10

IceFaces Copies PrimeFaces Line by Line

After release of IceFaces 3, we have been getting reports from PrimeFaces Community that IceFaces Ace Components look similar to PrimeFaces. After looking at Ice Ace Components, frankly, we are shocked and disappointed! IceSoft copied PrimeFaces code LINE BY LINE to come up with it’s new set of components named Ace. We are aware that we can’t take any action legally as code is open however we just want to let you know about this. Here is an example of PrimeFaces Panel and Ice Panel as a case study;

PrimeFaces PanelRenderer

package org.primefaces.component.panel;

import java.io.IOException;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.primefaces.component.menu.Menu;
import org.primefaces.renderkit.CoreRenderer;

public class PanelRenderer extends CoreRenderer {

    @Override
    public void decode(FacesContext context, UIComponent component) {
        Panel panel = (Panel) component;
        String clientId = panel.getClientId(context);
        Map params = context.getExternalContext().getRequestParameterMap();

        //Restore toggle state
        String collapsedParam = params.get(clientId + "_collapsed");
        if(collapsedParam != null) {
            panel.setCollapsed(Boolean.valueOf(collapsedParam));
        }

        //Restore visibility state
        String visibleParam = params.get(clientId + "_visible");
        if(visibleParam != null) {
            panel.setVisible(Boolean.valueOf(visibleParam));
        }

        decodeBehaviors(context, component);
    }

    @Override
    public void encodeEnd(FacesContext facesContext, UIComponent component) throws IOException {
        Panel panel = (Panel) component;

        encodeMarkup(facesContext, panel);
        encodeScript(facesContext, panel);
    }

    protected void encodeScript(FacesContext context, Panel panel) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String clientId = panel.getClientId(context);

        startScript(writer, clientId);

        writer.write("PrimeFaces.cw('Panel','" + panel.resolveWidgetVar() + "',{");
        writer.write("id:'" + clientId + "'");

        //Toggle configuration
        if(panel.isToggleable()) {
            writer.write(",toggleable:true");
            writer.write(",toggleSpeed:" + panel.getToggleSpeed());
            writer.write(",collapsed:" + panel.isCollapsed());
        }

        //Toggle configuration
        if(panel.isClosable()) {
            writer.write(",closable:true");
            writer.write(",closeSpeed:" + panel.getCloseSpeed());
        }

        //Options menu configuration
        if(panel.getOptionsMenu() != null) {
            writer.write(",hasMenu:true");
        }

        encodeClientBehaviors(context, panel);

        writer.write("});");

        endScript(writer);
    }

    protected void encodeMarkup(FacesContext context, Panel panel) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String clientId = panel.getClientId(context);
        Menu optionsMenu = panel.getOptionsMenu();

        writer.startElement("div", null);
        writer.writeAttribute("id", clientId, null);
        String styleClass = panel.getStyleClass() != null ? Panel.PANEL_CLASS + " " + panel.getStyleClass() : Panel.PANEL_CLASS;
        styleClass = panel.isVisible() ? styleClass : styleClass + " ui-helper-hidden";
        writer.writeAttribute("class", styleClass, "styleClass");
        if(panel.getStyle() != null) {
            writer.writeAttribute("style", panel.getStyle(), "style");
        }

        encodeHeader(context, panel);
        encodeContent(context, panel);
        encodeFooter(context, panel);

        if(panel.isToggleable()) {
            encodeStateHolder(context, panel, clientId + "_collapsed", String.valueOf(panel.isCollapsed()));
        }

        if(panel.isClosable()) {
            encodeStateHolder(context, panel, clientId + "_visible", String.valueOf(panel.isVisible()));
        }

        if (optionsMenu != null) {
            optionsMenu.setPosition("dynamic");
            optionsMenu.setTrigger(clientId + "_menu");
            optionsMenu.setMy("left top");
            optionsMenu.setAt("left bottom");

            optionsMenu.encodeAll(context);
        }

        writer.endElement("div");
    }

    protected void encodeHeader(FacesContext context, Panel panel) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String widgetVar = panel.resolveWidgetVar();
        UIComponent header = panel.getFacet("header");
        String headerText = panel.getHeader();
        String clientId = panel.getClientId(context);

        if(headerText == null && header == null) {
            return;
        }

        writer.startElement("div", null);
        writer.writeAttribute("id", panel.getClientId(context) + "_header", null);
        writer.writeAttribute("class", Panel.PANEL_TITLEBAR_CLASS, null);

        //Title
        writer.startElement("span", null);
        writer.writeAttribute("class", Panel.PANEL_TITLE_CLASS, null);

        if(header != null) {
            renderChild(context, header);
        } else if(headerText != null) {
            writer.write(headerText);
        }

        writer.endElement("span");

        //Options
        if(panel.isClosable()) {
            encodeIcon(context, panel, "ui-icon-closethick", clientId + "_closer", panel.getCloseTitle());
        }

        if(panel.isToggleable()) {
            String icon = panel.isCollapsed() ? "ui-icon-plusthick" : "ui-icon-minusthick";
            encodeIcon(context, panel, icon, clientId + "_toggler", panel.getToggleTitle());
        }

        if(panel.getOptionsMenu() != null) {
            encodeIcon(context, panel, "ui-icon-gear", clientId + "_menu", panel.getMenuTitle());
        }

        writer.endElement("div");
    }

    protected void encodeContent(FacesContext facesContext, Panel panel) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();

        writer.startElement("div", null);
        writer.writeAttribute("id", panel.getClientId() + "_content", null);
        writer.writeAttribute("class", Panel.PANEL_CONTENT_CLASS, null);
        if (panel.isCollapsed()) {
            writer.writeAttribute("style", "display:none", null);
        }

        renderChildren(facesContext, panel);

        writer.endElement("div");
    }

    protected void encodeFooter(FacesContext facesContext, Panel panel) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();
        UIComponent footer = panel.getFacet("footer");
        String footerText = panel.getFooter();

        if (footer != null || footerText != null) {
            writer.startElement("div", null);
            writer.writeAttribute("id", panel.getClientId(facesContext) + "_footer", null);
            writer.writeAttribute("class", Panel.PANEL_FOOTER_CLASS, null);

            if (footer != null) {
                renderChild(facesContext, footer);
            } else if (footerText != null) {
                writer.write(footerText);
            }

            writer.endElement("div");
        }
    }

    protected void encodeIcon(FacesContext context, Panel panel, String iconClass, String id, String title) throws IOException {
        ResponseWriter writer = context.getResponseWriter();

        writer.startElement("a", null);
        writer.writeAttribute("href", "javascript:void(0)", null);
        writer.writeAttribute("class", Panel.PANEL_TITLE_ICON_CLASS, null);
        if(title != null) {
            writer.writeAttribute("title", title, null);
        }

        writer.startElement("span", null);

        if(id != null) {
            writer.writeAttribute("id", id, null);
        }

        writer.writeAttribute("class", "ui-icon " + iconClass, null);

        writer.endElement("span");

        writer.endElement("a");
    }

    protected void encodeStateHolder(FacesContext context, Panel panel, String name, String value) throws IOException {
        ResponseWriter writer = context.getResponseWriter();

        writer.startElement("input", null);
        writer.writeAttribute("type", "hidden", null);
        writer.writeAttribute("id", name, null);
        writer.writeAttribute("name", name, null);
        writer.writeAttribute("value", value, null);
        writer.endElement("input");
    }

    @Override
    public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
        //Do nothing
    }

    @Override
    public boolean getRendersChildren() {
        return true;
    }
}

IceFaces PanelRenderer

package org.icefaces.ace.component.panel;

import java.io.IOException;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.icefaces.ace.component.menu.Menu;
import org.icefaces.ace.renderkit.CoreRenderer;

import org.icefaces.ace.util.Utils;
import org.icefaces.ace.util.JSONBuilder;
import org.icefaces.render.MandatoryResourceComponent;

@MandatoryResourceComponent(tagName="panel", value="org.icefaces.ace.component.panel.Panel")
public class PanelRenderer extends CoreRenderer {

    @Override
    public void decode(FacesContext context, UIComponent component) {
        Panel panel = (Panel) component;
        String clientId = panel.getClientId(context);
        Map params = context.getExternalContext().getRequestParameterMap();

        //Restore toggle state
        String collapsedParam = params.get(clientId + "_collapsed");
        if(collapsedParam != null) {
            panel.setCollapsed(Boolean.valueOf(collapsedParam));
        }

        //Restore visibility state
        String visibleParam = params.get(clientId + "_visible");
        if(visibleParam != null) {
            panel.setVisible(Boolean.valueOf(visibleParam));
        }

        decodeBehaviors(context, component);
    }

    @Override
    public void encodeEnd(FacesContext facesContext, UIComponent component) throws IOException {
        Panel panel = (Panel) component;

        encodeMarkup(facesContext, panel);
        encodeScript(facesContext, panel);
    }

    protected void encodeScript(FacesContext context, Panel panel) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String clientId = panel.getClientId(context);

        writer.startElement("script", null);
        writer.writeAttribute("type", "text/javascript", null);

        writer.write(this.resolveWidgetVar(panel) + " = new ");
		JSONBuilder jb = JSONBuilder.create();
		jb.beginFunction("ice.ace.Panel")
			.item(clientId)
			.beginMap()
			.entry("visible", panel.isVisible());

        //Toggle configuration
        if(panel.isToggleable()) {
            jb.entry("toggleable", true);
            jb.entry("toggleSpeed", panel.getToggleSpeed());
            jb.entry("collapsed", panel.isCollapsed());
        }

        //Toggle configuration
        if(panel.isClosable()) {
            jb.entry("closable", true);
            jb.entry("closeSpeed", panel.getCloseSpeed());
        }

        //Options menu configuration
        if(panel.getOptionsMenu() != null) {
            jb.entry("hasMenu", true);
        }

        encodeClientBehaviors(context, panel, jb);

        jb.endMap().endFunction();
		writer.write(jb.toString());

        writer.endElement("script");
    }

    protected void encodeMarkup(FacesContext context, Panel panel) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String clientId = panel.getClientId(context);
        Menu optionsMenu = panel.getOptionsMenu();

        writer.startElement("div", null);
        writer.writeAttribute("id", clientId, null);

        String styleClass = panel.getStyleClass() ;
		Utils.writeConcatenatedStyleClasses(writer, Panel.PANEL_CLASS, styleClass);
		String style = panel.getStyle();
        if(style != null) {
            writer.writeAttribute("style", style, "style");
        }

        encodeHeader(context, panel);
        encodeContent(context, panel);
        encodeFooter(context, panel);

        if(panel.isToggleable()) {
            encodeStateHolder(context, panel, clientId + "_collapsed", String.valueOf(panel.isCollapsed()));
        }

        if(panel.isClosable()) {
            encodeStateHolder(context, panel, clientId + "_visible", String.valueOf(panel.isVisible()));
        }

        if (optionsMenu != null) {
            optionsMenu.setPosition("dynamic");
            optionsMenu.setTrigger(clientId + "_menu");
            optionsMenu.setMy("left top");
            optionsMenu.setAt("left bottom");

            optionsMenu.encodeAll(context);
        }

        writer.endElement("div");
    }

    protected void encodeHeader(FacesContext context, Panel panel) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String widgetVar = this.resolveWidgetVar(panel);
        UIComponent header = panel.getFacet("header");
        String headerText = panel.getHeader();
        String clientId = panel.getClientId(context);

        if(headerText == null && header == null) {
            return;
        }

        writer.startElement("div", null);
        writer.writeAttribute("id", clientId + "_header", null);
        writer.writeAttribute("class", Panel.PANEL_TITLEBAR_CLASS, null);

        //Title
        writer.startElement("span", null);
        writer.writeAttribute("class", Panel.PANEL_TITLE_CLASS, null);

        if(header != null) {
            renderChild(context, header);
        } else if(headerText != null) {
            writer.write(headerText);
        }

        writer.endElement("span");

        //Options
        if(panel.isClosable()) {
            encodeIcon(context, panel, "ui-icon-closethick", clientId + "_closer");
        }

        if(panel.isToggleable()) {
            String icon = panel.isCollapsed() ? "ui-icon-plusthick" : "ui-icon-minusthick";
            encodeIcon(context, panel, icon, clientId + "_toggler");
        }

        if(panel.getOptionsMenu() != null) {
            encodeIcon(context, panel, "ui-icon-gear", clientId + "_menu");
        }

        writer.endElement("div");
    }

    protected void encodeContent(FacesContext facesContext, Panel panel) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();

        writer.startElement("div", null);
        writer.writeAttribute("id", panel.getClientId() + "_content", null);
        writer.writeAttribute("class", Panel.PANEL_CONTENT_CLASS, null);
        if (panel.isCollapsed()) {
            writer.writeAttribute("style", "display:none", null);
        }

        renderChildren(facesContext, panel);

        writer.endElement("div");
    }

    protected void encodeFooter(FacesContext facesContext, Panel panel) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();
        UIComponent footer = panel.getFacet("footer");
        String footerText = panel.getFooter();

        if (footer != null || footerText != null) {
            writer.startElement("div", null);
            writer.writeAttribute("id", panel.getClientId(facesContext) + "_footer", null);
            writer.writeAttribute("class", Panel.PANEL_FOOTER_CLASS, null);

            if (footer != null) {
                renderChild(facesContext, footer);
            } else if (footerText != null) {
                writer.write(footerText);
            }

            writer.endElement("div");
        }
    }

    protected void encodeIcon(FacesContext context, Panel panel, String iconClass, String id) throws IOException {
        ResponseWriter writer = context.getResponseWriter();

        writer.startElement("a", null);
        writer.writeAttribute("class", Panel.PANEL_TITLE_ICON_CLASS, null);

        writer.startElement("span", null);

        if(id != null) {
            writer.writeAttribute("id", id, null);
        }

        writer.writeAttribute("class", "ui-icon " + iconClass, null);

        writer.endElement("span");

        writer.endElement("a");
    }

    protected void encodeStateHolder(FacesContext context, Panel panel, String name, String value) throws IOException {
        ResponseWriter writer = context.getResponseWriter();

        writer.startElement("input", null);
        writer.writeAttribute("type", "hidden", null);
        writer.writeAttribute("id", name, null);
        writer.writeAttribute("name", name, null);
        writer.writeAttribute("value", value, null);
        writer.endElement("input");
    }

    @Override
    public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
        //Do nothing
    }

    @Override
    public boolean getRendersChildren() {
        return true;
    }
}

PrimeFaces Panel.js

/**
 * PrimeFaces Panel Widget
 */
PrimeFaces.widget.Panel = function(cfg) {
    this.cfg = cfg;
    this.id = this.cfg.id;
    this.jqId = PrimeFaces.escapeClientId(this.id);

    if(this.cfg.toggleable) {
        this.toggler = $(this.jqId + '_toggler');
        this.toggleStateHolder = $(this.jqId + '_collapsed');
        this.content = $(this.jqId + '_content');

        this.setupToggleTrigger();
    }

    if(this.cfg.closable) {
        this.visibleStateHolder = $(this.jqId + "_visible");

        this.setupCloseTrigger();
    }

    if(this.cfg.hasMenu) {
        this.visibleStateHolder = $(this.jqId + "_visible");

        this.setupMenuTrigger();
    }

    this.postConstruct();
}

PrimeFaces.extend(PrimeFaces.widget.Panel, PrimeFaces.widget.BaseWidget);

PrimeFaces.widget.Panel.prototype.toggle = function() {
    if(this.cfg.collapsed) {
        this.toggler.removeClass('ui-icon-plusthick').addClass('ui-icon-minusthick');
        this.cfg.collapsed = false;
        this.toggleStateHolder.val(false);
    }
    else {
        this.toggler.removeClass('ui-icon-minusthick').addClass('ui-icon-plusthick');
        this.cfg.collapsed = true;
        this.toggleStateHolder.val(true);
    }

    var _self = this;

    this.content.slideToggle(this.cfg.toggleSpeed,
        function(e) {
            if(_self.cfg.behaviors) {
                var toggleBehavior = _self.cfg.behaviors['toggle'];
                if(toggleBehavior) {
                    toggleBehavior.call(_self, e);
                }
            }
        });
}

PrimeFaces.widget.Panel.prototype.close = function() {
    this.visibleStateHolder.val(false);

    var _self = this;

    $(this.jqId).fadeOut(this.cfg.closeSpeed,
        function(e) {
            if(_self.cfg.behaviors) {
                var closeBehavior = _self.cfg.behaviors['close'];
                if(closeBehavior) {
                    closeBehavior.call(_self, e);
                }
            }
        }
    );
}

PrimeFaces.widget.Panel.prototype.show = function() {
    $(this.jqId).fadeIn(this.cfg.closeSpeed);

    this.visibleStateHolder.val(true);
}

PrimeFaces.widget.Panel.prototype.setupToggleTrigger = function() {
    var _self = this,
    trigger = this.toggler.parent();

    this.setupTriggerVisuals(trigger);

    trigger.click(function() {_self.toggle();});
}

PrimeFaces.widget.Panel.prototype.setupCloseTrigger = function() {
    var _self = this,
    trigger = $(this.jqId + '_closer').parent();

    this.setupTriggerVisuals(trigger);

    trigger.click(function() {_self.close();});
}

PrimeFaces.widget.Panel.prototype.setupMenuTrigger = function() {
    var trigger = $(this.jqId + '_menu').parent();

    this.setupTriggerVisuals(trigger);
}

PrimeFaces.widget.Panel.prototype.setupTriggerVisuals = function(trigger) {
    trigger.mouseover(function() {$(this).addClass('ui-state-hover');})
            .mouseout(function() {$(this).removeClass('ui-state-hover');});
}

IceFaces Panel.js

/**
 *  Panel Widget
 */
ice.ace.Panel = function(id, cfg) {
    this.id = id;
    this.cfg = cfg;
    this.jqId = ice.ace.escapeClientId(id);

    if(this.cfg.toggleable) {
        this.toggler = ice.ace.jq(this.jqId + '_toggler');
        this.toggleStateHolder = ice.ace.jq(this.jqId + '_collapsed');
        this.content = ice.ace.jq(this.jqId + '_content');

        this.setupToggleTrigger();
    }

    if(this.cfg.closable) {
        this.visibleStateHolder = ice.ace.jq(this.jqId + "_visible");

        this.setupCloseTrigger();
    }

    if(this.cfg.hasMenu) {
        this.visibleStateHolder = ice.ace.jq(this.jqId + "_visible");

        this.setupMenuTrigger();
    }

    if(!this.cfg.visible) {
        ice.ace.jq(this.jqId).css('display','none');
    }
}

ice.ace.Panel.prototype.toggle = function() {
    if(this.cfg.collapsed) {
        this.toggler.removeClass('ui-icon-plusthick').addClass('ui-icon-minusthick');
        this.cfg.collapsed = false;
        this.toggleStateHolder.val(false);
    }
    else {
        this.toggler.removeClass('ui-icon-minusthick').addClass('ui-icon-plusthick');
        this.cfg.collapsed = true;
        this.toggleStateHolder.val(true);
    }

    var _self = this;

    this.content.slideToggle(this.cfg.toggleSpeed,
        function(e) {
            if(_self.cfg.behaviors) {
                var toggleBehavior = _self.cfg.behaviors['toggle'];
                if(toggleBehavior) {
                    toggleBehavior.call(this, e);
                }
            }
        });
}

ice.ace.Panel.prototype.close = function() {
    this.visibleStateHolder.val(false);

    var _self = this;

    ice.ace.jq(this.jqId).fadeOut(this.cfg.closeSpeed,
        function(e) {
            if(_self.cfg.behaviors) {
                var closeBehavior = _self.cfg.behaviors['close'];
                if(closeBehavior) {
                    closeBehavior.call(this, e);
                }
            }
        }
    );
}

ice.ace.Panel.prototype.show = function() {
    ice.ace.jq(this.jqId).fadeIn(this.cfg.closeSpeed);

    this.visibleStateHolder.val(true);
}

ice.ace.Panel.prototype.setupToggleTrigger = function() {
    var _self = this,
    trigger = this.toggler.parent();

    this.setupTriggerVisuals(trigger);

    trigger.click(function() {_self.toggle();});
}

ice.ace.Panel.prototype.setupCloseTrigger = function() {
    var _self = this,
    trigger = ice.ace.jq(this.jqId + '_closer').parent();

    this.setupTriggerVisuals(trigger);

    trigger.click(function() {_self.close();});
}

ice.ace.Panel.prototype.setupMenuTrigger = function() {
    var trigger = ice.ace.jq(this.jqId + '_menu').parent();

    this.setupTriggerVisuals(trigger);
}

ice.ace.Panel.prototype.setupTriggerVisuals = function(trigger) {
    trigger.mouseover(function() {ice.ace.jq(this).addClass('ui-state-hover');})
            .mouseout(function() {ice.ace.jq(this).removeClass('ui-state-hover');});
}

We have looked in detail of all IceFaces Ace Components and more than 90% of IceFaces components are like Panel. They are copied line by line, only difference is the change of package names from prime to ice. This duplication applies to java renderers, components codes, javascripts and css files as well.

License of IceFaces components start with this line;

“Original Code developed and contributed by Prime Technology. Subsequent Code Modifications Copyright 2011-2012 ICEsoft Technologies Canada Corp. (c)”

First of all we are not aware of any “contribution”, we haven’t done any contribution to IceFaces or signed any contribution agreement just like a CLA (Contributor License Agreement). Their code includes reference to our work with the line above, however nowhere on their release announcements here or here this was mentioned at all, it’s like they have written the code themselves. This “tiny” detail is hidden from public and marketing point of view. This also explains why IceFaces license is now Apache, because PrimeFaces is Apache.

IceFaces has lost significant of it’s user base to PrimeFaces since 2009 and they are trying to get their popularity back. Last year, IceSoft has approached us with the idea of sponsoring PrimeFaces by paying 60K $ per year. Our clear answer was a big NO, it seems after getting rejected, they have chosen a very very wrong way when trying to build their new ACE Components that is aimed to replace their legacy components, which is copying our code line by line and offer commercial services like support, consulting and training over our copy-paste code. Competition should be about providing alternative solutions not copy-paste your competitors just because the license permits and it is legal.

We’ve also realized that IceSoft has copied the code from PrimeFaces 2 which was released a year ago, PrimeFaces 3 is much better than PrimeFaces 2, this seems to be second mistake of IceFaces. They have copied the wrong branch!.

We don’t think there is a stealing here as the code is open and free to anyone. Incorporating OSS code in another OSS code is totally fine and result of OSS nature. However we think it is wrong and not cool when the two OSS products are competing and on the same line. There are various OSS projects that use PrimeFaces out there, all of them link to PrimeFaces and use the library, none of them actually copies the code and renames the packages like Ice does. We’ll be happy if IceSoft updates their Ace component pages or release announcements to add the fact that, IceFaces Ace components are forked from PrimeFaces Codebase instead of advertising it as a home grown product. We don’t see a public reference or a credits in NOTICE file in icefaces jar at all.

As of PrimeFaces 3, we use 3rd party libraries as minimal as possible, for example PrimeFaces 2 which IceFaces copies, use jQuery UI and YUI a lot. Components like accordion, tabview, menu, menubar, autocomplete, paginator, buttons and many more were based on 3rd party widgets. With PrimeFaces 3 we have written the client side widgets of these and more by ourselves with JSF in mind which gave us the power to add advanced new features that you can’t find in these widgets. PrimeFaces 1 or 2 can be seen as a wrapper of 3rd party libs but PrimeFaces 3 is not, we are even considering to externalize our own javascript library for non-jsf use. We spent our days, nights and weekends to come up with our own ideas and the implementation of our own ideas. This is the reason of our success, the passion! PrimeFaces has became the leading component suite as can be seen from Google Trends as well. In addition PrimeFaces is the RIA library with the most followers on twitter. At time of writing stats are PrimeFaces 2067, RichFaces 1687, IceFaces 951, Vaadin 1554, ZK 706.

In the end, we are not annoyed at all, just disappointed to see a competitor of PrimeFaces copying our code. It is really not that hard to create rich JSF components, it would be better if IceFaces implemented their own solutions and provided a new alternative to the JSF ecosystem. We don’t think anyone needs IceSoft to enhance PrimeFaces, we as PrimeFaces team have already done it and it is called PrimeFaces 3.

Please share this article on social media so we can let more people know about this!

!!!You may copy our code, but you’ll never take our PASSION!!!

Optimus Prime
On Behalf of All AutoBots in PrimeFaces Community

Updates

  • They have added this page after getting so much criticism from the community.om the community. This reveals PrimeFaces is a core technology of IceFaces. IceFaces is powered by PrimeFaces. It is great to hear that we are now powering our competitors. It is not stated but Slider, DateTimeEntry and Core Engine (core.js) is also based on jQuery/PrimeFaces.
  • Our posts and posts of many PrimeFaces users in IceFaces forums has been deleted by IceSoft. Free Speech!
  • We have done a new and final announcement regarding this to state our final words and leave this behind. Thanks everyone for their feedback!

 

Posted in Uncategorized | 117 Comments
Feb 07

BlockUI

PrimeFaces 3.2 development has started along with 3.1.1 maintenance in parallel. The first feature of 3.2 we have added is BlockUI component that can block a particular content. Usage is quite simple thanks to the special ajax integration, also client side api is provided if you need to write extensions or customize it.

The component is inspired by the blockui jquery plugin, we have our own implementation instead of using that plugin but the idea is pretty much the same.

Checkout PrimeFaces Labs to see BlockUI in action! It is theme integrated as usual so don’t forget to try with different themes :)

Posted in PrimeFaces | 7 Comments
Feb 06

PrimeFaces 3.1 Released

Prime Teknoloji, company behind PrimeFaces is glad to announce the 3.1 final release. In total there are 12 filed changes over the release candidate. See the full changelog for more information.

Detailed information about what has changed, the new features and more can be found at the 3.1.RC1 Release Announcement.

Downloads

Check out PrimeFaces downloads page for manual download or if you are maven user update your dependency to 3.1 to migrate to this release.

Documentation

PrimeFaces User’s Guide has been updated to cover 3.1 and it is available at documentation page.

Roadmap

Next planned release is 3.2 which will introduce some new components, new DataTable features, implementation of most voted feature requests and of course maintenance on current codebase. In summary, we’ll keep rocking!

Always bet on PrimeFaces!

Posted in PrimeFaces | 13 Comments
Jan 30

PrimeFaces 3.1.RC1 Released

Prime Teknoloji is proud the announce the first release candidate of PrimeFaces 3.1. This release brings many new features, enhancements and bug fixes. See the all 90+ improvements at full changelog for detailed information. Significant changes can be summarized as;

New Components

Enhancements to Existing Components

Maintenance

Feature List is quite long in this release however, PrimeFaces Team has spent a lot of time on maintenance so there is a balance between new features and bug fixes. Almost half of the changelog is bug fixes, improvements and other half is new features.

Backward Compatibility

There are two points to note regarding backward compatibility with 3.0;

  • Component referencing is now aligned with JSF Spec, if PrimeFaces cannot find a component, it will throw an exception. Since PrimeFaces 2.2 we’ve been logging an info message that component cannot be found and falling back to the client id. If you haven’t ignored these messages and fixed your code since 2.2, there won’t be a problem. If not, you need to update your component referencing with respect to findComponent specification.
  • primefaces.THEME_FORMS setting is removed in favor of plain css, if you need to reset the theme aware styles on input components, add a reset css instead.

Accessibility

Accessibility enhancements are initiated with this release as we have already explained at here. Many commonly of PrimeFaces components like datatable, accordionPanel, tabView, input components, menus and more are equipped with WAI-ARIA roles and state attributes which improves support for Screen Readers. Keyboard support is already available in many PrimeFaces components, feature versions will even see improved accessibility features.

Download

Visit PrimeFaces downloads page to download the release manually or update your dependency version to 3.1.RC1 if you are a maven user. On downloads page, we’ve also included the javadocs inside the bundle that has binary and source already.

Documentation

3.1. Final is due 6th of February and updated User’s Guide will be available along with it next week.

Our Quality

PrimeFaces QA Team has created a Selenium Test Suite for PrimeFaces and added hundreds of tests. Thanks to the new test suite, we’ve nailed down various regressions before they go in 3.1. In future versions, quality of PrimeFaces will increase even more with more testing. I’d like to thank the QA team for their great work done while the Core team was busy with developing the 3.1.

Roadmap

Tentative date for 3.2 is mid march, currently pending datatable enhancements have the highest priority. We also aim to work on most voted features requests along with regular maintenance work.

That’s all for this release announcement, hope you enjoy using PrimeFaces 3.1.

Optimus Prime
PrimeFaces Lead

Posted in PrimeFaces | 9 Comments
Jan 27

Take Photos with PrimeFaces

PhotoCam is a new PrimeFaces component to take photos with a webcam and pass the image to the JSF backing beans. WebCam integration was a popular request with more than 30 votes so we’ve decided to bring it in with flash, canvas and javascript implementation.

Although it is a new component, PrimeFaces Community has already started to use it. Check out the following video we’ve found in youtube that allows users to take a photo and crop it with PrimeFaces Imagecropper. (We are not sure about the background music though, if you follow our PrimeFaces Trailers, you’d understand :)

Demos

There are two demos available one is the actual component demo and other is integration with PrimeFaces Push where you can take a photo and send it to everyone connected. One note is the IE support, photo cam doesn’t support IE right now and we’ll work on it targeting future versions of PrimeFaces.

!!!Always Bet On Prime!!!

Posted in PrimeFaces | 5 Comments
Jan 26

Checkbox Menu

SelectCheckboxMenu is a new component in PrimeFaces to display a group of checkboxes in an overlay to save space in page layout. We’ve seen this UI in a real estate website and though it would be cool to implement something similar in PrimeFaces.

Check out PrimeFaces Labs to see SelectCheckboxMenu live!

Posted in PrimeFaces | 6 Comments
Jan 25

Native Mobile Apps for PrimeFaces

We are pleased to announce the official mobile application of PrimeFaces for iPhone and Android platforms. The application is mainly a news app connected to PrimeFaces Blog so you can monitor PrimeFaces development everywhere with your native app.

Notable features are;

- Native :)
- Read blog entries
- Contact form to send us messages
- Favorites
- Tags
- Facebook and Twitter integration
- Push notifications

Download

The apps are available at Apple AppStore and Android Market.

PleaseMobile.Me

These native applications are actually products of our beta testing for Prime Teknoloji’s new mobile development platform. PleaseMobile.Me (Global) or MobilUygulamam.com (Turkish) is a web platform to create native mobile apps for your restaurant, estate agency, auto dealer, blog etc. for ios, android, blackberry and nokia platforms. The data inside the applications (logo, colors, backgrounds, your menu, your blog or any data you have) can be updated automatically via our web interface developed and of course admin console of the project is powered by PrimeFaces.

Your business can get a native mobile app  with the content you provide and with the look you want in just 5 minutes without requiring any knowledge of programming.

Posted in PrimeFaces | 11 Comments
Jan 24

Themes 1.0.3 Released

Prime Teknoloji is glad to announce the new release of PrimeFaces Themes. This release wagon brings 1.0.3 versions of all 31 PrimeFaces Themes. Note that as Aristo is the default bundled theme, any updates on aristo is bundled with PrimeFaces releases.

Significant changes of this release are the unified shadows on input fields and fixes of font color problems in some themes. See the changelog for more information.

As stated on PrimeFaces Theme Gallery, Themes 1.0.3 is compatible with PrimeFaces 3.0+.

Regarding roadmap of Themes, full-time web designer of Prime Teknoloji will start working on premium themes soon, so stay tuned!!! We’ll take it to the next level!

Posted in PrimeFaces | 7 Comments