How to Digitally Sign a SOAP request BODY in ColdFusion? - web-services

New one for me: I need to digitally sign and encrypt the body of SOAP request from a ColdFusion client application using a provider-issued certificate. I also need to decrypt the response in order to process it.
I have searched for days an have found nothing. I have found information referencing other languages, but nothing in ColdFusion. If it is not possible to do it natively through the ColdFusion language API, can someone help me with possibly calling the correct Java or .net classes via the 'createObject()' function or any other method you may have?
17SEP2012 - ADDITIONAL INFO:
More Info: Post timed out while I was looking at code. Here is the complete post:
I am consuming a web service, not providing one. I have reached the point where I have the whole thing working, but it only works once through a ColdFusion "createObject" invokation.
Still need help. System: Windows Server 2003 with ColdFusion 9 single server installation.
I used Apache's wss4J library and wrote a simple Java class as the entry point. The custom Java class simply takes the complete SOAP message as a String argument, passes the String to the wss4j DateStamp class, then passes the resulting SOAP parts object to the signature class, then the encryption class. It then returns the signed and encrypted SOAP envelope, which has been converted from a document (SOAP parts) to a String by the PrettyDocumentToString function.
All of this works and I get a SOAP envelope with security header and signed, encrypted body. The problem is that, after restarting the ColdFusion service (single server install on Windows Server 2003), it all works one time, but subsequent runs result in an error occurring within the wss4j signature code. I have even used Winmerge to compare the entire ColdFusion9 directory immediately after restart, immediately after the 1st run, and immediately after the 2nd run. The only differences were in the log files. There were differences in the ColdFusion9\lib\neo-datasource.xml file, but only in the order of a couple of datasource descriptors, not the content.
Below is the code and stack trace:
writeOutputs and writeDumps are only for visualization during debugging.
ColdFusion calling script:
<cfscript>
variables.tempPath = getDirectoryFromPath(getCurrentTemplatePath());
variables.filePath = tempPath & "ASI_source_request_example.xml";
variables.fileContent = FileRead(filePath);
writeOutput("FILECONTENT: <br>");
writeOutput("variables.fileContent);
writeDump(var="#variables.fileContent#", format="html", output="#expandPath('./')#_DUMP-OUTPUT.htm");
variables.encSOAP=createobject("java","ProcessIDSRSSOAP").runProcess(fileContent);
writeOutput("<br><br>encSOAP: <br>");
writeOutput(variables.encSOAP);
writeDump(var="#variables.encSOAP#", format="html", output="#expandPath('./')#_DUMP-OUTPUT.htm");
</cfscript>
Java class:
import java.io.FileReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.ws.security.SOAPConstants;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSEncryptionPart;
import org.apache.ws.security.WSSConfig;
import org.apache.ws.security.common.SOAPUtil;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.CryptoFactory;
import org.apache.ws.security.message.WSSecEncrypt;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSecSignature;
import org.apache.ws.security.message.WSSecTimestamp;
import org.apache.ws.security.util.WSSecurityUtil;
import org.w3c.dom.Document;
public class ProcessIDSRSSOAP {
private static Crypto crypto = null;
private static Properties properties = new Properties();
private static String user = "";
private static String cryptoPwd = "";
private static WSSecSignature builder = new WSSecSignature();
private static SOAPConstants soapConstants = null;
private final WSSecHeader secHeader = new WSSecHeader();
private Document tsDoc = null;
private Document signedDoc = null;
private Document encryptedDoc = null;
private static final org.apache.commons.logging.Log LOG = org.apache.commons.logging.LogFactory
.getLog(ProcessIDSRSSOAP.class);
public ProcessIDSRSSOAP() throws Exception {
WSSConfig.init();
}
/*
* public static void main(String[] args) throws Exception {
* ProcessIDSRSSOAP processor = new ProcessIDSRSSOAP();
* processor.runProcess(args[0]); }
*/
public String runProcess(String inDoc) throws Exception {
// ProcessIDSRSSOAP processor = new ProcessIDSRSSOAP();
// LOG.debug(inDoc);
Class<ProcessIDSRSSOAP> thisClass = ProcessIDSRSSOAP.class;
ClassLoader thisLoader = thisClass.getClassLoader();
URL propertiesURL = thisLoader.getResource("crypto.properties");
String propertiesPath = propertiesURL.getPath();
propertiesPath = propertiesPath.replaceAll("%20", " ");
properties.load(new FileReader(propertiesPath));
user = properties
.getProperty("org.apache.ws.security.crypto.merlin.keystore.alias");
cryptoPwd = properties
.getProperty("org.apache.ws.security.crypto.merlin.keystore.password");
crypto = CryptoFactory.getInstance("crypto.properties");
builder.setUserInfo(user, cryptoPwd);
builder.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
SOAPUtil.toSOAPPart(inDoc.trim());
Document PKIDoc = processDoc(inDoc);
String PKIDocString = org.apache.ws.security.util.XMLUtils
.PrettyDocumentToString(PKIDoc);
LOG.debug(PKIDocString);
return PKIDocString;
}
/**
* #param SOAPMsg
* The entire SOAP message as a type String
* #throws Exception
*/
public Document processDoc(String SOAPMsg) throws Exception {
tsDoc = timestampMSG(SOAPMsg);// Time stamp the SOAP String and make it
// a Document type.
secHeader.insertSecurityHeader(tsDoc);// Insert the security header.
soapConstants = WSSecurityUtil.getSOAPConstants(tsDoc
.getDocumentElement());
signedDoc = signBody(tsDoc);// Send the Document on for signing.
encryptedDoc = encryptBody(signedDoc);
return encryptedDoc;
}
/**
* #param msg
* The entire SOAP message as a type String
* #throws Exception
*/
public Document timestampMSG(String msg) throws Exception {
Document doc = SOAPUtil.toSOAPPart(msg);
WSSecHeader secHeader = new WSSecHeader();
secHeader.insertSecurityHeader(doc);
WSSecTimestamp timestamp = new WSSecTimestamp();
timestamp.setTimeToLive(300);
Document createdDoc = timestamp.build(doc, secHeader);
return createdDoc;
}
/**
* #param doc
* Expects a SOAP message as a type Document
* #throws Exception
*/
public Document signBody(Document doc) throws Exception {
List<WSEncryptionPart> parts = new ArrayList<WSEncryptionPart>();
WSEncryptionPart encP = new WSEncryptionPart(soapConstants
.getBodyQName().getLocalPart(), soapConstants.getEnvelopeURI(),
"");
parts.add(encP);
builder.setParts(parts);
Document signedDoc = builder.build(doc, crypto, secHeader);
return signedDoc;
}
public Document encryptBody(Document doc) throws Exception {
SOAPConstants soapConstants = WSSecurityUtil.getSOAPConstants(doc
.getDocumentElement());
WSSecEncrypt encrypt = new WSSecEncrypt();
encrypt.setUserInfo(user, cryptoPwd);
encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
WSSecHeader secHeader = new WSSecHeader();
secHeader.insertSecurityHeader(doc);
List<WSEncryptionPart> parts = new ArrayList<WSEncryptionPart>();
WSEncryptionPart encP = new WSEncryptionPart(soapConstants
.getBodyQName().getLocalPart(), // define the body
soapConstants.getEnvelopeURI(), "");
parts.add(encP);
encrypt.setParts(parts);
Document encryptedDoc = encrypt.build(doc, crypto, secHeader);
return encryptedDoc;
}
}
ColdFusion Error:
Signature creation failed (Cannot setup signature data structure)
The error occurred in G:/Inetpub/wwwroot/SOAP/index.cfm: line 14
12 : writeDump(var="#variables.fileContent#", format="html", output="#expandPath('./')#_DUMP-OUTPUT.htm");
13 :
14 : variables.encSOAP=createobject("java","ProcessIDSRSSOAP").runProcess(fileContent);
15 :
16 : writeOutput("<br><br>encSOAP: <br>");
Stack Trace:
at cfindex2ecfm1134068877.runPage(G:/Inetpub/wwwroot/SOAP/index.cfm:14)
org.apache.ws.security.WSSecurityException: Signature creation failed (Cannot setup signature data structure)
at org.apache.ws.security.message.WSSecSignatureBase.addReferencesToSign(WSSecSignatureBase.java:191)
at org.apache.ws.security.message.WSSecSignature.addReferencesToSign(WSSecSignature.java:409)
at org.apache.ws.security.message.WSSecSignature.build(WSSecSignature.java:381)
at ProcessIDSRSSOAP.signBody(ProcessIDSRSSOAP.java:118)
at ProcessIDSRSSOAP.processDoc(ProcessIDSRSSOAP.java:85)
at ProcessIDSRSSOAP.runProcess(ProcessIDSRSSOAP.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at coldfusion.runtime.java.JavaProxy.invoke(JavaProxy.java:97)
at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2360)
at cfindex2ecfm1134068877.runPage(G:\Inetpub\wwwroot\SOAP\index.cfm:14)
at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:231)
at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:416)
at coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:65)
at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:381)
at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:48)
at coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40)
at coldfusion.filter.PathFilter.invoke(PathFilter.java:94)
at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:70)
at coldfusion.filter.BrowserDebugFilter.invoke(BrowserDebugFilter.java:79)
at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28)
at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38)
at coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:46)
at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38)
at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)
at coldfusion.filter.CachingFilter.invoke(CachingFilter.java:62)
at coldfusion.CfmServlet.service(CfmServlet.java:200)
at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89)
at jrun.servlet.FilterChain.doFilter(FilterChain.java:86)
at coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42)
at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46)
at jrun.servlet.FilterChain.doFilter(FilterChain.java:94)
at jrun.servlet.FilterChain.service(FilterChain.java:101)
at jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:106)
at jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42)
at jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:286)
at jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:543)
at jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:203)
at jrunx.scheduler.ThreadPool$DownstreamMetrics.invokeRunnable(ThreadPool.java:320)
at jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:428)
at jrunx.scheduler.ThreadPool$UpstreamMetrics.invokeRunnable(ThreadPool.java:266)
at jrunx.scheduler.WorkerThread.run(WorkerThread.java:66)
Caused by: java.lang.NullPointerException
at org.apache.ws.security.message.DOMCallbackLookup.getElements(DOMCallbackLookup.java:94)
at org.apache.ws.security.util.WSSecurityUtil.findElements(WSSecurityUtil.java:267)
at org.apache.ws.security.message.WSSecSignatureBase.addReferencesToSign(WSSecSignatureBase.java:156)
... 43 more
Obviously, something is missing from org.apache.ws.security.message.DOMCallbackLookup.getElements.
The code feeding it is:
return callbackLookup.getElements(part.getName(),part.getNamespace());
I can't seem to figure out why it works the first time when called from CF, but fails with this error on subsequent runs.

The actual issue was that I had my function calls reversed. I reversed the order of the block
signedDoc = signBody(tsDoc);// Send the Document on for signing.
encryptedDoc = encryptBody(signedDoc);
to
encryptedDoc = encryptBody(signedDoc);
signedDoc = signBody(tsDoc);// Send the Document on for signing.
and it worked.

You would need to add a handler, have a look here as I believe this is similar to what you are trying to achieve: http://milanchandnacf.blogspot.co.uk/2011/09/adding-handler-to-coldfusion-web.html

Related

Fastest way to ingest data from BigQuery to PubSub

At the moment I am going through the GCP docs trying to figure out what is the optimal/fastest way to ingest data from BigQuery (using Python) to PubSub. What I am doing so far (in a simplified way) is:
MESSAGE_SIZE_IN_BYTES = 500
MAX_BATCH_MESSAGES = 20
MAX_BYTES_BATCH = MESSAGE_SIZE_IN_BYTES * MAX_BATCH_MESSAGES
BATCH_MAX_LATENCY_IN_10MS = 0.01
MAX_FLOW_MESSAGES = 20
MAX_FLOW_BYTES = MESSAGE_SIZE_IN_BYTES * MAX_FLOW_MESSAGES
batch_settings = pubsub_v1.types.BatchSettings(
max_messages=MAX_BATCH_MESSAGES,
max_bytes=MAX_BYTES_BATCH,
max_latency=BATCH_MAX_LATENCY_IN_10MS,
)
publisher_options = pubsub_v1.types.PublisherOptions(
flow_control=pubsub_v1.types.PublishFlowControl(
message_limit=MAX_FLOW_MESSAGES,
byte_limit=MAX_FLOW_BYTES,
limit_exceeded_behavior=pubsub_v1.types.LimitExceededBehavior.BLOCK,
),
)
pubsub_client = pubsub_v1.PublisherClient(credentials=credentials,
batch_settings=self.batch_settings,
publisher_options=self.publisher_options)
bigquery_client = ....
bq_query_job = bigquery_client.query(QUERY)
rows = bq_query_job.result()
for row in rows:
callback_obj = PubsubCallback(...)
json_data = json.dumps(row).encode("utf-8")
publish_future = pubsub_client.publish(topic_path, json_data)
publish_future.add_done_callback(callback_obj.callback)
publish_futures.append(publish_future)
so one message per row. I have being trying to tweak different params for the PubSub publisher client etc, but I cannot get further than 20/30 messages(rows) per second. Is there a way to read from BigQuery using Pubsub in a faster way (at least 1000 times faster than now)?
We also have a need to get data from BigQuery into PubSub and we do so using Dataflow. I've just looked at one of the jobs we ran today and we loaded 3.4million rows in about 5 minutes (so ~11000 rows per second).
Our Dataflow jobs are written in java but you could write them in python if you wish. Here is the code for the pipeline I described above:
package com.ourcompany.pipelines;
import com.google.api.services.bigquery.model.TableRow;
import java.util.HashMap;
import java.util.Map;
import org.apache.beam.sdk.Pipeline;
import org.apache.beam.sdk.PipelineResult;
import org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO;
import org.apache.beam.sdk.io.gcp.pubsub.PubsubIO;
import org.apache.beam.sdk.io.gcp.pubsub.PubsubMessage;
import org.apache.beam.sdk.options.Description;
import org.apache.beam.sdk.options.PipelineOptions;
import org.apache.beam.sdk.options.PipelineOptionsFactory;
import org.apache.beam.sdk.options.Validation.Required;
import org.apache.beam.sdk.options.ValueProvider;
import org.apache.beam.sdk.transforms.DoFn;
import org.apache.beam.sdk.transforms.ParDo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {#code BigQueryEventReplayer} pipeline runs a supplied SQL query
* against BigQuery, and sends the results one-by-one to PubSub
* The query MUST return a column named 'json', it is this column
* (and ONLY this column) that will be sent onward. The column must be a String type
* and should be valid JSON.
*/
public class BigQueryEventReplayer {
private static final Logger logger = LoggerFactory.getLogger(BigQueryEventReplayer.class);
/**
* Options for the BigQueryEventReplayer. See descriptions for more info
*/
public interface Options extends PipelineOptions {
#Description("SQL query to be run."
+ "An SQL string literal which will be run 'as is'")
#Required
ValueProvider<String> getBigQuerySql();
void setBigQuerySql(ValueProvider<String> value);
#Description("The name of the topic which data should be published to. "
+ "The name should be in the format of projects/<project-id>/topics/<topic-name>.")
#Required
ValueProvider<String> getOutputTopic();
void setOutputTopic(ValueProvider<String> value);
#Description("The ID of the BigQuery dataset targeted by the event")
#Required
ValueProvider<String> getBigQueryTargetDataset();
void setBigQueryTargetDataset(ValueProvider<String> value);
#Description("The ID of the BigQuery table targeted by the event")
#Required
ValueProvider<String> getBigQueryTargetTable();
void setBigQueryTargetTable(ValueProvider<String> value);
#Description("The SourceSystem attribute of the event")
#Required
ValueProvider<String> getSourceSystem();
void setSourceSystem(ValueProvider<String> value);
}
/**
* Takes the data from the TableRow and prepares it for the PubSub, including
* adding attributes to ensure the payload is routed correctly.
*/
public static class MapQueryToPubsub extends DoFn<TableRow, PubsubMessage> {
private final ValueProvider<String> targetDataset;
private final ValueProvider<String> targetTable;
private final ValueProvider<String> sourceSystem;
MapQueryToPubsub(
ValueProvider<String> targetDataset,
ValueProvider<String> targetTable,
ValueProvider<String> sourceSystem) {
this.targetDataset = targetDataset;
this.targetTable = targetTable;
this.sourceSystem = sourceSystem;
}
/**
* Entry point of DoFn for Dataflow.
*/
#ProcessElement
public void processElement(ProcessContext c) {
TableRow row = c.element();
if (!row.containsKey("json")) {
logger.warn("table does not contain column named 'json'");
}
Map<String, String> attributes = new HashMap<>();
attributes.put("sourceSystem", sourceSystem.get());
attributes.put("targetDataset", targetDataset.get());
attributes.put("targetTable", targetTable.get());
String json = (String) row.get("json");
c.output(new PubsubMessage(json.getBytes(), attributes));
}
}
/**
* Run the pipeline. This is the entrypoint for running 'locally'
*/
public static void main(String[] args) {
// Parse the user options passed from the command-line
Options options = PipelineOptionsFactory.fromArgs(args).withValidation().as(Options.class);
run(options);
}
/**
* Run the pipeline. This is the entrypoint that GCP will use
*/
public static PipelineResult run(Options options) {
Pipeline pipeline = Pipeline.create(options);
pipeline.apply("Read from BigQuery query",
BigQueryIO.readTableRows().fromQuery(options.getBigQuerySql()).usingStandardSql().withoutValidation()
.withTemplateCompatibility())
.apply("Map data to PubsubMessage",
ParDo.of(
new MapQueryToPubsub(
options.getBigQueryTargetDataset(),
options.getBigQueryTargetTable(),
options.getSourceSystem()
)
)
)
.apply("Write message to PubSub", PubsubIO.writeMessages().to(options.getOutputTopic()));
return pipeline.run();
}
}
This pipeline requires that each row retrieved from BigQuery is a JSON document, something that can easily be achieved using TO_JSON_STRING.
I know this might look rather daunting to some (it kinda does to me I admit) but it will get you the throughput that you require!
You can ignore this part:
Map<String, String> attributes = new HashMap<>();
attributes.put("sourceSystem", sourceSystem.get());
attributes.put("targetDataset", targetDataset.get());
attributes.put("targetTable", targetTable.get());
that's just some extra attributes we add to the pubsub message purely for our own use.
Use Pub/Sub Batch Messages. This allows your code to batch multiple messages into a single call to the Pub/Sub service.
Example code from Google (link):
from concurrent import futures
from google.cloud import pubsub_v1
# TODO(developer)
# project_id = "your-project-id"
# topic_id = "your-topic-id"
# Configure the batch to publish as soon as there are 10 messages
# or 1 KiB of data, or 1 second has passed.
batch_settings = pubsub_v1.types.BatchSettings(
max_messages=10, # default 100
max_bytes=1024, # default 1 MB
max_latency=1, # default 10 ms
)
publisher = pubsub_v1.PublisherClient(batch_settings)
topic_path = publisher.topic_path(project_id, topic_id)
publish_futures = []
# Resolve the publish future in a separate thread.
def callback(future: pubsub_v1.publisher.futures.Future) -> None:
message_id = future.result()
print(message_id)
for n in range(1, 10):
data_str = f"Message number {n}"
# Data must be a bytestring
data = data_str.encode("utf-8")
publish_future = publisher.publish(topic_path, data)
# Non-blocking. Allow the publisher client to batch multiple messages.
publish_future.add_done_callback(callback)
publish_futures.append(publish_future)
futures.wait(publish_futures, return_when=futures.ALL_COMPLETED)
print(f"Published messages with batch settings to {topic_path}.")

Unload a dll loaded by a classloader in Java11

I had a scenario where I need to perform database connectivity using Java11 twice using Windows Authentication on SQL server.
Initially, the sqljdbc_auth.dll is loaded for the first call and the connection was successful. But, in order to make the connection second time at a different place, it throws an SQLException saying 'sqljdbc_auth.dll already loaded by another classloader'.
So, I need to unload the dll in between the two calls.
There is an option to do the same till Java8 version by invoking the finalize() on the classloader using reflection mechanism but unable to find an alternative in Java11
Sample code:
Here, I placed the sqljdbc_auth.dll in the PATH and jar named sql_jdbc.jar in the urls list and these are compatible with Java11
private static void loadFile(){
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();;
ClassLoader loader = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); // here, urls is an array and contains only a single sql_jdbc.jar path in it
Thread.currentThread().setContextClassLoader(loader);
Driver driver = (Driver)loader.loadClass("com.microsoft.sqlserver.jdbc.SQLServerDriver").newInstance();
Connection connection = driver.connect("jdbc:sqlserver://IP-addr:1433;DatabaseName=db_name;SelectMethod=cursor;integratedSecurity=true", props);
//perform db actions here
Thread.currentThread().setContextClassLoader(currentClassLoader);
unloadDLL("sqljdbc_auth.dll",loader);
}
private synchronized static void unloadDllFile(String dllName, ClassLoader classLoader) throws Throwable {
try {
Field field = ClassLoader.class.getDeclaredField("nativeLibraries");
field.setAccessible(true);
Map<Object, Object> lib = (ConcurrentHashMap<Object, Object>) field.get(classLoader);
Set<Object> keyset = lib.keySet();
for (Object dllpath : keyset) {
if (dllpath.toString().contains(dllName)) {
Object o = lib.get(dllpath);
classLoader = null;
field = null;
lib.remove(dllpath);
keyset.remove(dllpath);
o = null;
System.gc();
}
}
} catch (Exception e) {
System.out.println("Exception in dll is "+e.getMessage());
}
}
There is a similar code in the second component but it throws an exception there.
Exception stacktrace while loading in the second component is:
com.microsoft.sqlserver.jdbc.SQLServerException: This driver is not configured for integrated authentication. ClientConnectionId:d19de7a1-d099-477c-9c18-0c4cd5807f5e
at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:2892)
at com.microsoft.sqlserver.jdbc.AuthenticationJNI.<init>(AuthenticationJNI.java:72)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:3636)
at com.microsoft.sqlserver.jdbc.SQLServerConnection$LogonCommand.doExecute(SQLServerConnection.java:3627)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7194)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2935)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:2456)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:2103)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectInternal(SQLServerConnection.java:1950)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:1162)
at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:735)
at sample.java_samples.Sample2.loadFile(Sample2.java:66)
at sample.java_samples.Sample2.main(Sample2.java:23)
Caused by: java.lang.UnsatisfiedLinkError: Native Library C:\Windows\System32\sqljdbc_auth.dll already loaded in another classloader
at java.base/java.lang.ClassLoader$NativeLibrary.loadLibrary(ClassLoader.java:2456)
at java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2684)
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2649)
at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:829)
at java.base/java.lang.System.loadLibrary(System.java:1867)
at com.microsoft.sqlserver.jdbc.AuthenticationJNI.<clinit>(AuthenticationJNI.java:52)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:3635)
... 10 more
Thanks

How to checkout and checkin any document outside alfresco using rest API?

I have created one Web Application using Servlets and JSP. Through that I have connected to alfresco repository. I am also able be to upload document in Alfresco and view document in external web application.
Now my requirement is, I have to give checkin and checkout option to those documents.
I found below rest apis for this purpuse.
But I am not getting how to use these apis in servlets to full-fill my requirment.
POST /alfresco/service/slingshot/doclib/action/cancel-checkout/site/{site}/{container}/{path}
POST /alfresco/service/slingshot/doclib/action/cancel-checkout/node/{store_type}/{store_id}/{id}
Can anyone please provide the simple steps or some piece of code to do this task?
Thanks in advance.
Please do not use the internal slingshot URLs for this. Instead, use OpenCMIS from Apache Chemistry. It will save you a lot of time and headaches and it is more portable to other repositories besides Alfresco.
The example below grabs an existing document by path, performs a checkout, then checks in a new major version of the plain text document.
package com.someco.cmis.examples;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.client.api.ObjectId;
import org.apache.chemistry.opencmis.client.api.Repository;
import org.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.client.api.SessionFactory;
import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
import org.apache.chemistry.opencmis.commons.SessionParameter;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.enums.BindingType;
public class CheckoutCheckinExample {
private String serviceUrl = "http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/atom"; // Uncomment for Atom Pub binding
private Session session = null;
public static void main(String[] args) {
CheckoutCheckinExample cce = new CheckoutCheckinExample();
cce.doExample();
}
public void doExample() {
Document doc = (Document) getSession().getObjectByPath("/test/test-plain-1.txt");
String fileName = doc.getName();
ObjectId pwcId = doc.checkOut(); // Checkout the document
Document pwc = (Document) getSession().getObject(pwcId); // Get the working copy
// Set up an updated content stream
String docText = "This is a new major version.";
byte[] content = docText.getBytes();
InputStream stream = new ByteArrayInputStream(content);
ContentStream contentStream = session.getObjectFactory().createContentStream(fileName, Long.valueOf(content.length), "text/plain", stream);
// Check in the working copy as a major version with a comment
ObjectId updatedId = pwc.checkIn(true, null, contentStream, "My new version comment");
doc = (Document) getSession().getObject(updatedId);
System.out.println("Doc is now version: " + doc.getProperty("cmis:versionLabel").getValueAsString());
}
public Session getSession() {
if (session == null) {
// default factory implementation
SessionFactory factory = SessionFactoryImpl.newInstance();
Map<String, String> parameter = new HashMap<String, String>();
// user credentials
parameter.put(SessionParameter.USER, "admin"); // <-- Replace
parameter.put(SessionParameter.PASSWORD, "admin"); // <-- Replace
// connection settings
parameter.put(SessionParameter.ATOMPUB_URL, this.serviceUrl); // Uncomment for Atom Pub binding
parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value()); // Uncomment for Atom Pub binding
List<Repository> repositories = factory.getRepositories(parameter);
this.session = repositories.get(0).createSession();
}
return this.session;
}
}
Note that on the version of Alfresco I tested with (5.1.e) the document must already have the versionable aspect applied for the version label to get incremented, otherwise the checkin will simply override the original.

Add SOAP header object using pure JAX-WS

I'm trying to implement simple web service client for PayPal Express Checkout API using JAX WS. PayPal Express Checkout API provides WSDL file, from which I was able to generate Java classes using CXF's wsdl2java utility.
From authentication reasons, it demands adding SOAP Header to each request. This header is quite simple and should look like here:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ECSOAPAPIBasics#id09C3I0CF0O6
Generated from WSDL classes include ebay.apis.eblbasecomponents.CustomSecurityHeaderType class which represents header which I need to add to each request.
So the question is: how can I add manually created instance of CustomSecurityHeaderType class to SOAP request's header taking into account following conditions:
I'm not very eager to use classes from com.sun.* package as mentioned in answer here: JAX-WS - Adding SOAP Headers (mainly because of possible portability issues between different JDK's)
I don't want to manually marshal that object into nested javax.xml.soap.SOAPElement instances as mentioned in answer here:
How do I add a SOAP Header using Java JAX-WS
So, it looks like I've found possible answer while combining JAX-WS & JAXB related answers from SO (I would really appreciate if somebody experienced in these technologies can check whether following is correct):
The obvious thing for me is to add SOAP message handler and alter header of SOAPMessage instance in it:
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.Handler;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.soap.SOAPHeader;
import ebay.api.paypalapi.ObjectFactory; // class generated by wsdl2java
// following class is generated by wsdl2java utility Service class
final PayPalAPIInterfaceService payPalService = new PayPalAPIInterfaceService();
final PayPalAPIAAInterface expressCheckoutPort = payPalService.getPayPalAPIAA();
final Binding binding = ((BindingProvider) expressCheckoutPort).getBinding();
List<Handler> handlersList = new ArrayList<Handler>();
// now, adding instance of Handler to handlersList which should do our job:
// creating header instance
final CustomSecurityHeaderType headerObj = new CustomSecurityHeaderType();
final UserIdPasswordType credentials = new UserIdPasswordType();
credentials.setUsername("username");
credentials.setPassword("password");
credentials.setSignature("signature");
headerObj.setCredentials(credentials);
// bookmark #1 - please read explanation after code
final ObjectFactory objectFactory = new ObjectFactory();
// creating JAXBElement from headerObj
final JAXBElement<CustomSecurityHeaderType> requesterCredentials = objectFactory.createRequesterCredentials(headerObj);
handlersList.add(new SOAPHandler<SOAPMessageContext>() {
#Override
public boolean handleMessage(final SOAPMessageContext context) {
try {
// checking whether handled message is outbound one as per Martin Strauss answer
final Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound");
if (outbound != null && outbound) {
// obtaining marshaller which should marshal instance to xml
final Marshaller marshaller = JAXBContext.newInstance(CustomSecurityHeaderType.class).createMarshaller();
// adding header because otherwise it's null
final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader();
// marshalling instance (appending) to SOAP header's xml node
marshaller.marshal(requesterCredentials, soapHeader);
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
return true;
}
// ... default implementations of other methods go here
});
// as per Jean-Bernard Pellerin's comment setting handlerChain list here, after all handlers were added to list
binding.setHandlerChain(handlersList);
Explanation of bookmark #1:
one should marshal not the header object itself, but JAXBElement representing that object, because otherwise one will get an exception. One should use one of ObjectFactory classes which are generated from WSDL for creating needed JAXBElement instances from original objects.
(Thanks #skaffman for answer: No #XmlRootElement generated by JAXB )
One should also refer to Martin Straus answer which extends this one
This solution works great, but there's a catch. It generates this error when the inbound message is processed:
dic 19, 2012 7:00:55 PM com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl addHeader
SEVERE: SAAJ0120: no se puede agregar una cabecera si ya hay una
Exception in thread "main" javax.xml.ws.WebServiceException: java.lang.RuntimeException: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
at com.sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.java:167)
at com.sun.xml.ws.handler.HandlerTube.processResponse(HandlerTube.java:174)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1074)
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:979)
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:950)
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:825)
at com.sun.xml.ws.client.Stub.process(Stub.java:443)
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:174)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:102)
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:154)
at $Proxy38.wsRdyCrearTicketDA(Unknown Source)
at ar.com.fit.fides.remedy.api.ws.ServicioCreacionTickets.crearTicket(ServicioCreacionTickets.java:55)
at ar.com.fit.fides.remedy.api.ws.ConectorRemedyWS.crearTicket(ConectorRemedyWS.java:43)
at ar.com.fit.fides.remedy.api.ws.ConectorRemedyWS.main(ConectorRemedyWS.java:90)
Caused by: java.lang.RuntimeException: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:50)
at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:23)
at com.sun.xml.ws.handler.HandlerProcessor.callHandleMessageReverse(HandlerProcessor.java:341)
at com.sun.xml.ws.handler.HandlerProcessor.callHandlersResponse(HandlerProcessor.java:214)
at com.sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.java:161)
... 14 more
Caused by: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
at com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl.addHeader(EnvelopeImpl.java:128)
at com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl.addHeader(EnvelopeImpl.java:108)
at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:45)
So, the solution is to check whether the message being handled if the outbound message, like this:
public boolean handleMessage(SOAPMessageContext context) {
try {
Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound");
if (outbound != null && outbound) {
// obtaining marshaller which should marshal instance to xml
final Marshaller marshaller = JAXBContext.newInstance(AuthenticationInfo.class).createMarshaller();
// adding header because otherwise it's null
final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader();
// marshalling instance (appending) to SOAP header's xml node
marshaller.marshal(info, soapHeader);
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
return true;
}
I created a web service exposing method with params user and password as header like this:
#WebService(serviceName="authentication")
public class WSAuthentication {
String name = null;
String password = null;
public WSAuthentication() {
super();
}
public WSAuthentication(String name, String password) {
this.name = name;
this.password = password;
}
private static String getData(WSAuthentication sec) {
System.out.println("********************* AUTHENTICATION ********************" + "\n" +
"**********USER: " + sec.name + "\n" +
"******PASSWORD: " + sec.password + "\n" +
"******************************** AUTHENTICATION ****************************");
return sec.name + " -- " + sec.password;
}
#WebMethod(operationName="security", action="authenticate")
#WebResult(name="answer")
public String security(#WebParam(header=true, mode=Mode.IN, name="user") String user, #WebParam(header=true, mode=Mode.IN, name="password") String password) {
WSAuthentication secure = new WSAuthentication(user, password);
return getData(secure);
}
}
Try compiling it and testing generated from WSDL class. I hope this helps.
I found this answer:
JAX-WS - Adding SOAP Headers
Basically you add -XadditionalHeaders to the compiler options and objects in the headers also appear in your generated code as parameters of the method.
If you are using maven, and the jaxws-maven-plugin all you have to do is add the xadditionalHeaders flag to true and the client will be generated with the methods that have the headers as input.
https://jax-ws-commons.java.net/jaxws-maven-plugin/wsimport-mojo.html#xadditionalHeaders

Testing Solr via Embedded Server

I'm coding some tests for my solr-indexer application. Following testing best practices, I want to write code self-dependant, just loading the schema.xml and solrconfig.xml and creating a temporary data tree for the indexing-searching tests.
As the application is most written in java, I'm dealing with SolrJ library, but I'm getting problems (well, I'm lost in the universe of corecontainers-coredescriptor-coreconfig-solrcore ...)
Anyone can place here some code to create an Embedded Server that loads the config and also writes to a parameter-pased data-dir?
You can start with the SolrExampleTests which extends SolrExampleTestBase which extends AbstractSolrTestCase .
Also this SampleTest.
Also take a look at this and this threads.
This is an example for a simple test case. solr is the directory that contains your solr configuration files:
import java.io.IOException;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.util.AbstractSolrTestCase;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.SolrParams;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class SolrSearchConfigTest extends AbstractSolrTestCase {
private SolrServer server;
#Override
public String getSchemaFile() {
return "solr/conf/schema.xml";
}
#Override
public String getSolrConfigFile() {
return "solr/conf/solrconfig.xml";
}
#Before
#Override
public void setUp() throws Exception {
super.setUp();
server = new EmbeddedSolrServer(h.getCoreContainer(), h.getCore().getName());
}
#Test
public void testThatNoResultsAreReturned() throws SolrServerException {
SolrParams params = new SolrQuery("text that is not found");
QueryResponse response = server.query(params);
assertEquals(0L, response.getResults().getNumFound());
}
#Test
public void testThatDocumentIsFound() throws SolrServerException, IOException {
SolrInputDocument document = new SolrInputDocument();
document.addField("id", "1");
document.addField("name", "my name");
server.add(document);
server.commit();
SolrParams params = new SolrQuery("name");
QueryResponse response = server.query(params);
assertEquals(1L, response.getResults().getNumFound());
assertEquals("1", response.getResults().get(0).get("id"));
}
}
See this blogpost for more info:Solr Integration Tests
First you need to set your Solr Home Directory which contains solr.xml and conf folder containing solrconfig.xml, schema.xml etc.
After that you can use this simple and basic code for Solrj.
File solrHome = new File("Your/Solr/Home/Dir/");
File configFile = new File(solrHome, "solr.xml");
CoreContainer coreContainer = new CoreContainer(solrHome.toString(), configFile);
SolrServer solrServer = new EmbeddedSolrServer(coreContainer, "Your-Core-Name-in-solr.xml");
SolrQuery query = new SolrQuery("Your Solr Query");
QueryResponse rsp = solrServer.query(query);
SolrDocumentList docs = rsp.getResults();
Iterator<SolrDocument> i = docs.iterator();
while (i.hasNext()) {
System.out.println(i.next().toString());
}
I hope this helps.