add an error message to panel in apache wicket - java

I have an apache wicket panel for google recaptcha where I integrate wicket with google recaptcha and everything is working as it should, but I need to add an error message or feedbackpanel I am not quite good with wicket, so please advise me what is the best solution for my case?
public class ReCaptchaPanel extends Panel {
private static final long serialVersionUID = -8346261172610929107L;
private static final Logger log = LoggerFactory.getLogger(ReCaptchaPanel.class);
final String publicKey = "6LdrIgceAAAAADWF-gIz_OHJg_5gLu7IVw11hTnt";
final String secretKey = "6LdrIgceAAAAAE9qxHViBj3Sl8uyqUVdluugPRTq";
final String scriptClassName = "data-sitekey";
final String recCaptchaClassName = "g-recaptcha";
String currentLanguage = getLocale().getLanguage();
String scriptValue = "https://www.google.com/recaptcha/api.js?hl=" + currentLanguage;
/* in the following link you can find the language codes
* https://developers.google.com/recaptcha/docs/language
* */
public ReCaptchaPanel(String id) {
super(id);
final IFeedbackMessageFilter feedbackFilter = new ContainerFeedbackMessageFilter(this);
final FeedbackPanel feedbackPanel = new FeedbackPanel("feedback", feedbackFilter);
feedbackPanel.setOutputMarkupId(true);
add(feedbackPanel);
add(new Label("reCaptchaComponent", "").add(new SimpleAttributeModifier("class", recCaptchaClassName))
.add(new SimpleAttributeModifier(scriptClassName, publicKey)));
add(new Label("reCaptchaScript", "").add(new SimpleAttributeModifier("src", scriptValue)));
}
public ReCaptchaPanel(String id, IModel model) {
super(id, model);
}
/**
* Validates Google reCAPTCHA V2 .
*
* #param secretKey Secret key (key given for communication between arena and Google)
* #param response reCAPTCHA response from client side (g-recaptcha-response).
* #return true if validation successful, false otherwise.
*/
public synchronized boolean isCaptchaValid(String secretKey, String response) {
try {
String url = "https://www.google.com/recaptcha/api/siteverify",
params = "secret=" + secretKey + "&response=" + response;
HttpURLConnection http = (HttpURLConnection) new URL(url).openConnection();
http.setDoOutput(true);
http.setRequestMethod("POST");
http.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded; charset=UTF-8");
OutputStream out = http.getOutputStream();
out.write(params.getBytes(StandardCharsets.UTF_8));
out.flush();
out.close();
InputStream res = http.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(res, StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
JSONObject json = new JSONObject(sb.toString());
res.close();
log.info("Google ReCaptcha has been verified");
return json.getBoolean("success");
} catch (Exception e) {
e.printStackTrace();
}
log.error(" Google ReCaptcha failed");
return false;
}
public synchronized boolean isCaptchaValid() {
HttpServletRequest httpServletRequest = ApplicationBase.getHttpServletRequest();
assert httpServletRequest != null;
String response = httpServletRequest.getParameter("g-recaptcha-
response");
return isCaptchaValid(secretKey, response);
}
}
I use this panel in another wicket component ( form) ,
class FormEmail extends Form {
.........
final ReCaptchaPanel reCaptchaPanel = new ReCaptchaPanel("reCaptchaPanel") {
/**
* #see org.apache.wicket.Component#isVisible()
*/
#Override
public boolean isVisible() {
SessionBase session = (SessionBase) getSession();
ItemListPanelConfigParams configParams = new ItemListPanelConfigParams(session);
return configParams.isShowCaptcha();
}
};
add(reCaptchaPanel);
Button buttonSend = new Button("buttonSend") {
#Override
public void onSubmit() {
onSendCallback(emailRecipient, emailReplyTo, comment);
setResponsePage(callbackPage);
}
};
what i need to achieve is that add a message when the verify method in code above is false; I mean an error message, anyone knows how.

Related

HtmlViewService in OBIEE Web Service API is not rendering report (it's only showing spinning loader)

I have a Java application that leverages the OBIEE Web Service API to consume data from the BI Server. I am able to XMLViewService and the WebCatalogService just fine, but I can't quite get the HtmlViewService to properly render a report in the Java app. The report just shows the spinning loader, but never actually renders the report. I'm pretty sure it has to do with the fact that the Java app and the BI Server are on different domains. This is what the API documentation says:
In situations where Oracle BI Web Services and the third-party Web server do not belong to the same Domain Name Service (DNS) domain, users may get JavaScript errors related to browser security constraints for cross-domain scripting. To avoid these issues, use the setBridge() method to modify callback URLs to point to the third-party Web server. Be aware that a Web component executed by the third-party Web server to re-route requests to Oracle BI Web Services is not provided. This function would need to be fulfilled by the third-party application.
Several years ago, I did this same type of integration using .NET/C# and ran in the the same issue because the .NET app and the BI Server were on different domains. As a result, I had to create an HTTP Handler (.ashx file) as well as use the setBridge() method to to solve the issue.
The challenge that I'm having is that I can't find a servlet bridge example for Java. And I'm not too confident in porting the .NET/.ASHX code to a Java servlet/bridge. Does anyone have any code examples or direction they could provide to point me in the right direction? Here's a snippet of code to show you what I'm doing to pull back the report data:
// define report path
ReportRef reportRef = new ReportRef();
reportRef.setReportPath(reportFolder + "/" + reportName);
// set page params
StartPageParams pageParams = new StartPageParams();
pageParams.setDontUseHttpCookies(true);
// set report params
String pageId = htmlService.startPage(pageParams, sawSessionId);
String reportId = pageId + reportName;
htmlService.addReportToPage(pageId, reportId, reportRef, null, null, null, sawSessionId);
// get report html
StringBuffer reportHtml = new StringBuffer();
reportHtml.append(htmlService.getHtmlForReport(pageId, reportId, sawSessionId));
// return html
return reportHtml.toString();
This is the error that is coming back in the browser:
XMLHttpRequest cannot load http://myobiserver.com/analytics/saw.dll?ajaxGo. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://myjavaapp.com' is therefore not allowed access.
Per requested, here is my .NET/.ASHX bridge:
using System.Collections.Specialized;
using System.Net;
using System.Text;
using System.Web;
using System;
using System.Collections;
using System.Configuration;
using System.Collections.Specialized;
using System.Web;
using System.Text;
using System.Net;
using System.IO;
using System.Diagnostics;
/*
This is a ASP.NET handler that handles communication
between the SharePoint site and OracleBI.
It will be deployed to:
C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\OracleBI
*/
public class OracleBridge: IHttpHandler
{
public bool IsReusable {get{return true;}}
public OracleBridge()
{
}
string getServer()
{
string strServer = "http://<enter-domain>/analytics/saw.dll";
int index = strServer.LastIndexOf("/");//split off saw.dll
if (index >=0)
return strServer.Substring(0,index+1);
else
return strServer;
}
public void ProcessRequest(HttpContext context)
{
HttpWebRequest req = forwardRequest(context);
forwardResponse(context,req);
}
private HttpWebRequest forwardRequest(HttpContext context)
{
string strURL = makeURL(context);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(strURL);
req.Method = context.Request.HttpMethod;
NameValueCollection headers = context.Request.Headers;
req.Accept = headers.Get("Accept");
req.Expect = headers.Get("Expect");
req.ContentType = headers.Get("Content-Type");
string strModifiedSince = headers.Get("If-Modified-Since");
if (strModifiedSince != null && strModifiedSince.Length != 0)
req.IfModifiedSince = DateTime.Parse(strModifiedSince);
req.Referer = headers.Get("Referer");
req.UserAgent = headers.Get("User-Agent");
if (!req.Method.Equals("GET"))
{
CopyStreams(context.Request.InputStream,req.GetRequestStream());
}
return req;
}
private void forwardResponse(HttpContext context, HttpWebRequest req)
{
HttpWebResponse resp =null;
try
{
resp = (HttpWebResponse)req.GetResponse();
}
catch(WebException e)
{
resp = (HttpWebResponse)e.Response;
}
context.Response.StatusCode = (int)resp.StatusCode;
for (int i = 0; i < resp.Cookies.Count; i++)
{
Cookie c = resp.Cookies[i];
HttpCookie hc = new HttpCookie(c.Name, c.Value);
hc.Path = c.Path;
hc.Domain = getServer();
context.Response.Cookies.Add(hc);
}
context.Response.ContentType = resp.ContentType;
CopyStreams(resp.GetResponseStream(), context.Response.OutputStream);
}
private string makeURL(HttpContext context)
{
string strQuery = context.Request.Url.Query;
string[] arrParams = strQuery.Split('?','&');
StringBuilder resultingParams = new StringBuilder();
string strURL=null;
foreach(string strParam in arrParams )
{
string[] arrNameValue = strParam.Split('=');
if (!arrNameValue[0].Equals("RedirectURL"))
{
if (strParam.Length != 0)
{
if (resultingParams.Length != 0)
resultingParams.Append("&");
resultingParams.Append(strParam);
}
}
else if (arrNameValue.Length >1)
strURL = HttpUtility.UrlDecode(arrNameValue[1]);
}
if (strURL ==null)
throw new Exception("Invalid URL format. requestURL parameter is missing");
String sAppendChar = strURL.Contains("?") ? "&" : "?";
if (strURL.StartsWith("http:") || strURL.StartsWith("https:"))
{
String tmpURL = strURL + sAppendChar + resultingParams.ToString();
return tmpURL;
}
else
{
String tmpURL = getServer() + strURL + sAppendChar + resultingParams.ToString();
return tmpURL;
}
}
private void CopyStreams(Stream inStr,Stream outStr)
{
byte[] buf = new byte[4096];
try
{
do
{
int iRead = inStr.Read(buf,0,4096);
if (iRead == 0)
break;
outStr.Write(buf,0,iRead);
}
while (true);
}
finally
{
outStr.Close();
}
}
}
Using the BridgeServlet from link (http://pastebin.com/NibVnBLb) posted in previous answer did not work for us. In our web portal, embedding Oracle BI dashboard using BridgeServlet above, redirected us to OBI login page and console log showed incorrect resources(js/css) web links (local URL instead of OBIEE URL's).
Instead we used this class (with some minor adjustments) https://gist.github.com/rafaeltuelho/9376341#file-obieehttpservletbridge-java.
Tested with Java 11, Oracle Business Intelligence 12.2.1.4.0, WSDL v12 of OBIEE (http://OBIEE-server:port/analytics/saw.dll/wsdl/v12), with SSO disabled.
Here it is the BridgeServlet class:
package com.abs.bi;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class OBIEEBridge
*/
public class BridgeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* #see HttpServlet#HttpServlet()
*/
public BridgeServlet() {
super();
}
/**
* #see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
this.processRequest(request, response);
} catch (Exception e) {
throw new ServletException(e);
}
}
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
this.processRequest(request, response);
} catch (Exception e) {
throw new ServletException(e);
}
}
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpURLConnection urlCon = forwardRequest(request);
forwardResponse(response, urlCon);
}
#SuppressWarnings("unchecked")
private String decodeURL(HttpServletRequest request) {
StringBuffer bufURL = new StringBuffer("");
Map<String, String[]> params = request.getParameterMap();
String[] arrURL = params.get("RedirectURL");
String strURL = arrURL == null || arrURL.length == 0 ? null : arrURL[0];
bufURL.append(strURL);
int nQIndex = strURL.lastIndexOf('?');
if (params != null && !params.isEmpty()) {
bufURL.append(((nQIndex >= 0) ? "&" : "?"));
Set<String> keys = params.keySet();
Iterator<String> it = keys.iterator();
while (it.hasNext()) {
try {
String strKey = it.next();
if (strKey.equalsIgnoreCase("RedirectURL")) {
continue;
}
String strEncodedKey = URLEncoder.encode(strKey, "UTF-8");
String[] paramValues = params.get(strKey);
for (String paramValue : paramValues) {
bufURL.append(strEncodedKey);
bufURL.append("=");
bufURL.append(URLEncoder.encode(paramValue, "UTF-8"));
bufURL.append("&");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
bufURL.deleteCharAt(bufURL.length() - 1);
}
return bufURL.toString();
}
#SuppressWarnings("unchecked")
private HttpURLConnection forwardRequest(HttpServletRequest request) throws IOException {
String strURL = decodeURL(request);
String[] arrURL = strURL.split("&", 2);
String baseURL = arrURL[0];
URL url = new URL(baseURL);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
String strMethod = request.getMethod();
con.setRequestMethod(strMethod);
Enumeration<String> en = request.getHeaderNames();
String strHeader;
while (en.hasMoreElements()) {
strHeader = en.nextElement();
String strHeaderValue = request.getHeader(strHeader);
con.setRequestProperty(strHeader, strHeaderValue);
}
// is not a HTTP GET request
if (strMethod.compareTo("GET") != 0) {
con.setDoOutput(true);
con.setDoInput(true);
con.setUseCaches(false);
DataOutputStream forwardStream = new DataOutputStream(con.getOutputStream());
try {
String urlParameters = arrURL[1];
forwardStream.writeBytes(urlParameters);
forwardStream.flush();
} finally {
forwardStream.close();
}
}
return con;
}
private void forwardResponse(HttpServletResponse response, HttpURLConnection con) throws IOException {
int nContentLen = -1;
String strKey;
String strValue;
try {
response.setStatus(con.getResponseCode());
for (int i = 1; true; ++i) {
strKey = con.getHeaderFieldKey(i);
strValue = con.getHeaderField(i);
if (strKey == null) {
break;
}
if (strKey.equals("Content-Length")) {
nContentLen = Integer.parseInt(con.getHeaderField(i));
continue;
}
if (strKey.equalsIgnoreCase("Connection") || strKey.equalsIgnoreCase("Server")
|| strKey.equalsIgnoreCase("Transfer-Encoding") || strKey.equalsIgnoreCase("Content-Length")) {
continue; // skip certain headers
}
if (strKey.equalsIgnoreCase("Set-Cookie")) {
String[] cookieStr1 = strValue.split(";");
String[] cookieStr2 = cookieStr1[0].split("=");
// String[] cookieStr3 = cookieStr1[1].split("=");
/*
* Change the Set-Cookie HTTP Header to remove the 'path' attribute. Thus the
* browser can accept the ORA_BIPS_NQID cookie from Oracle BI Server
*/
Cookie c = new Cookie(cookieStr2[0], cookieStr2[1]);
c.setPath("/");
response.addCookie(c);
} else {
response.setHeader(strKey, strValue);
}
}
copyStreams(con.getInputStream(), response.getOutputStream(), nContentLen);
} finally {
response.getOutputStream().close();
con.getInputStream().close();
}
}
private void copyStreams(InputStream inputStream, OutputStream forwardStream, int nContentLen) throws IOException {
byte[] buf = new byte[1024];
int nCount = 0;
int nBytesToRead = 1024;
int nTotalCount = 0;
do {
if (nContentLen != -1)
nBytesToRead = nContentLen - nTotalCount > 1024 ? 1024 : nContentLen - nTotalCount;
if (nBytesToRead == 0)
break;
// try to read some bytes from src stream
nCount = inputStream.read(buf, 0, nBytesToRead);
if (nCount < 0)
break;
nTotalCount += nCount;
// try to write some bytes in target stream
forwardStream.write(buf, 0, nCount);
} while (true);
}
}
AbsServiceUtils which contains SAWSessionService and HtmlViewService web service calls to Oracle BI server:
package com.abs.bi;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspWriter;
import oracle.bi.web.soap.AuthResult;
import oracle.bi.web.soap.HtmlViewService;
import oracle.bi.web.soap.HtmlViewServiceSoap;
import oracle.bi.web.soap.ReportHTMLLinksMode;
import oracle.bi.web.soap.ReportHTMLOptions;
import oracle.bi.web.soap.ReportRef;
import oracle.bi.web.soap.SAWLocale;
import oracle.bi.web.soap.SAWSessionParameters;
import oracle.bi.web.soap.SAWSessionService;
import oracle.bi.web.soap.SAWSessionServiceSoap;
import oracle.bi.web.soap.StartPageParams;
public class AbsServiceUtils {
private AbsServiceUtils() {
}
public static URL buildWsdlUrl() throws MalformedURLException {
return new URL(IAbsService.BASEWSDLURL);
}
public static void writeBiContent(HttpServletRequest request, JspWriter out, String biReport) throws IOException {
String userAgent = request.getHeader("User-Agent");
Locale userLocale = request.getLocale();
String bridgeServletContextPath = request.getContextPath() + "/bridgeservlet";
String reportHtml = writeBiContent(biReport, userAgent, userLocale, bridgeServletContextPath);
if (out != null) {
out.println(reportHtml);
}
}
public static String writeBiContent(String biReport, String userAgent, Locale userLocale,
String bridgeServletContextPath) throws MalformedURLException {
HtmlViewService htmlViewService = new HtmlViewService(buildWsdlUrl());
HtmlViewServiceSoap htmlClient = htmlViewService.getHtmlViewService();
SAWSessionService sAWSessionService = new SAWSessionService(buildWsdlUrl());
SAWSessionServiceSoap myPort = sAWSessionService.getSAWSessionServiceSoap();
SAWSessionParameters sessionparams = new SAWSessionParameters();
sessionparams.setUserAgent(userAgent);
SAWLocale sawlocale = new SAWLocale();
sawlocale.setLanguage(userLocale.getLanguage());
sawlocale.setCountry(userLocale.getCountry());
sessionparams.setLocale(sawlocale);
sessionparams.setAsyncLogon(false);
AuthResult result = myPort.logonex(IAbsService.BIUSERNAME, IAbsService.BIPASSWORD, sessionparams);
String sessionID = result.getSessionID();
List<String> keepAliveSessionList = new ArrayList<>(1);
keepAliveSessionList.add(sessionID);
myPort.keepAlive(keepAliveSessionList);
StartPageParams spparams = new StartPageParams();
spparams.setDontUseHttpCookies(true);
String pageID = htmlClient.startPage(spparams, sessionID);
/**
* This method will set the path to the servlet which will act like a bridge to
* retrieve all the OBIEE resources like the javascript, CSS and the report.
*/
if (bridgeServletContextPath != null) {
htmlClient.setBridge(bridgeServletContextPath, sessionID);
}
ReportHTMLOptions htmlOptions = new ReportHTMLOptions();
htmlOptions.setEnableDelayLoading(false);
htmlOptions.setLinkMode(ReportHTMLLinksMode.IN_PLACE.value());
ReportRef reportref = new ReportRef();
reportref.setReportPath(IAbsService.BIROOTPATH + biReport);
StartPageParams startpageparams = new StartPageParams();
startpageparams.setDontUseHttpCookies(false);
htmlClient.addReportToPage(pageID, biReport.replace(" ", ""), reportref, null, null, htmlOptions, sessionID);
String reportHtml = htmlClient.getHeadersHtml(pageID, sessionID);
reportHtml = reportHtml + htmlClient.getHtmlForReport(pageID, biReport.replace(" ", ""), sessionID);
reportHtml = reportHtml + htmlClient.getCommonBodyHtml(pageID, sessionID);
return reportHtml;
}
}
IAbsService:
package com.abs.bi;
public interface IAbsService {
public static final String BASEWSDLURL = "http://<OracleBIServer:port>/analytics/saw.dll/wsdl/v12";
public static final String BIUSERNAME = "USER";
public static final String BIPASSWORD = "PASS";
public static final String BIROOTPATH = "/shared/sharedfolder/";
public static final String BIREPORTNAME = "report";
}
Use the link http://pastebin.com/NibVnBLb to check out the bridge code for java. Hope this might be helful.

Android Twitter App Can't Make Objects from Json Response

I'm trying to simply make objects out of a Twitter stream I download from a user. I am using the information provided from https://github.com/Rockncoder/TwitterTutorial. Can someone help determine if this code actually works? Some of the classes are kind of sketchy, as in the Twitter.java class is just an ArrayList and it only has what's listed below in it.
Is my process correct? Any help is appreciated.
public class MainActivity extends ListActivity {
private ListActivity activity;
final static String ScreenName = "riddlemetombers";
final static String LOG_TAG = "rmt";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activity = this;
downloadTweets();
}
// download twitter timeline after first checking to see if there is a network connection
public void downloadTweets() {
ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
new DownloadTwitterTask().execute(ScreenName);
} else {
Log.v(LOG_TAG, "No network connection available.");
}
}
// Uses an AsyncTask to download a Twitter user's timeline
private class DownloadTwitterTask extends AsyncTask<String, Void, String> {
final String CONSUMER_KEY = (String) getResources().getString(R.string.api_key);
final String CONSUMER_SECRET = (String)getResources().getString(R.string.api_secret);
final static String TwitterTokenURL = "https://api.twitter.com/oauth2/token";
final static String TwitterStreamURL = "https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=";
#Override
protected String doInBackground(String... screenNames) {
String result = null;
if (screenNames.length > 0) {
result = getTwitterStream(screenNames[0]);
}
return result;
}
// onPostExecute convert the JSON results into a Twitter object (which is an Array list of tweets
#Override
protected void onPostExecute(String result) {
Twitter twits = jsonToTwitter(result);
// lets write the results to the console as well
for (Tweet tweet : twits) {
Log.i(LOG_TAG, tweet.getText());
}
// send the tweets to the adapter for rendering
ArrayAdapter<Tweet> adapter = new ArrayAdapter<Tweet>(activity, R.layout.items, twits);
setListAdapter(adapter);
}
// converts a string of JSON data into a Twitter object
private Twitter jsonToTwitter(String result) {
Twitter twits = null;
if (result != null && result.length() > 0) {
try {
Gson gson = new Gson();
twits = gson.fromJson(result, Twitter.class);
if(twits==null){Log.d(LOG_TAG, "Twits null");}
else if(twits!=null) {Log.d(LOG_TAG, "Twits NOT null");}
} catch (IllegalStateException ex) {
// just eat the exception
}
}
return twits;
}
// convert a JSON authentication object into an Authenticated object
private Authenticated jsonToAuthenticated(String rawAuthorization) {
Authenticated auth = null;
if (rawAuthorization != null && rawAuthorization.length() > 0) {
try {
Gson gson = new Gson();
auth = gson.fromJson(rawAuthorization, Authenticated.class);
} catch (IllegalStateException ex) {
// just eat the exception
}
}
return auth;
}
private String getResponseBody(HttpRequestBase request) {
StringBuilder sb = new StringBuilder();
try {
DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams());
HttpResponse response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
String reason = response.getStatusLine().getReasonPhrase();
if (statusCode == 200) {
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent();
BufferedReader bReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 8);
String line = null;
while ((line = bReader.readLine()) != null) {
sb.append(line);
}
} else {
sb.append(reason);
}
} catch (UnsupportedEncodingException ex) {
} catch (ClientProtocolException ex1) {
} catch (IOException ex2) {
}
return sb.toString();
}
private String getTwitterStream(String screenName) {
String results = null;
// Step 1: Encode consumer key and secret
try {
// URL encode the consumer key and secret
String urlApiKey = URLEncoder.encode(CONSUMER_KEY, "UTF-8");
String urlApiSecret = URLEncoder.encode(CONSUMER_SECRET, "UTF-8");
// Concatenate the encoded consumer key, a colon character, and the
// encoded consumer secret
String combined = urlApiKey + ":" + urlApiSecret;
// Base64 encode the string
String base64Encoded = Base64.encodeToString(combined.getBytes(), Base64.NO_WRAP);
// Step 2: Obtain a bearer token
HttpPost httpPost = new HttpPost(TwitterTokenURL);
httpPost.setHeader("Authorization", "Basic " + base64Encoded);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
httpPost.setEntity(new StringEntity("grant_type=client_credentials"));
String rawAuthorization = getResponseBody(httpPost);
Authenticated auth = jsonToAuthenticated(rawAuthorization);
// Applications should verify that the value associated with the
// token_type key of the returned object is bearer
if (auth != null && auth.token_type.equals("bearer")) {
// Step 3: Authenticate API requests with bearer token
HttpGet httpGet = new HttpGet(TwitterStreamURL + screenName);
// construct a normal HTTPS request and include an Authorization
// header with the value of Bearer <>
httpGet.setHeader("Authorization", "Bearer " + auth.access_token);
httpGet.setHeader("Content-Type", "application/json");
// update the results with the body of the response
results = getResponseBody(httpGet);
}
} catch (UnsupportedEncodingException ex) {
} catch (IllegalStateException ex1) {
}
return results;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
TWITTER CLASS
import java.util.ArrayList;
// a collection of tweets
public class Twitter extends ArrayList<Tweet> {
private static final long serialVersionUID = 1L;
}
TWEET CLASS
import com.google.gson.annotations.SerializedName;
public class Tweet {
#SerializedName("created_at")
private String DateCreated;
#SerializedName("id")
private String Id;
#SerializedName("text")
private String Text;
#SerializedName("in_reply_to_status_id")
private String InReplyToStatusId;
#SerializedName("in_reply_to_user_id")
private String InReplyToUserId;
#SerializedName("in_reply_to_screen_name")
private String InReplyToScreenName;
#SerializedName("user")
private TwitterUser User;
public String getDateCreated() {
return DateCreated;
}
public String getId() {
return Id;
}
public String getInReplyToScreenName() {
return InReplyToScreenName;
}
public String getInReplyToStatusId() {
return InReplyToStatusId;
}
public String getInReplyToUserId() {
return InReplyToUserId;
}
public String getText() {
return Text;
}
public void setDateCreated(String dateCreated) {
DateCreated = dateCreated;
}
public void setId(String id) {
Id = id;
}
public void setInReplyToScreenName(String inReplyToScreenName) {
InReplyToScreenName = inReplyToScreenName;
}
public void setInReplyToStatusId(String inReplyToStatusId) {
InReplyToStatusId = inReplyToStatusId;
}
public void setInReplyToUserId(String inReplyToUserId) {
InReplyToUserId = inReplyToUserId;
}
public void setText(String text) {
Text = text;
}
public void setUser(TwitterUser user) {
User = user;
}
public TwitterUser getUser() {
return User;
}
#Override
public String toString(){
return getText();
}
}
I've done several Log.d(LOG_TAG, Stuff) to see if I'm getting stuff, and it indicates I'm getting some kind of content back. Maybe the problem is in making objects of the data.
Not sure why you want to use the code from https://github.com/Rockncoder/TwitterTutorial.
Why don't use use http://twitter4j.org. They have give sample example to use it.
Moreover it support Twitter 1.1 as well. Just include twitter-core.jar and you are ready write your code.
Hope it helps.

crawler for gwt application taking too much time

i have a gwt application that i need to optimize for seo ( crawl the content for google), and i have been trying many solutions wich are not meeting our needs (it's taking us a big amount of time to return the html page), the trials are:
I tried to use htmlUnit as headless browser to crawl the page on demand, it takes about 15 second to get the html content (when auditing this timing, it results that 80% of this timing is taken by a loop that waits for background javascript "while (waitForBackgroundJavaScript > 0 && loopCount < _maxLoopChecks) ")
A technic that consists on crawling the page prior to google request, then giving the saved snapshot when google is asking for it (but this solution is definitely not convenient because the content changes very frequently and google may consider this as a "CLOACKING")
Any suggestion?
the code used to crawl:
public class CrawlFilter implements Filter {
private class SyncAllAjaxController extends NicelyResynchronizingAjaxController {
private static final long serialVersionUID = 1L;
#Override
public boolean processSynchron(HtmlPage page, WebRequest request, boolean async) {
return true;
}
}
private final Logger log = Logger.getLogger(CrawlFilter.class.getName());
/**
* Special URL token that gets passed from the crawler to the servlet
* filter. This token is used in case there are already existing query
* parameters.
*/
private static final String ESCAPED_FRAGMENT_FORMAT1 = "_escaped_fragment_=";
private static final int ESCAPED_FRAGMENT_LENGTH1 = ESCAPED_FRAGMENT_FORMAT1.length();
/**
* Special URL token that gets passed from the crawler to the servlet
* filter. This token is used in case there are not already existing query
* parameters.
*/
private static final String ESCAPED_FRAGMENT_FORMAT2 = "&" + ESCAPED_FRAGMENT_FORMAT1;
private static final int ESCAPED_FRAGMENT_LENGTH2 = ESCAPED_FRAGMENT_FORMAT2.length();
private static final long _pumpEventLoopTimeoutMillis = 30000;
private static final long _jsTimeoutMillis = 1000;
private static final long _pageWaitMillis = 200;
private static final int _maxLoopChecks = 2;
private WebClient webClient;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
// Grab the request uri and query strings.
final HttpServletRequest httpRequest = (HttpServletRequest) request;
final String requestURI = httpRequest.getRequestURI();
final String queryString = httpRequest.getQueryString();
final HttpServletResponse httpResponse = (HttpServletResponse) response;
if ((queryString != null) && (queryString.contains(ESCAPED_FRAGMENT_FORMAT1))) {
final int port = httpRequest.getServerPort();
final String urlStringWithHashFragment = requestURI + rewriteQueryString(queryString);
final String scheme = httpRequest.getScheme();
final URL urlWithHashFragment = new URL(scheme, "127.0.0.1", port, urlStringWithHashFragment);
final WebRequest webRequest = new WebRequest(urlWithHashFragment);
log.fine("Crawl filter encountered escaped fragment, will open: " + webRequest.toString());
httpResponse.setContentType("text/html;charset=UTF-8");
final PrintWriter out = httpResponse.getWriter();
out.println(renderPage(webRequest));
out.flush();
out.close();
log.fine("HtmlUnit completed webClient.getPage(webRequest) where webRequest = " + webRequest.toString());
} else {
filterChain.doFilter(request, response);
}
}
#Override
public void destroy() {
if (webClient != null) {
webClient.closeAllWindows();
}
}
#Override
public void init(FilterConfig config) throws ServletException {
}
private StringBuilder renderPage(WebRequest webRequest) throws IOException {
webClient = new WebClient(BrowserVersion.FIREFOX_17);
webClient.getCache().clear();
webClient.getOptions().setCssEnabled(false);
webClient.getOptions().setJavaScriptEnabled(true);
webClient.getOptions().setThrowExceptionOnScriptError(false);
webClient.getOptions().setRedirectEnabled(false);
webClient.setAjaxController(new SyncAllAjaxController());
webClient.setCssErrorHandler(new SilentCssErrorHandler());
final HtmlPage page = webClient.getPage(webRequest);
webClient.getJavaScriptEngine().pumpEventLoop(_pumpEventLoopTimeoutMillis);
int waitForBackgroundJavaScript = webClient.waitForBackgroundJavaScript(_jsTimeoutMillis);
int loopCount = 0;
while (waitForBackgroundJavaScript > 0 && loopCount < _maxLoopChecks) {
++loopCount;
waitForBackgroundJavaScript = webClient.waitForBackgroundJavaScript(_jsTimeoutMillis);
if (waitForBackgroundJavaScript == 0) {
log.fine("HtmlUnit exits background javascript at loop counter " + loopCount);
break;
}
synchronized (page) {
log.fine("HtmlUnit waits for background javascript at loop counter " + loopCount);
try {
page.wait(_pageWaitMillis);
} catch (InterruptedException e) {
log.log(Level.SEVERE, "HtmlUnit ERROR on page.wait at loop counter " + loopCount, e);
}
}
}
webClient.getAjaxController().processSynchron(page, webRequest, false);
if (webClient.getJavaScriptEngine().isScriptRunning()) {
log.warning("HtmlUnit webClient.getJavaScriptEngine().shutdownJavaScriptExecutor()");
webClient.getJavaScriptEngine().shutdownJavaScriptExecutor();
}
final String staticSnapshotHtml = page.asXml();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("<hr />\n");
stringBuilder.append("<center><h3>This is a non-interactive snapshot for crawlers. Follow <a href=\"");
stringBuilder.append(webRequest.getUrl() + "\">this link</a> for the interactive application.<br></h3></center>");
stringBuilder.append("<hr />");
stringBuilder.append(staticSnapshotHtml);
return stringBuilder;
}
/**
* Maps from the query string that contains _escaped_fragment_ to one that
* doesn't, but is instead followed by a hash fragment. It also unescapes any
* characters that were escaped by the crawler. If the query string does not
* contain _escaped_fragment_, it is not modified.
*
* #param queryString
* #return A modified query string followed by a hash fragment if applicable.
* The non-modified query string otherwise.
* #throws UnsupportedEncodingException
*/
private static String rewriteQueryString(String queryString) throws UnsupportedEncodingException {
int index = queryString.indexOf(ESCAPED_FRAGMENT_FORMAT2);
int length = ESCAPED_FRAGMENT_LENGTH2;
if (index == -1) {
index = queryString.indexOf(ESCAPED_FRAGMENT_FORMAT1);
length = ESCAPED_FRAGMENT_LENGTH1;
}
if (index != -1) {
StringBuilder queryStringSb = new StringBuilder();
if (index > 0) {
queryStringSb.append("?");
queryStringSb.append(queryString.substring(0, index));
}
queryStringSb.append("#!");
queryStringSb.append(URLDecoder.decode(queryString.substring(index
+ length, queryString.length()), "UTF-8"));
return queryStringSb.toString();
}
return queryString;
}
}
I suggest having HtmlUnit generate the static html offline. You control the update frequency.
Then, have your servlet filter intercepting the crawler request return the already generated static html.

How do I post a picture/image using the IO Codenameone

Because codenameone can not use external libraries (HttpConnection) then I have to use the internal library / API provided Codenameone, it's just that I've managed to post the data to format text / string by using ConnectionRequest, I want to know is there any way to post the data in the form of an image with using ConnectionRequest? Thank you for your help
Snippet ConnectionRequest i'm using:
ConnectionRequest myrequest = new ConnectionRequest();
myrequest.setUrl("http://www.xxxx.com/mobile/login/");
myrequest.setPost(true);
myrequest.addArgument("email", "info#xxx.net");
myrequest.addArgument("password", "xxx");
myrequest.setPriority(ConnectionRequest.PRIORITY_CRITICAL);
NetworkManager.getInstance().addToQueue(myrequest);
myrequest.addResponseListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
NetworkEvent n = (NetworkEvent)evt;
// gets the data from the server as a byte array...
byte[] data = (byte[])n.getMetaData();
String response = new String(data);
}
});
Thank you for your answer Shai, I see in the repository that has been added Codenameone MultipartRequest function (good job for Codenameone team)
It's just that if I use Write.flush function (), then the program I always get a Stream Closed Exception, but if I comment this command, the program I became normal again, following the code of which I edited slightly Codenameone suit my needs:
/**
* A multipart post request allows a developer to submit large binary data
* files to the server in a post request
*
* #author Shai Almog
*/
public class MultipartRequest extends ConnectionRequest {
private String boundary;
private Hashtable args = new Hashtable();
private Hashtable mimeTypes = new Hashtable();
private static final String CRLF = "\r\n";
protected void readResponse(InputStream input) throws IOException {
// TODO Auto-generated method stub
StringBuffer stringBuffer = new StringBuffer();
int ch;
while ((ch = input.read()) != -1) {
stringBuffer.append((char) ch);
}
fireResponseListener(new NetworkEvent(this, stringBuffer.toString()));
}
/**
* Initialize variables
*/
public MultipartRequest() {
setPost(true);
setWriteRequest(true);
// Just generate some unique random value.
boundary = Long.toString(System.currentTimeMillis(), 16);
// Line separator required by multipart/form-data.
setContentType("multipart/form-data; boundary=" + boundary);
}
/**
* Adds a binary argument to the arguments
* #param name the name of the data
* #param data the data as bytes
* #param mimeType the mime type for the content
*/
public void addData(String name, byte[] data, String mimeType) {
args.put(name, data);
mimeTypes.put(name, mimeType);
}
/**
* Adds a binary argument to the arguments, notice the input stream will be read only during submission
* #param name the name of the data
* #param data the data stream
* #param mimeType the mime type for the content
*/
public void addData(String name, InputStream data, String mimeType) {
args.put(name, data);
mimeTypes.put(name, mimeType);
}
/**
* #inheritDoc
*/
public void addArgument(String name, String value) {
args.put(name, value);
}
/**
* #inheritDoc
*/
protected void buildRequestBody(OutputStream os) throws IOException {
Writer writer = null;
writer = new OutputStreamWriter(os, "UTF-8");
Enumeration e = args.keys();
while(e.hasMoreElements()) {
String key = (String)e.nextElement();
Object value = args.get(key);
writer.write("--" + boundary);
writer.write(CRLF);
if(value instanceof String) {
writer.write("Content-Disposition: form-data; name=\"" + key + "\"");
writer.write(CRLF);
writer.write("Content-Type: text/plain; charset=UTF-8");
writer.write(CRLF);
writer.write(CRLF);
// writer.flush(); // always error if I use this??
writer.write(Util.encodeBody((String)value));
writer.write(CRLF); // always error if I use this??
// writer.flush();
} else {
writer.write("Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + key +"\"");
writer.write(CRLF);
writer.write("Content-Type: ");
writer.write((String)mimeTypes.get(key));
writer.write(CRLF);
writer.write("Content-Transfer-Encoding: binary");
writer.write(CRLF);
writer.write(CRLF);
if(value instanceof InputStream) {
InputStream i = (InputStream)value;
byte[] buffer = new byte[8192];
int s = i.read(buffer);
while(s > -1) {
os.write(buffer, 0, s);
s = i.read(buffer);
}
} else {
os.write((byte[])value);
}
writer.write(CRLF);
// writer.flush();
}
writer.write(CRLF);
//writer.flush();
}
writer.write("--" + boundary + "--");
writer.write(CRLF);
writer.close();
}
Examples of how to use :
public class FormTest extends Form implements ActionListener{
private Button btnUpload;
private Button btnBrowse;
public FormTest(){
NetworkManager.getInstance().start();
setLayout(new BoxLayout(BoxLayout.Y_AXIS));
btnBrowse = new Button("Browse");
btnUpload = new Button("Upload");
addComponent(btnBrowse);
addComponent(btnUpload);
btnBrowse.addActionListener(this);
btnUpload.addActionListener(this);
}
private MultipartRequest request;
public void actionPerformed(ActionEvent evt) {
// TODO Auto-generated method stub
if (evt.getSource().equals(btnBrowse)){
//browse here
btnBrowse.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// TODO Auto-generated method stub
Utility.pathfile = "";
Utility.main.getFile();
new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
while (Utility.pathfile.equals("")) {
}
}
}).start();
}
});
}
if (evt.getSource().equals(btnUpload)){
//upload here
request = new MultipartRequest();
request.setUrl("http://10.151.xx.xx/testuploadinfo.php");
request.addArgument("Parameter1","Value1");
//add the data image
request.addData("file", getTheImageByte("Your Url to Image here"),"image/png");
request.setPriority(ConnectionRequest.PRIORITY_CRITICAL);
request.addResponseListener(FormTest.this);
NetworkManager.getInstance().addToQueue(request);
//Dialog.show("Test","ok", "","");
}
if (evt instanceof NetworkEvent) {
NetworkEvent ne = (NetworkEvent)evt;
Dialog.show("Result:", ne.getMetaData().toString(), "","");
}
}
private byte[] getTheImageByte(String url) {
Bitmap bitmap = null, scaleBitmap = null;
byte[] data = null;
InputStream inputStream = null;
FileConnection fileConnection = null;
try {
fileConnection = (FileConnection) Connector
.open(url);
if (fileConnection.exists()) {
inputStream = fileConnection.openInputStream();
data = new byte[(int) fileConnection.fileSize()];
data = IOUtilities.streamToBytes(inputStream);
}
} catch (Exception e) {
try {
if (inputStream != null) {
inputStream.close();
}
if (fileConnection != null) {
fileConnection.close();
}
} catch (Exception exp) {
}
}
return data;// return the scale Bitmap not the original bitmap;
}
}
And simple PHP code :
<?php
print_r($_FILES);
$new_image_name = "image.jpg";
move_uploaded_file($_FILES["file"]["tmp_name"], "sia/".$new_image_name);
?>
Sure you can just add the image data as an argument to the request but you will need to encode it. Alternatively you can override the method:
protected void buildRequestBody(OutputStream os) throws IOException
And write into the post output stream any arbitrary data you need.

help needed in importing yahoo contacts to my page through play framework

I am working in Play framework and I am trying to import yahoo contacts.
I have cleared the yahoo authentication api's to get the access_token and guid.
With that, when I try to import the contacts using http://social.yahooapis.com/v1/user/{guid}/contacts with the auth parameters, I am getting the connection timeout exception in my page and log.
When I paste the same contact url being generated through the code in the browser, I am getting as signature_invalid
I hope I have followed all the stuffs mentioned in the yahoo api dev notes to create the oauth_signature, but still I am not getting it.
Can anyone help me on this please?
Controller code for generating signature -
public class Yahoo {
private static String token = "";
private static String currentUrl = "";
private static String verifier = "";
private static String tokenSecret = "";
private static String accessToken = "";
private static String yahooGuid = "";
public Yahoo(){
}
/**
* Requests access to the Yahoo API for request token.
* #return True if the request is successful, false if not.
*/
public static Yahoo authorize() {
Session session = Session.current();
if(session.contains("authorized") && session.get("authorized").equals("0")){
session.put("authorized", "1");
String url = getRequestTokenUrl();
WS.WSRequest request = WS.url(url);
Logger.info("Yahoo: Create request to get request token'%s'", request.url);
WS.HttpResponse response = request.get();
Logger.info("Yahoo: Token response status is %d", response.getStatus());
if (response.getStatus() == 200){
String[] pairs = response.getString().split("&");
String[] tokenSecret = pairs[1].split("=");
Yahoo.tokenSecret = tokenSecret[1];
for (String pair : pairs) {
String[] kv = pair.split("=");
if (kv.length != 2) {
break;
} else {
if (kv[0].equals("oauth_token")) {
Yahoo.token = kv[1];
}
}
}
Logger.info("level 1 - yahoo token = %s, secret = %s",Yahoo.token, Yahoo.tokenSecret);
}
}
return null;
}
/**
* Requests access to the Yahoo API for access token.
* #return True if the request is successful, false if not.
*/
public static Yahoo getAccessToken(){
String url = getAccessTokenUrl();
WS.WSRequest request = WS.url(url);
Logger.info("Yahoo: Create request to get Access token'%s'", request.url);
WS.HttpResponse response = request.get();
Logger.info("Yahoo: Token response status is %d", response.getStatus());
if (response.getStatus() == 200){
String[] pairs = response.getString().split("&");
String[] guidPair = pairs[5].split("=");
String[] tokenSecret = pairs[1].split("=");
Yahoo.tokenSecret = tokenSecret[1];
yahooGuid = guidPair[1];
for (String pair : pairs) {
String[] kv = pair.split("=");
if (kv.length != 2) {
break;
} else {
if (kv[0].equals("oauth_token")) {
Yahoo.accessToken = kv[1];
}
}
}
Logger.info("level 3 - yahoo token = %s, secret = %s, guid = %s",Yahoo.accessToken, Yahoo.tokenSecret, Yahoo.yahooGuid);
}
return null;
}
/**
* Requests Signature
* #return String
*/
public static String getBaseSignature(){
String signature = "";
String data = generateBaseString();
String key = keyString();
Logger.info("key : %s",key);
try {
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(data.getBytes());
signature = new String(Base64.encode(rawHmac));
signature = java.net.URLEncoder.encode(signature, "ISO-8859-1");
Logger.info("Signature=%s", signature);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return signature;
}
/**
* Requests access to the Yahoo API for getting contacts.
*
*/
public static void getContacts(){
String url = getContactUrl();
WS.WSRequest request = WS.url(url);
Logger.info("Yahoo: Create request to get Contacts '%s'", request.url);
WS.HttpResponse response = request.get();
Logger.info("Yahoo: Token response status is %d", response.getStatus());
if (response.getStatus() == 200){
String[] pairs = response.getString().split("&");
for(int i=0;i<pairs.length;i++){
Logger.info("%s", pairs[i]);
}
}else {
//errors contains a JSON response
JsonParser parser = new JsonParser();
JsonObject message = parser.parse(response.getString()).getAsJsonObject();
Logger.error("Yahoo: Could not get token (status %d): %s", response.getStatus(), message.get("message").getAsString());
}
}
public static String generateBaseString(){
String baseString = getBaseUrl();
Logger.info("token secret : %s",tokenSecret);
Logger.info("base url : %s",baseString);
Logger.info("callback url : %s",getCallBackUrl().toString().split("oauth_token")[0].replace('?', '\0'));
String returnString = "";
try {
returnString = java.net.URLEncoder.encode("GET", "ISO-8859-1") + "&" + java.net.URLEncoder.encode("http://social.yahooapis.com/v1/user/"+yahooGuid+"/contacts", "ISO-8859-1") + "&" + java.net.URLEncoder.encode(baseString, "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Logger.info("Yahoo: Base string: %s",returnString);
return returnString;
}
public static String keyString(){
String consumerSecret = encodeString(getConsumerSecret());
String tokenSec = encodeString(tokenSecret);
String keyString = consumerSecret + encodeString("&") + tokenSec;
return keyString;
}
public static String encodeString(String msgString){
String msg = "";
try {
msg = java.net.URLEncoder.encode(msgString.toString(), "ISO-8859-1");
Logger.info("encode=%s", msg);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return msg;
}
You are getting invalid signature when you copy and paste on the browser because you are not passing the oauth headers.
I strongly suggest you using a lib to do that, I have done the exact same thing with linkedin:
http://geeks.aretotally.in/projects/play-framework-linkedin-module
You will find explanation and link to the source code.
Hope it helps.
Thank you,
Felipe
http://geeks.aretotally.in
http://playframework.info
http://mashup.fm

Categories

Resources