I have webapp in Vaadin Framework 8. I have Windows GUI app in C#.
The gui app is using WebBrowser component to display webapp. WebBrowser component is internally using IE11 core through ActiveX. I can successfully load and display the webapp in the gui app browser component.
I need to pass data from webapp to the gui app.
The webapp has many rows loaded on server side, only few are displayed in grid. I want to pass all data from webapp to gui app in some format (csv or json).
I have tryed some approaches, but I wasn't successfull.
[Approach 1]
Webapp: attach downloadable resource (csv) to Link with predefined id using FileDownloader. Download by user mouse click works fine, file save dialog pops up and data are downloaded successfully.
Link link = new Link("Data");
link.setId("myId");
StreamResource resource = getMyResource(data);
FileDownloader downloader = new FileDownloader(resource);
downloader.extend(link);
Page.getCurrent().getJavaScript().addFunction("test", new JavaScriptFunction() {
#Override
public void call(JsonArray arguments) {
Page.getCurrent().getJavaScript()
.execute("document.getElementById('myId').click()");
}
});
Gui app: raise onClick event on link and capture WebBrowser.FileDownload event, capture WebBrowser.Navigate event.
I have failed to raise onClick event from C# using:
HtmlElement el = webBrowser.Document.GetElementById("myId");
el.RaiseEvent("onClick");
el.InvokeMember("click");
webBrowser.Document.InvokeScript("document.getElementById('myId').click();", null);
webBrowser.Document.InvokeScript("test", null);
Result:
WebBrowser.FileDownload event doesn't work (is fired but can't capture url nor data), capture WebBrowser.Navigate event works partialy (can see resource url, but can't download data using byte[] b = new WebClient().DownloadData(e.Url);).
[Approach 2]
Similar to approach 1. I tryed to get resource url, put the direct url to Link and download the resource in c# using direct link. I can construct the same resource url as is used by browser to download data when user clicks the link.
Extended file downloader that keeps resource, key and connector:
public class ExtendedFileDownloader extends FileDownloader {
private String myKey;
private Resource myResource;
private ClientConnector myConnector;
public ExtendedFileDownloader(StreamResource resource, ClientConnector connector) {
super(resource);
myConnector = connector;
}
#Override
protected void setResource(String key, Resource resource) {
super.setResource(key, resource);
myKey = key;
myResource = resource;
}
public String getResourceUrl() {
ResourceReference ref =
ResourceReference.create(
myResource,
(myConnector != null) ? myConnector : this,
myKey);
String url = ref.getURL();
return url;
}
}
In view:
// fix app://path... urls to /<base-path>/path urls
private String fixResourceReferenceUrl(String resourceReferenceUrl) {
String resourceReferencePath = resourceReferenceUrl.replace("app://", "");
String uiBaseUrl = ui.getUiRootPath();
String fixedUrl = uiBaseUrl + "/" + resourceReferencePath;
return fixedUrl;
}
Link link2 = new Link("Data2");
link2.setId("myId2");
StreamResource resource = getMyResource(data);
ExtendedFileDownloader downloader = new ExtendedFileDownloader(resource, this);
String fixedResourceUrl = fixResourceReferenceUrl(downloader.getResourceUrl());
link2.setResource(new ExternalResource(fixedResourceUrl));
Result:
The data cannot be downloaded using this link, server error 410 or NotFound errors.
Any Ideas ? Any other approaches to try ?
I have finally solved the problem. The solution is very close to approach 2.
The resource url is passed in element with custom attribute. C# WebClient needs to set cookies from WebBrowser and Referer HTTP headers. The data can be successfully downloaded by C# app.
Element attribute in vaadin webapp can be set using Vaadin-addon Attributes.
Cookies in C# app can be retrieved using this solution.
// Fix resource urls begining with app://
public String fixResourceReferenceUrl(String resourceReferenceUrl) {
try {
String uiRootPath = UI.getCurrent().getUiRootPath();
URI location = Page.getCurrent().getLocation();
String appLocation = new URIBuilder()
.setScheme(location.getScheme())
.setHost(location.getHost())
.setPort(location.getPort())
.setPath(uiRootPath)
.build()
.toString();
String resourceReferencePath = resourceReferenceUrl.replace("app://", "");
String fixedUrl = appLocation + "/" + resourceReferencePath;
return fixedUrl;
}
catch (Exception e) {
return null;
}
}
In view (using ExtendedFileDownloader from above):
Link link = new Link("Data");
link.setId("myId");
StreamResource resource = getMyResource(data);
ExtendedFileDownloader downloader = new ExtendedFileDownloader(resource);
downloader.extend(link);
Attribute attr = new Attribute("x-my-data", fixResourceReferenceUrl(downloader.getResourceUrl()));
attr.extend(link);
link.setVisible(true);
In C# app:
[DllImport("wininet.dll", SetLastError = true)]
public static extern bool InternetGetCookieEx(
string url,
string cookieName,
StringBuilder cookieData,
ref int size,
Int32 dwFlags,
IntPtr lpReserved);
private const Int32 InternetCookieHttponly = 0x2000;
public static String GetUriCookies(String uri)
{
// Determine the size of the cookie
int datasize = 8192 * 16;
StringBuilder cookieData = new StringBuilder(datasize);
if (!InternetGetCookieEx(uri, null, cookieData, ref datasize, InternetCookieHttponly, IntPtr.Zero))
{
if (datasize < 0)
return null;
// Allocate stringbuilder large enough to hold the cookie
cookieData = new StringBuilder(datasize);
if (!InternetGetCookieEx(
uri,
null, cookieData,
ref datasize,
InternetCookieHttponly,
IntPtr.Zero))
return null;
}
return cookieData.ToString();
}
private void button_Click(object sender, EventArgs e)
{
HtmlElement el = webBrowser.Document.GetElementById("myId");
String url = el.GetAttribute("x-my-data");
String cookies = GetUriCookies(url);
WebClient wc = new WebClient();
wc.Headers.Add("Cookie", cookies);
wc.Headers.Add("Referer", WEB_APP_URL); // url of webapp base path, http://myhost/MyUI
byte[] data = wc.DownloadData(url);
}
Related
I'm trying to use M2Doc programmatically, I managed to generate my .docx file without getting errors in the validation part but I'm getting the following Error in the generated document:
{m:self.Name} Couldn't find the 'aqlFeatureAccess(org.eclipse.emf.common.util.URI.Hierarchical,java.lang.String)' service
The "self.Name" part is what I wrote in my template.
I think I'm lacking some kind of reference to a service but I don't know how to fix it.
The self variable is a reference to a model based on a meta-model I created. But I'm not sure I imported it correctly in my code.
I based my code on the code I found on the M2Doc website + some code I found on their GitHub, especially concerning how to add a service in the queryEnvironment.
I searched in the source code of acceleo and M2Doc to see which services they add but it seems that they already import all the services I'm using.
As I said, the validation part is going well and doesn't generate a validation file.
public static void parseDocument(String templateName) throws Exception{
final URI templateURI = URI.createFileURI("Template/"+templateName+"."+M2DocUtils.DOCX_EXTENSION_FILE);
final IQueryEnvironment queryEnvironment =
org.eclipse.acceleo.query.runtime.Query.newEnvironmentWithDefaultServices(null);
final Map<String, String> options = new HashMap<>(); // can be empty
M2DocUtils.prepareEnvironmentServices(queryEnvironment, templateURI, options); // delegate to IServicesConfigurator
prepareEnvironmentServicesCustom(queryEnvironment, options);
final IClassProvider classProvider = new ClassProvider(ClassLoader.getSystemClassLoader()); // use M2DocPlugin.getClassProvider() when running inside Eclipse
try (DocumentTemplate template = M2DocUtils.parse(templateURI, queryEnvironment, classProvider)) {
ValidationMessageLevel validationLevel = validateDocument(template, queryEnvironment, templateName);
if(validationLevel == ValidationMessageLevel.OK){
generateDocument(template, queryEnvironment, templateName, "Model/ComplexKaosModel.kaos");
}
}
}
public static void prepareEnvironmentServicesCustom(IQueryEnvironment queryEnvironment, Map<String, String> options){
Set<IService> services = ServiceUtils.getServices(queryEnvironment, FilterService.class);
ServiceUtils.registerServices(queryEnvironment, services);
M2DocUtils.getConfigurators().forEach((configurator) -> {
ServiceUtils.registerServices(queryEnvironment, configurator.getServices(queryEnvironment, options));
});
}
public static void generateDocument(DocumentTemplate template, IQueryEnvironment queryEnvironment,
String templateName, String modelPath)throws Exception{
final Map<String, Object> variable = new HashMap<>();
variable.put("self", URI.createFileURI(modelPath));
final Monitor monitor = new BasicMonitor.Printing(System.out);
final URI outputURI = URI.createFileURI("Generated/"+templateName+".generated."+M2DocUtils.DOCX_EXTENSION_FILE);
M2DocUtils.generate(template, queryEnvironment, variable, outputURI, monitor);
}
The variable "self" contains an URI:
variable.put("self", URI.createFileURI(modelPath));
You have to load your model and set the value of self to an element from your model using something like:
final ResourceSet rs = new ResourceSetImpl();
final Resource r = rs.getResource(uri, true);
final EObject value = r.getContents()...;
variable.put("self", value);
You can get more details on resource loading in the EMF documentation.
I am now going through my code to ensure it is correct and consistent. Straight way I find that I load a view in two different ways:
One is:
public HikeRecordView() {
//On load of page get the stored view data and create the page
verticalPanel.addAttachHandler(new Handler() {
public void onAttachOrDetach(AttachEvent event) {
if (event.isAttached()) {
rpc = (DBConnectionAsync) GWT.create(DBConnection.class);
ServiceDefTarget target = (ServiceDefTarget) rpc;
String moduleRelativeURL = GWT.getModuleBaseURL() + "MySQLConnection";
target.setServiceEntryPoint(moduleRelativeURL);
horizontalPanel_Existing.clear();
verticalPanel.clear();
AsyncCallback<ViewData> callback = new ViewDataHandler<ViewData>(HikeRecordView.this);
rpc.getViewData(callback);
}
}
});
initWidget(verticalPanel);
}
And the other is:
public PackHolidayView() {
rpc = (DBConnectionAsync) GWT.create(DBConnection.class);
ServiceDefTarget target = (ServiceDefTarget) rpc;
String moduleRelativeURL = GWT.getModuleBaseURL() + "MySQLConnection";
target.setServiceEntryPoint(moduleRelativeURL);
//On load of page render the page
verticalPanel.addAttachHandler(new Handler() {
public void onAttachOrDetach(AttachEvent event) {
verticalPanel.clear();
verticalPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
//On load of page get the Account Level and ID of the account holder.
AsyncCallback<ViewData> callback = new ViewDataHandler<ViewData>(PackHolidayView.this);
rpc.getViewData(callback);
}
});
initWidget(verticalPanel);
}
They both seem to work so which is the best/recommended way to load a view please?
You should think about splitting the code between Model, View and Presenter. Create appropriate layers. Consider using GIN for Dependency Injection.
Read an article located here: http://www.canoo.com/blog/2011/04/05/gwt-dependency-injection-recipes-using-gin/
I have a requirement to display the image as part of the FieldGroup. This is for the functionality where the Image appears as normal on a web page, when in edit mode I need to edit this image value by providing an 'upload' option.
I have a Pojo with a property of type com.vaadin.ui.Image along with the other String and Integer values.
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
I need to work this Image as a normal form element, for example when I edit a form, I have an editable TextField to type in the String value and change it, the same way I intend to display an upload button where I would have an option to replace the existing image.
I have tried using EasyUploads addon for this purpose by intercepting the build method of FieldGroup and specifying the Image field as of type org.vaadin.easyuploads.UploadField
Like this;
#Override
#SuppressWarnings({ "rawtypes", "unchecked" })
protected <T extends Field> T build(String caption, Class<?> dataType,
Class<T> fieldType) throws BindException {
T field = super.build(caption, dataType, fieldType);
if (caption.equalsIgnoreCase("image")) {
final UploadField imageField = new UploadField() {
#Override
protected void updateDisplay() {
final byte[] pngData = (byte[]) getValue();
String filename = getLastFileName();
String mimeType = getLastMimeType();
long filesize = getLastFileSize();
if (mimeType.equals("image/jpeg")) {
StreamSource imagesource = new ImageSource(pngData);
StreamResource resource = new StreamResource(
imagesource, "Uploaded File");
Embedded embedded = new Embedded("Image:" + filename
+ "(" + filesize + " bytes)", resource);
getRootLayout().addComponent(embedded);
} else {
super.updateDisplay();
}
}
};
imageField.setFieldType(FieldType.BYTE_ARRAY);
...
This however fails to display the already available image, errors out with the stacktrace:
Caused by: java.lang.IllegalArgumentException: Property type class com.vaadin.ui.Image is not compatible with UploadField
at org.vaadin.easyuploads.UploadField.setPropertyDataSource(UploadField.java:1021)
at com.vaadin.data.fieldgroup.FieldGroup.bind(FieldGroup.java:265)
at com.vaadin.data.fieldgroup.BeanFieldGroup.bind(BeanFieldGroup.java:167)
at com.vaadin.data.fieldgroup.FieldGroup.setItemDataSource(FieldGroup.java:106)
at com.vaadin.data.fieldgroup.BeanFieldGroup.setItemDataSource(BeanFieldGroup.java:142)
Is there a cleaner way of using an Image as part of the FieldGroup in vaadin 7?
I would suggest replacing in your Pojo the Image instance with a simple byte[], because looking through the UploadField code, I can't see any natural way of converting the result from the upload (which is either a byte[] or a File instance) into something else, using the FieldGroup like you asked.
If you look inside AbstractField.getValue(), you will see that the model value is eventually passed through a settable converter which would have normally helped you in this case (see com.vaadin.data.util.converter.Converter). But I think you are pretty much forced to use byte[] if you want to bind a image bean to the FieldGroup.
Anyway, if you DO replace with byte[] the following steps will help you:
Create a custom FieldGroupFieldFactory that will create an UploadField if you want to bind to a byte[] property + passes a ValueChangeListener for the UploadField is done uploading:
public class ImageEnhancedFieldFactory extends DefaultFieldGroupFieldFactory {
private Property.ValueChangeListener fileUploadedListener;
private ImageEnhancedFieldFactory(Property.ValueChangeListener fileUploadedListener) {
this.fileUploadedListener = fileUploadedListener;
}
#Override
public <T extends Field> T createField(Class<?> type, Class<T> fieldType) {
if (byte[].class.equals(type)) {
UploadField uploadField = new UploadField(UploadField.StorageMode.MEMORY);
uploadField.setFieldType(UploadField.FieldType.BYTE_ARRAY);
uploadField.setButtonCaption("Change image");
uploadField.addListener(fileUploadedListener);
return (T) uploadField;
}
return super.createField(type, fieldType);
}
}
Create an Image instance that shows the content of the byte[] from the pojo:
final ImagePojo imagePojo = new ImagePojo();
imagePojo.setName("superman");
imagePojo.setImageContent(new byte[0]);
BeanItem<ImagePojo> item = new BeanItem<ImagePojo>(imagePojo);
final StreamResource imageResource = new StreamResource(new StreamResource.StreamSource() {
#Override
public InputStream getStream() {
return new ByteArrayInputStream(imagePojo.getImageContent());
}
}, "myimage");
imageResource.setCacheTime(0);
final Image image = new Image("Image", imageResource);
addComponent(image);
NOTE: its necessary to set the cache time to 0 in order to prevent the browser from caching the resource( see https://vaadin.com/book/vaadin7/-/page/components.embedded.html in the section Generating and Reloading Images)
3.Create the FieldGroup (with the new FieldGroupFieldFactory set) and bind to the properties of the pojo, including the one that contains the image content (the byte[]):
FieldGroup fieldGroup = new FieldGroup(item);
fieldGroup.setFieldFactory(new ImageEnhancedFieldFactory(new Property.ValueChangeListener() {
#Override
public void valueChange(Property.ValueChangeEvent event) {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String filename = "myfilename-" + df.format(new Date()) + ".jpg";
imagePojo.setImageContent((byte[])event.getProperty().getValue());
image.markAsDirty();
imageResource.setFilename(filename);
}
}));
addComponent(fieldGroup.buildAndBind("Image name", "name"));
addComponent(fieldGroup.buildAndBind("Image content", "imageContent"));
I left a snippet on Gist of a component that you can paste in you UI and play around if you need (https://gist.github.com/gabrielruiu/9953279)
I use spring-social-facebook to interact with Graph API (1.0.3.RELEASE if it's metter). And I can't find any operation to retrieve profile's image url. I found only operations which return array of bytes and they are not very convenient for the implementation.
Does any kind of operation which retrieves image url exist in Spring Social?
If not, is there any 'tidy' workaround for this?
Thanks!
Finally, I didn't find any mention of profile picture url in spring social
My solution:
Initially I planned to extend UserOperations (FacebokTemplate.userOperations) class and add new method. But it's a package-level class and doens't visible outside.
So I decided to create my own template class extending FacebookTemplate and implement the method in this way:
public String fetchPictureUrl(String userId, ImageType imageType) {
URI uri = URIBuilder.fromUri(GRAPH_API_URL + userId + "/picture" +
"?type=" + imageType.toString().toLowerCase() + "&redirect=false").build();
JsonNode response = getRestTemplate().getForObject(uri, JsonNode.class);
return response.get("data").get("url").getTextValue();
}
i just ran into same issue, hope this will help someone else
Connection<Facebook> connection = userConnectionRepository.findPrimaryConnection(Facebook.class);
connection.createData().getImageUrl()
You could take the miniature picture in this way :
"http://graph.facebook.com/" + fbUser.getId() + "/picture?type=square"
In social API you have ability just to fetch the image binary file:
byte[] profileImage = facebook.userOperations().getUserProfileImage(imageType);
To get URL you need to have something custom (as mentioned in the post above).
I took the part of code from Facebook Social API (see Facebook template source code for fetchImage) and the following utility class:
public final class FacebookUtils {
private static final String PICTURE_PATH = "picture";
private static final String TYPE_PARAMETER = "type";
private static final String WIDTH_PARAMETER = "width";
private static final String HEIGHT_PARAMETER = "height";
private FacebookUtils() {
}
public static String getUserProfileImageUrl(Facebook facebook, String userId, String width, String height, ImageType imageType) {
URIBuilder uriBuilder = URIBuilder.fromUri(facebook.getBaseGraphApiUrl() + userId + StringUtils.SLASH_CHARACTER + PICTURE_PATH);
if (imageType != null) {
uriBuilder.queryParam(TYPE_PARAMETER, imageType.toString().toLowerCase());
}
if (width != null) {
uriBuilder.queryParam(WIDTH_PARAMETER, width.toString());
}
if (height != null) {
uriBuilder.queryParam(HEIGHT_PARAMETER, height.toString());
}
URI uri = uriBuilder.build();
return uri.toString();
}
}
EDIT: This occurs during any component AJAX call.
I am building a web application using ICEFaces 3.2.0 community along with Spring Security 3.2 Everything has been going very well up until a few days ago. I have an ACE AutoCompleteEntry component in the page with a backing bean attached to the value as the following example:
<ace:autoCompleteEntry id="autoCompleteState"
label="State"
labelPosition="top"
value="#{autoCompleteEntry.selectedText}"
rows="10" width="160"
filterMatchMode="startsWith">
<f:selectItems value="#{autoCompleteEntry.states}"/>
</ace:autoCompleteEntry>
The backing bean attached is as follows:
#ManagedBean(name=AutoCompleteEntry.BEAN_NAME)
#SessionScoped
public class AutoCompleteEntry implements Serializable {
public static final String BEAN_NAME = "autoCompleteEntry";
public static final String STATE_FILENAME = "State_Names.txt";
public static final String RESOURCE_PATH = "/resources/selectinputtext/";
public AutoCompleteEntry() {
}
public List<SelectItem> states;
public List<SelectItem> getStates() {
if(states == null) {
states = new ArrayList<SelectItem>();
for(String state : readStateFile()) {
states.add(new SelectItem(state));
}
}
return states;
}
private String selectedText = null;
public String getSelectedText() {return selectedText;}
public void setSelectedText(String selectedText) {this.selectedText = selectedText;}
private static List<String> readStateFile() {
InputStream fileIn = null;
BufferedReader in = null;
try {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
fileIn = ec.getResourceAsStream(AutoCompleteEntry.RESOURCE_PATH + STATE_FILENAME);
if(fileIn != null) {
in = new BufferedReader(new InputStreamReader(fileIn));
List<String> loadedStates = new ArrayList<String>(53);
String read;
while((read = in.readLine()) != null) {
loadedStates.add(read);
}
return loadedStates;
}
}catch (IOException failedRead) {
failedRead.printStackTrace();
}finally {
try {
if(in != null) {
in.close();
}
}catch (IOException failedClose) {
failedClose.printStackTrace();
}
}
List<String> errorReturn = new ArrayList<String>(1);
errorReturn.add("Error Loading State List");
return errorReturn;
}
}
The problem is that each time I attempt to test the component instead of bringing up a list of the States it redirects to an absolute path of my main page, which results in a 404. In developer tools I see an error of:
> Uncaught TypeError: Cannot read property 'value' of undefined
bridge.uncompressed.js.jsf:2701
namespace.onAfterUpdate.viewIDElement bridge.uncompressed.js.jsf:2701
apply bridge.uncompressed.js.jsf:122
(anonymous function) bridge.uncompressed.js.jsf:484
(anonymous function) bridge.uncompressed.js.jsf:363
(anonymous function) bridge.uncompressed.js.jsf:240
broadcast bridge.uncompressed.js.jsf:483
(anonymous function) bridge.uncompressed.js.jsf:1928
sendEvent jsf.js.jsf:1447
AjaxEngine.req.sendRequest jsf.js.jsf:1333
request jsf.js.jsf:1834
fullSubmit bridge.uncompressed.js.jsf:2309
submit bridge.uncompressed.js.jsf:2314
iceSubmit compat.uncompressed.js.jsf:1523
onclick
The developer tools log shows:
> [window] persisted focus for element "autoCompleteState_input"
bridge.uncompressed.js.jsf:1252
[window] full submit to localhost:8181/HHCA_Portal/pages/secure/HHCA.jsf
javax.faces.execute: #all
javax.faces.render: patientRecordsForm
javax.faces.source: autoCompleteState_input
view ID: v33tl98j
event type: unknown bridge.uncompressed.js.jsf:1252
XHR finished loading: "localhost:8181/HHCA_Portal/pages/secure/HHCA.jsf".
jsf.js.jsf:1334
AjaxEngine.req.sendRequest jsf.js.jsf:1334
request jsf.js.jsf:1834
fullSubmit bridge.uncompressed.js.jsf:2309
ice.ace.AjaxRequest ace-jquery.uncompressed.js.jsf:20854
ice.ace.ab ace-jquery.uncompressed.js.jsf:20779
ice.ace.Autocompleter.getUpdatedChoices autocompleteentry.js.jsf:695
ice.ace.Autocompleter.onObserverEvent autocompleteentry.js.jsf:637
(anonymous function)
I have spent many hours working on this and other issues, and I have run out of ideas. If someone has some kind of assistance I would really appreciate the help.
If you are using JSF 2 then you can add your own exception handler , this should be able to capture ajax requests.
<factory>
<exception-handler-factory>
test.MyExceptionHandlerFactory
</exception-handler-factory>
</factory>
see the examples here ,
http://balusc.blogspot.com/2012/03/full-ajax-exception-handler.html
http://wmarkito.wordpress.com/2012/04/05/adding-global-exception-handling-using-jsf-2-x-exceptionhandler/