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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
/** * 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
/** * 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!