I have written the following code using the Utgard OPC library.
I need to read data from an OPC server once every 15 seconds. However, I'm not sure if this is the most optimal way to implement it. In my scenario I require to read upward of 300 tags from the server.
Any suggestions?
package opcClientSalem;
import java.util.concurrent.Executors;
import org.jinterop.dcom.common.JIException;
//import org.jinterop.dcom.core.JIVariant;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.openscada.opc.lib.common.NotConnectedException;
import org.openscada.opc.lib.da.AccessBase;
import org.openscada.opc.lib.da.AddFailedException;
import org.openscada.opc.lib.da.AutoReconnectController;
import org.openscada.opc.lib.da.DataCallback;
import org.openscada.opc.lib.da.DuplicateGroupException;
import org.openscada.opc.lib.da.Item;
import org.openscada.opc.lib.da.ItemState;
import org.openscada.opc.lib.da.Server;
import org.openscada.opc.lib.da.SyncAccess;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
public class opcClientSalem {
public static void main(String[] args) throws Exception {
// create connection information
System.out.println("**********Initializing OPC Client**********");
java.util.logging.Logger.getLogger("org.jinterop").setLevel(java.util.logging.Level.OFF);
final ConnectionInformation ci = new ConnectionInformation("myusername","mypassword");
ci.setHost("myhost");
ci.setDomain("");
ci.setProgId("Matrikon.OPC.Simulation.1");
ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");
String itemIdArr[] = {"Random.Real8","Random.Int2"}; // This is where I would have an array of all items
// create a new server
final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());
AutoReconnectController controller = new AutoReconnectController(server);
try {
// connect to server
System.out.println("**********Attempting to connect to OPC**********");
controller.connect();
System.out.println("**********Successfully connected to OPC**********");
// add sync access, poll every 15000 ms
final AccessBase access = new SyncAccess(server, 15000);
while(true){
for(final String str : itemIdArr){
access.addItem(str, new DataCallback() {
#Override
public void changed(Item item, ItemState state) {
// Building a JSON string with value recieved
String record = "[ {" +"\""+"name"+"\" :\""+str + "\",\""+"timestamp"+"\" :"+ state.getTimestamp().getTime().getTime()+ ",\""+"value"+"\" : "+value.replace("[", "").replace("]", "") +",\"tags\":{\"test\":\"test1\"}} ]";
try {
// Post JSON string to my API which ingests this data
new opcClientSalem().restpost(record);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
// start reading
access.bind();
Thread.sleep(5000);
}
// wait a little bit
// stop reading
//access.unbind();
} catch (final JIException e) {
//System.out.println(String.format("%08X: %s", e.getErrorCode(), server.getErrorMessage(e.getErrorCode())));
}
}
private void restpost(String record) throws ClientProtocolException, IOException{
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://localhost/myapi/datapoints");
StringEntity input = new StringEntity(record);
post.setEntity(input);
HttpResponse response = client.execute(post);
System.out.println("Post success::"+record);
}
}
I'm not sure you need to add the items over and over again in your while group.
In other libraries (.net or native c++) usually you need to add the items only once, and the callback called whenever the value of the item is changed.
In .net or c++ we get a global callback per group, which seems more effective than individual callbacks per items. Maybe SyncAccess has some global callback, look for it.
So the possible optimizations:
remove the while part, add items only once and sleep the thread infinite.
look for global callback for all items
You should create a subscription in this case.
I prepared a servlet in my web site to be notified from PayPal webhook. The development version of the servlet logs the http headers and the body. Here is a screen capture with one example:
I've created a "self contained test application" that shows the problem.
package com.rsws.renew;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.Map;
import com.paypal.api.payments.Event;
import com.paypal.base.Constants;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.PayPalRESTException;
import com.paypal.base.rest.PayPalResource;
/**
* #author Ignacio
*
*/
public class TestWebHook {
public static void main(String[] argv) {
try {
InputStream is = InvoicePaid.class
.getResourceAsStream("/sdk_config.properties");
try {
PayPalResource.initConfig(is);
} catch (PayPalRESTException e) {
e.printStackTrace();
}
APIContext apiContext = new APIContext();
Map<String, String> map = new HashMap<>(PayPalResource.getConfigurations());
apiContext.setConfigurationMap(map);
Map<String,String> headers = new HashMap<String,String>();
// this is the data provided by PayPal sandbox
map.put(Constants.PAYPAL_WEBHOOK_ID, "3W2725225F637605K");
String payload = "{\"id\":\"WH-0T490472X6099635W-7LJ29748BW389372K\",\"create_time\":\"2015-09-25T23:14:14Z\",\"resource_type\":\"invoices\",\"event_type\":\"INVOICING.INVOICE.PAID\",\"summary\":\"An invoice was created\",\"resource\":{\"id\":\"INV2-8FSD-3HT6-BRHR-UHYV\",\"number\":\"MM00063\",\"status\":\"PAID\",\"merchant_info\":{\"email\":\"example#outlook.com\",\"first_name\":\"Dennis\",\"last_name\":\"Doctor\",\"business_name\":\"Medical Professional LLC\",\"address\":{\"line1\":\"1234 Main St\",\"line2\":\"Apt 302\",\"city\":\"Portland\",\"state\":\"OR\",\"postal_code\":\"97217\",\"country_code\":\"US\"}},\"billing_info\":[{\"email\":\"example#example.com\",\"business_name\":\"Medical Professionals LLC\",\"language\":\"en_US\"}],\"items\":[{\"name\":\"Sample Item\",\"quantity\":1,\"unit_price\":{\"currency\":\"USD\",\"value\":\"1.00\"},\"unit_of_measure\":\"QUANTITY\"}],\"invoice_date\":\"2015-09-28 PDT\",\"payment_term\":{\"term_type\":\"DUE_ON_RECEIPT\",\"due_date\":\"2015-09-28 PDT\"},\"tax_calculated_after_discount\":true,\"tax_inclusive\":false,\"total_amount\":{\"currency\":\"USD\",\"value\":\"1.00\"},\"payments\":[{\"type\":\"PAYPAL\",\"transaction_id\":\"22592127VV907111U\",\"transaction_type\":\"SALE\",\"method\":\"PAYPAL\",\"date\":\"2015-09-28 14:37:13 PDT\"}],\"metadata\":{\"created_date\":\"2015-09-28 14:35:46 PDT\",\"last_updated_date\":\"2015-09-28 14:37:13 PDT\",\"first_sent_date\":\"2015-09-28 14:35:47 PDT\",\"last_sent_date\":\"2015-09-28 14:35:47 PDT\"},\"paid_amount\":{\"paypal\":{\"currency\":\"USD\",\"value\":\"1.00\"}},\"links\":[{\"rel\":\"self\",\"href\":\"https://api.paypal.com/v1/invoicing/invoices/INV2-8FSD-3HT6-BRHR-UHYV\",\"method\":\"GET\"}]},\"links\":[{\"href\":\"https://api.paypal.com/v1/notifications/webhooks-events/WH-0T490472X6099635W-7LJ29748BW389372K\",\"rel\":\"self\",\"method\":\"GET\"},{\"href\":\"https://api.paypal.com/v1/notifications/webhooks-events/WH-0T490472X6099635W-7LJ29748BW389372K/resend\",\"rel\":\"resend\",\"method\":\"POST\"}]}";
headers.put("PAYPAL-CERT-URL", "https://api.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-df8cd2d5");
headers.put("PAYPAL-TRANSMISSION-ID", "464163d0-e0ae-11e5-af72-51ae350aaff1");
headers.put("PAYPAL-TRANSMISSION-TIME", "2016-03-02T19:38:01Z");
headers.put("PAYPAL-AUTH-ALGO", "SHA256withRSA");
headers.put("PAYPAL-TRANSMISSION-SIG", "S3AjY87GLp1MP/UsGAWPoEes+laa7xbV4X7pMi9PdC0QR7MoNC/L/O2UThAh1IBzDZ5DGXvkEDvXK9fF0IfoS2QtLJUBm5+UFoo1jJMlH+QCiJUEHSuio2UrFGbxoqaIPcA1PN0tmd5FwikDRPCnpht6pvMvCZV1FEQbBMr9ld3d3XoWBKeWQG+oxAWSTNYJiKQIrM6l/8+hKVQ1LZID8dtR3c7y6eFxNFsDQ3WgwChZZ15vpyhDWQ4t08m3PsWFyjvsQmNRyXQyUeAC8xw96sBwGmHsgwKJwbAamVrWicQqQ/tXuUcqx9Y0pg3P4LuGNPFKzktq9L3ZImTEJxpRLA==");
// this shows invalid
System.out.println(Event.validateReceivedEvent(apiContext, headers, payload) ? "valid" : "invalid");
// this is the data provided in the sdk examples https://github.com/paypal/PayPal-Java-SDK/blob/master/rest-api-sdk/src/test/java/com/paypal/base/ValidateCertTest.java
map.put(Constants.PAYPAL_WEBHOOK_ID, "3RN13029J36659323");
payload = "{\"id\":\"WH-2W7266712B616591M-36507203HX6402335\",\"create_time\":\"2015-05-12T18:14:14Z\",\"resource_type\":\"sale\",\"event_type\":\"PAYMENT.SALE.COMPLETED\",\"summary\":\"Payment completed for $ 20.0 USD\",\"resource\":{\"id\":\"7DW85331GX749735N\",\"create_time\":\"2015-05-12T18:13:18Z\",\"update_time\":\"2015-05-12T18:13:36Z\",\"amount\":{\"total\":\"20.00\",\"currency\":\"USD\"},\"payment_mode\":\"INSTANT_TRANSFER\",\"state\":\"completed\",\"protection_eligibility\":\"ELIGIBLE\",\"protection_eligibility_type\":\"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE\",\"parent_payment\":\"PAY-1A142943SV880364LKVJEFPQ\",\"transaction_fee\":{\"value\":\"0.88\",\"currency\":\"USD\"},\"links\":[{\"href\":\"https://api.sandbox.paypal.com/v1/payments/sale/7DW85331GX749735N\",\"rel\":\"self\",\"method\":\"GET\"},{\"href\":\"https://api.sandbox.paypal.com/v1/payments/sale/7DW85331GX749735N/refund\",\"rel\":\"refund\",\"method\":\"POST\"},{\"href\":\"https://api.sandbox.paypal.com/v1/payments/payment/PAY-1A142943SV880364LKVJEFPQ\",\"rel\":\"parent_payment\",\"method\":\"GET\"}]},\"links\":[{\"href\":\"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2W7266712B616591M-36507203HX6402335\",\"rel\":\"self\",\"method\":\"GET\"},{\"href\":\"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2W7266712B616591M-36507203HX6402335/resend\",\"rel\":\"resend\",\"method\":\"POST\"}]}";
headers.put("PAYPAL-CERT-URL", "https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-a5cafa77");
headers.put("PAYPAL-TRANSMISSION-ID", "b2384410-f8d2-11e4-8bf3-77339302725b");
headers.put("PAYPAL-TRANSMISSION-TIME", "2015-05-12T18:14:14Z");
headers.put("PAYPAL-AUTH-ALGO", "SHA256withRSA");
headers.put("PAYPAL-TRANSMISSION-SIG", "vSOIQFIZQHv8G2vpbOpD/4fSC4/MYhdHyv+AmgJyeJQq6q5avWyHIe/zL6qO5hle192HSqKbYveLoFXGJun2od2zXN3Q45VBXwdX3woXYGaNq532flAtiYin+tQ/0pNwRDsVIufCxa3a8HskaXy+YEfXNnwCSL287esD3HgOHmuAs0mYKQdbR4e8Evk8XOOQaZzGeV7GNXXz19gzzvyHbsbHmDz5VoRl9so5OoHqvnc5RtgjZfG8KA9lXh2MTPSbtdTLQb9ikKYnOGM+FasFMxk5stJisgmxaefpO9Q1qm3rCjaJ29aAOyDNr3Q7WkeN3w4bSXtFMwyRBOF28pJg9g==");
// this shows valid
System.out.println(Event.validateReceivedEvent(apiContext, headers, payload) ? "valid" : "invalid");
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (PayPalRESTException e) {
e.printStackTrace();
}
}
}
The code shows valid when the data has been taken from examples and invalid when the data comes from paypal web site.
I wonder why this cannot be validated. Any help is welcome.
You may want to test the validation with actual sandbox transactions and webhook events. Simulator mock data may not be updated with the sandbox algorithm, and is recommended for testing URL accessibility of your script.
This code was mostly taken from Pentaho's website on sample programs. The goal is to use their API to generate HTML output with a Pentaho report.
The report was created with Pentaho 3.5.0-RC2
The version of jars I am using are the following:
pentaho-reporting-engine-classic-core-3.5.0-RC2.jar
libloader-1.1.1.jar
libbase-1.1.1.jar
The exception I am getting is the following:
org.pentaho.reporting.libraries.resourceloader.ContentNotRecognizedException: None of the selected factories was able to handle the given data: ResourceKey{schema=org.pentaho.reporting.libraries.resourceloader.loader.URLResourceLoader, identifier=file:/C:/Users/elias.kopsiaftis/workspace_experimental/TestPentahoReport/bin/Test.prpt, factoryParameters={}, parent=null}
at org.pentaho.reporting.libraries.resourceloader.DefaultResourceManagerBackend.create(DefaultResourceManagerBackend.java:295)
at org.pentaho.reporting.libraries.resourceloader.ResourceManager.create(ResourceManager.java:387)
at org.pentaho.reporting.libraries.resourceloader.ResourceManager.create(ResourceManager.java:342)
at org.pentaho.reporting.libraries.resourceloader.ResourceManager.createDirectly(ResourceManager.java:205)
at ReportTester.getReportDefinition(ReportTester.java:74)
at ReportTester.getReport(ReportTester.java:25)
at ReportTester.main(ReportTester.java:84)
java.lang.NullPointerException
at ReportTester.getReport(ReportTester.java:26)
at ReportTester.main(ReportTester.java:84)
And finally, the code snippet!
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
import java.util.HashMap;
import java.io.*;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.modules.output.table.html.HtmlReportUtil;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.base.util.StackableException;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
public class ReportTester {
private String reportName = "FormCompleteUsagePerPartnerByMonth.prpt";
private String reportPath = "";
public void getReport() {
try {
final MasterReport report = getReportDefinition();
report.getParameterValues().put("partner", "");
report.getParameterValues().put("startingdate", "2015-03-01");
report.getParameterValues().put("endingdate", "2015-03-05");
HtmlReportUtil.createStreamHTML(report, System.out);
} catch(Exception e) {
e.printStackTrace();
}
}
private MasterReport getReportDefinition() {
try {
// Using the classloader, get the URL to the reportDefinition file
final ClassLoader classloader = this.getClass().getClassLoader();
final URL reportDefinitionURL = classloader.getResource(reportPath + reportName);
if(reportDefinitionURL == null) {
System.out.println("URL was null");
}
// Parse the report file
final ResourceManager resourceManager = new ResourceManager();
resourceManager.registerDefaults();
final Resource directly = resourceManager.createDirectly(reportDefinitionURL, MasterReport.class);
return (MasterReport) directly.getResource();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
ReportTester rt = new ReportTester();
rt.getReport();
}
}
Any advice on this would be much appreciated as I have been scratching my head on this all day. Thanks in advance!
I am trying to extract the data from the table on the following website. I.e Club, venue, start time. http://www.national-autograss.co.uk/february.htm
I have got many examples on here working that use a css class table but this website doesn't. I have made an attempt with the code below but it doesn't seem to provide any output. Any help would be very much appreciated.
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class Main {
public static void main(String[] args) {
Document doc = null;
try {
doc = Jsoup.connect("http://www.national-autograss.co.uk/february.htm").get();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Elements elements = doc.select("table#table1");
String name;
for( Element element : elements )
{
name = element.text();
System.out.println(name);
}
}
}
An id should be unique, so you should use directly doc.select("#table1") and so on
I am using reflections with onejar maven plugin, and I have a problem when running the resulting jar:
20204 [main] ERROR org.reflections.Reflections - given scan urls are
empty. set urls in the configuration
It seems to me that reflections doesn't support the class loader that onejar is using. I've been using the following configuration of Reflections in my code:
Reflections reflections = new Reflections("path.to.my.package");
Any ideas? Thanks.
Yep a known issue. There is a work around...
Use Custom UrlType and custom Vfs.Dir; Uses OneJarURLConnection to correctly read content from embedded JARs; uses 'standard' Vfs.File.
Issue and solution discussed here : http://code.google.com/p/reflections/issues/detail?id=128
Source if you need it:
OneJarDir
package com.sdl.ws.integration.profserv.shared.onejar;
import com.simontuffs.onejar.OneJarURLConnection;
import org.reflections.vfs.Vfs;
import org.reflections.vfs.ZipDir;
import org.reflections.vfs.ZipFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class OneJarDir implements Vfs.Dir {
private JarFile oneJarFile = null;
private List<Vfs.File> oneJarClassFiles = new ArrayList<Vfs.File>();
private OneJarURLConnection oneJarConnection;
public OneJarDir(OneJarURLConnection oneJarConnection) {
this.oneJarConnection = oneJarConnection;
try {
this.oneJarConnection.connect();
this.oneJarFile = this.oneJarConnection.getJarFile();
Enumeration<JarEntry> entries = oneJarFile.entries();
while (entries.hasMoreElements()) {
oneJarClassFiles.add(new ZipFile(new ZipDir(oneJarFile), entries.nextElement()));
}
} catch (IOException e) {
throw new RuntimeException("Can't create One-Jar VFS directory", e);
}
}
public String getPath() {
return oneJarConnection.getURL().getPath();
}
public Iterable<Vfs.File> getFiles() {
return oneJarClassFiles;
}
public void close() {
try {
if (oneJarConnection != null)
oneJarConnection.getInputStream().close();
} catch (IOException e) {
throw new RuntimeException("Can't close VFS JAR stream", e);
}
}
}
OneJarUrlType
package com.sdl.ws.integration.profserv.shared.onejar;
import com.simontuffs.onejar.OneJarURLConnection;
import org.reflections.vfs.Vfs;
import java.io.IOException;
import java.net.URL;
public class OneJarUrlType implements Vfs.UrlType {
private static final String _JAR_DIR = "jar!";
public boolean matches(URL url) {
// check if "double-jarred' by one-jar; this would appear to conflict with the standard JAR loader, so it either needs to be first (which it is)
// OR the standard needs to be removed. This match assumes a nested JAR, unlike the standard JAR type.
String externalForm = url.toExternalForm();
// ugly, but should be much faster than regex.
int idx1 = externalForm.indexOf(_JAR_DIR);
return (idx1 > 0 && externalForm.indexOf(_JAR_DIR, idx1 + _JAR_DIR.length()) > 0);
}
public Vfs.Dir createDir(URL url) {
try {
return new OneJarDir(new OneJarURLConnection(url));
} catch (IOException e) {
throw new RuntimeException("Can't open One-Jar embedded JAR", e);
}
}
}