I have problems with the Multi-Window Handling in my appliacation.
I currently using Conversation Scope to enable multi window / tab handling but in case the user opens a link (button) in a new tab the conversation is shared between the old and new tab.
Apache Deltaspike has a solution for that (http://deltaspike.apache.org/documentation/#_module_overview) but I already using Seam 3 (and JSF 2.1) and don't want to migrate to Deltaspike.
So I'm searching for an alternative solution without Deltaspike or is it possible to use Deltaspike AND Seam 3?
I build a solution with p:remoteCommand and this answer: In javascript, how can I uniquely identify one browser window from another which are under the same cookiedbased sessionId
I added this JS to my template which creates a unique id for each browswer tab an stores it in the window.name. Then it calls a p:remoteCommand to check the guid:
$(window).load(function() {
// ----------------------
var GUID = function() {
// ------------------
var S4 = function() {
return (Math.floor(Math.random() * 0x10000 /* 65536 */
).toString(16));
};
return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
};
if (!window.name.match(/^GUID-/)) {
window.name = "GUID-" + GUID();
}
if ($('#guid_form\\:server_guid').text().length == 0 ||
$('#guid_form\\:server_guid').text() != window.name) {
checkGuid([{name:'guid', value:window.name}]);
}
})
Added a Primefaces remoteCommand to my template which is called by the script above.
<h:form id="guid_form">
<h:outputText value="#{checkTabAction.guid}" id="server_guid"/>
<p:remoteCommand name="checkGuid" actionListener="#{checkTabAction.checkGuid}" process="#this" partialSubmit="true" />
</h:form>
And added a check action which validateds the current browser tab / window by comparing the guid's:
#ConversationScoped
#Named(value = "checkTabAction")
public class CheckTabAction implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
private Logger log;
private String guid = null;
public void checkGuid() {
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
String guid = params.get("guid").toString();
if (this.guid == null) {
this.guid = guid;
}
if (!StringUtils.equals(this.guid, guid)) {
log.info("New tab detected!");
throw new NonexistentConversationException("New tab detected!");
}
}
public String getGuid() {
return guid;
}
}
Related
I am creating a custom button component which involves an label and button. I can set the displaytext and other properties using XAML after defining them in the attrs.xml (like
<UButton displayText="Hello" ... />
). but i need to allow the Click event of this control to be handled by the user by defining it in the XAML like
mclick="button_click"
. but i am not able to find a documentation for handling this. Can you please guide me.
Let's look Button's source code.
In this link (Android attrs.xml source code), search onClick, you will find:
<!-- Name of the method in this View's context to invoke when the view is
clicked. This name must correspond to a public method that takes
exactly one parameter of type View. For instance, if you specify
<code>android:onClick="sayHello"</code>, you must declare a
<code>public void sayHello(View v)</code> method of your context
(typically, your Activity). -->
<attr name="onClick" format="string" />
That why when you use :
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="onClick"/>
it will find the onClick method.
Here, you will see how Android handle the android:onClick="onClick", in it's View(Context context, AttributeSet attrs, int defStyleAttr) method, android:onClick="onClick" is correspond to R.styleable.View_onClick:
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot "
+ "be used within a restricted context");
}
final String handlerName = a.getString(attr);
if (handlerName != null) {
setOnClickListener(new OnClickListener() {
private Method mHandler;
public void onClick(View v) {
if (mHandler == null) {
try {
mHandler = getContext().getClass().getMethod(handlerName,
View.class);
} catch (NoSuchMethodException e) {
int id = getId();
String idText = id == NO_ID ? "" : " with id '"
+ getContext().getResources().getResourceEntryName(
id) + "'";
throw new IllegalStateException("Could not find a method " +
handlerName + "(View) in the activity "
+ getContext().getClass() + " for onClick handler"
+ " on view " + View.this.getClass() + idText, e);
}
}
try {
mHandler.invoke(getContext(), View.this);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not execute non "
+ "public method of the activity", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException("Could not execute "
+ "method of the activity", e);
}
}
});
}
break;
You will find, it find the method by reflection.
So, I guess you have forgot to handle the mclick="button_click". As usually, we don't use this to add click listener, it is a little complex. We use View's setOnClickListener.
In my Vaadin 6 application I sometimes get the following error:
SEVERE: Terminal error:
java.lang.RuntimeException: handleURI for http://AAA.BBB.CCC.DDD:8080/myapp/ uri: '' returns ambigious result.
at com.vaadin.ui.Window.handleURI(Window.java:432)
at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleURI(AbstractCommunicationManager.java:2291)
at com.vaadin.terminal.gwt.server.CommunicationManager.handleURI(CommunicationManager.java:370)
at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.handleURI(AbstractApplicationServlet.java:1099)
at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:535)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
Accrording to Vaadin source it occurs in the following method:
public DownloadStream handleURI(URL context, String relativeUri) {
DownloadStream result = null;
if (uriHandlerList != null) {
Object[] handlers;
synchronized (uriHandlerList) {
handlers = uriHandlerList.toArray();
}
for (int i = 0; i < handlers.length; i++) {
final DownloadStream ds = ((URIHandler) handlers[i]).handleURI(
context, relativeUri);
if (ds != null) {
if (result != null) {
throw new RuntimeException("handleURI for " + context
+ " uri: '" + relativeUri
+ "' returns ambigious result.");
}
result = ds;
}
}
}
return result;
}
I actually create a DownloadStream in a column generator (in order to display images in a table):
public class ImageColumnGenerator implements Table.ColumnGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(ImageColumnGenerator.class);
public final static String IMAGE_FIELD = "image";
public Object generateCell(final Table aTable, final Object aItemId, final Object aColumnId) {
if (!IMAGE_FIELD.equals(aColumnId)) {
return null;
}
final BeanItem<UserProductImageBean> beanItem = (BeanItem<UserProductImageBean>)
aTable.getItem(aItemId);
final StreamResource streamResource = new StreamResource(new StreamResource.StreamSource() {
public InputStream getStream() {
return new ByteArrayInputStream(beanItem.getBean().getImageData());
}
},
beanItem.getBean().getFileName(),
MyApplication.getInstance());
LOGGER.debug("imageResource: " + streamResource);
final Embedded embedded = new Embedded("", streamResource);
return embedded;
}
}
beanItem.getBean().getImageData() is a byte array (byte[]) with image data, which I get from a web service.
MyApplication.getInstance() is defined as follows:
public class MyApplication extends Application implements ApplicationContext.TransactionListener
{
private static ThreadLocal<MyApplication> currentApplication =
new ThreadLocal<MyApplication> ();
public static MyApplication getInstance()
{
return currentApplication.get ();
}
}
What can I do in order to fix the aforementioned (severe) error?
As soon as nobody answer. I'm not at all expert in what hell it is above, but - try to find out on what kind of urls this error arise on, and do with them something before feed them to DownloadStream
Ok so I'm trying to move my project from Primefaces 3.5 to 4.0 and I'm struggling with the new MenuModel API. I'm sure it's something stupid, but I can't display a DefaultMenuItem for my Breadcrumb where the value is from a .properties file (defined in faces-config as resource bundle).
What I had with Primefaces 3.5 :
MenuItem menuItem;
// Where "getPath()" returns all the pages needed to get to the current one
for (Page page : currentPage.getPath()) {
menuItem = new MenuItem();
// Where "getTitle()" returns something like "#{message.home}" which will be resolved as "Home"
menuItem.setValueExpression("value", expressionFactory.createValueExpression(elContext, page.getTitle(), String.class));
menuItem.setActionExpression(expressionFactory.createMethodExpression(elContext, page.getAction(), String.class, new Class[0]));
menuItem.setAjax(false);
menuItem.setAsync(false);
menuItem.setImmediate(true);
breadCrumbModel.addMenuItem(menuItem);
}
What I tried with Primefaces 4.0 and the new DefaultMenuItem object :
menuItem.setValue(page.getTitle());
menuItem.setValue(expressionFactory.createValueExpression(elContext, tmp.getTitle(), String.class));
menuItem.setParam("value", expressionFactory.createValueExpression(elContext, tmp.getTitle(), String.class));
menuItem.setTitle(page.getTitle()); <--- I even tried that in despair haha
I looked up the user guide and the api doc (uncommented :( ) but it doesn't talk much about this scenario, most of the time the examples use plain ol' String. What am I doing wrong ?
Thanks.
After having the same issue i found this solution and it works!, i'm using primefaces 5.0:
public void addMiga(String action, String label) throws Exception {
DefaultMenuItem item = new DefaultMenuItem();
item.setFragment(label);
String valueTitulo = "";
HtmlOutputText seccionLabel = new HtmlOutputText();
if (label != null && !"".equals(label)) {
String[] expresiones = label.split(",");
for (String expresion : expresiones) {
if (expresion.contains("label_")
|| expresion.contains("_label")) {
seccionLabel.setValueExpression("value",
getValueExpression(expresion));
} else {
seccionLabel.setValue(expresion);
}
valueTitulo += " " + (String) seccionLabel.getValue();
}
}
item.setValue(valueTitulo);
item.setId(Integer.toString(this.migas.getElements().size()));
item.setOnclick(ConstantesErp.FUNCION_STATUS);
item.setCommand(action);
item.setAjax(false);
this.migas.addElement(item);
}
public static ValueExpression getValueExpression(String nombre) {
FacesContext fc = FacesContext.getCurrentInstance();
Application app = fc.getApplication();
ELContext elContext = fc.getELContext();
ExpressionFactory factory = app.getExpressionFactory();
ValueExpression ve = null;
try {
ve = factory.createValueExpression(elContext, "#" + "{" + nombre
+ "}", String.class);
} catch (Exception e) {
ve = null;
}
return ve;
}
Thanks in advance for your attention.
I've created a JSF 2.0 custom component to render Google GeoCharts. So, the renderer just writes some javascript and a div at the end.
The component works fine doing that. But my requirement is that the GeoChart should change its values using an ajax request. To do that, i'm using a primefaces command button putting in its attribute "update" the id of the geoChart.
The problem is that the GeoChart is not being updated when the comand button is clicked.
I'm using a maven archetype (myfaces-archetype-jsfcomponents20) allowing the automatic generation of some configuration files. So, I just need to write to classes and annotations. Those classes are the UIComponent and the Renderer.
I'm running this code in GlassFish 3.1 and Mojarra 2.1.6
The code of those classes:
UIComponent:
#JSFComponent(
name = "processum:geochart",
clazz = "org.processum.component.gchart.GoogleChart",
tagClass = "org.processum.component.gchart.GoogleChartTag")
abstract class AbstractGoogleChart extends UIComponentBase {
public static final String COMPONENT_TYPE = "org.processum.GoogleChart";
public static final String DEFAULT_RENDERER_TYPE = "org.processum.GoogleChartRenderer";
public static final String COMPONENT_FAMILY = "javax.faces.Output";
/**
*
* GoogleChartModel
*/
#JSFProperty
public abstract GoogleGeoChartModel getModel();
}
Renderer:
#JSFRenderer(
renderKitId = "HTML_BASIC",
family = "javax.faces.Output",
type = "org.processum.GoogleChartRenderer")
public class GoogleChartRenderer extends Renderer {
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
GoogleChart chart = (GoogleChart) component;
String divId = (String) component.getClientId() + "chartDiv";
GoogleGeoChartModel model = chart.getModel();
ResponseWriter writer = context.getResponseWriter();
writer.write("<script type='text/javascript'>"
+ "google.load('visualization', '1', {'packages': ['geochart']});"
+ "google.setOnLoadCallback(initGeoMap);"
+ "function initGeoMap() {"
+ "var data = google.visualization.arrayToDataTable(["
+ "[");
StringBuilder data = new StringBuilder();
for (String header : model.getHeaders()) {
data.append("\'").append(header).append("\'").append(",");
}
data.deleteCharAt(data.length() - 1).append("],");
for (String[] value : model.getValues()) {
data.append("[");
for (int i = 0; i < value.length; i++) {
if (i == 0) {
data.append("\'").append(value[i]).append("\'");
} else {
data.append(value[i]);
}
data.append(",");
}
data.deleteCharAt(data.length() - 1).append("],");
}
data.deleteCharAt(data.length() - 1);
writer.write(data.toString());
writer.write("]);");
writer.write("var options = {"
+ "region: 'CO',"
+ "displayMode: 'markers'"
+ "};"
+ "var chart = new google.visualization.GeoChart(document.getElementById(\'" + divId + "\'));"
+ "chart.draw(data, options);"
+ "};"
+ "</script>"
+ "<div id=\"" + divId + "\"/>");
}
}
XHTML:
<h:form>
<div id="anotherContent" style="width: 900px; height: 500px;">
<h:panelGroup id="chartCont" layout="block">
<processum:geochart id="chart" model="#{chartBackBean.model}"/>
</h:panelGroup>
</div>
<p:commandButton id="change" value="Cambio" actionListener="#{chartBackBean.change}" update="chart"/>
</h:form>
Some debugging:
The ajax request is triggered adequately. The managed bean updates the GoogleGeoChartModel with new values.
Next, the control flow goes to the Renderer which "encodes" the new values of the chart (that means that the encondeEnd is called again). But, this new "enconding" is not painted in the browser.
The html has two JSF components. My custom component and a h:panelGroup. If in the "update" attribute on primefaces button I put the id of the chart's div and then I trigger the ajax request, the chart does not change. But instead I put the id of the h:panelGroup, the div of the chart disappears.
Vaadin's Window extends Panel. A call to Window.setCaption() simply calls Panel.setCaption() which does support HTML. When I call Window.setCaption(), however, HTML is not supported. Not sure what is going on.
The client side (gwt) code for the caption handling is different. The VWindow class uses Util.escapeHTML() on the caption VPanel does not.
VWindow.java:
public void setCaption(String c, String icon) {
String html = Util.escapeHTML(c);
if (icon != null) {
icon = client.translateVaadinUri(icon);
html = "<img src=\"" + Util.escapeAttribute(icon)
+ "\" class=\"v-icon\" />" + html;
}
DOM.setInnerHTML(headerText, html);
}
VPanel.java
public void setCaption(String text) {
DOM.setInnerHTML(captionText, text);
}