In this blog I will talking about my findings of using Oracle HCM Cloud as storage. You can actually checkin/checkout your files here. There are various reasons you may want to store your files there. Few could be
1. FBL
2. HDL
3. You Paas Application needs to store some file (Although I will prefer storage cloud but then you might need a separate license. Why not to use UCM of Oracle HCM Cloud itself).
Before coming to actual implementation, I would like to clear few things.
1. FBL
2. HDL
3. You Paas Application needs to store some file (Although I will prefer storage cloud but then you might need a separate license. Why not to use UCM of Oracle HCM Cloud itself).
Before coming to actual implementation, I would like to clear few things.
1. If saas url is https://hcm-abc.oracleoutsourcing.com/homePage/faces/AtkHomePageWelcome, What will be content-server url
Ans:
https://fs-abc.oracleoutsourcing.com/cs/idcplg
(Same SaaS username/password will work)
2. As we are going to use RIDC apis we need to understand supported protocols:
Intradoc (with SSL also): This is a
direct connection between client and server. Server must be configured to
accept request from selected clients. You must be setting IntradocServerPort
and SocketHostAddressSecurityFilter in WCC_domain
/ucm/cs/config/config.cfg file or (Web UI
hos:port/cs/idplg >> Administration >> Admin Server >>
General Configuration
As
this connection is done over trusted network, it does not need any password
authentication. Only provide username and do transaction.
IdcContext userContext = new
IdcContext("HCM_ADMIN"); //No need of password.
Intradoc communication can also be done over SSL. You should do below
line of coding for that.
// create the manager
IdcClientManager manager = new
IdcClientManager();
// build a client that will communicate
using the intradoc protocol
IdcClient idcClient =
manager.createClient("idc://localhost:4444");
// set the SSL socket options
IntradocClientConfig config = (IntradocClientConfig)
idcClient.getConfig();
config.setKeystoreFile("ketstore/client_keystore"); //location of keystore file
config.setKeystorePassword
("password"); // keystore
password
config.setKeystoreAlias("SecureClient"); //keystore alias
config.setKeystoreAliasPassword("password"); //password for keystore alias
In
case of SaaS, intradoc may not be useful as we can’t configure
SocketHostAddressSecurityFilter
property of UCM server.
http/https: RIDC communicates with the web server for Oracle Content Server using the Apache HttpClient package. Unlike Intradoc, this protocol requires authentication credentials for each request. It does not require any configuration on server side.
If you want to use http/https, you need to use url as
// create the manager
IdcClientManager manager = new IdcClientManager();
IdcClient idcClient = manager.createClient("https://host.oracleoutsourcing.com/_dav/cs/idcplg");
//NOTE: we have used _dav in url. Otherwise you will receive //oracle.stellent.ridc.protocol.http.HttpProtocolException: Http status: //HTTP/1.1 302 Moved Temporarily //ERROR
//If you are using https, you need to set truststores as
System.setProperty("javax.net.ssl.trustStore", "D:/jks/Cert_DER.jks");
System.setProperty("javax.net.ssl.trustStorePassword","mypassword");
//For usercontext you should set AuthScheme as Basic
IdcContext userContext = new IdcContext("username", "password");
userContext.setAuthScheme(IdcContext.HttpAuthScheme.BasicAuth);
JAX-WS: The JAX-WS protocol is supported only in Oracle WebCenter Content 11g with Oracle Content Server running in Oracle WebLogic Server. To provide JAX-WS support, several additional JAR files are required.
NOW COMING TO THE MAIN PART: Steps to import a file on Oracle HCM Cloud (UCM) server
1. No special configuration is needed on server side
2. Get url to make connection. It should be https://host.oracleoutsourcing.com/_dav/cs/idcplg
Replace host with your environment and don’t forget _dav
3. Get username/password who has access to UCM. You can use your SaaS username/password to login to https://host.oracleoutsourcing.com/cs
If you are able to login successfully then check if you can upload/download documents etc. If you can do it through UI, you should be able to do it programmatically.
4. Create keystore and install certificate. (THIS STEP IS IMPORTANT and ERROR PRON)
- Launch https://host.oracleoutsourcing.com/cs in IE
- Install certificate in browserLock-Icon-in-url >> View Certificate >> Install Certificate
- Export certificate from browser Tools >> Internet Options >> Content >> Certificate >> Other People >> Select Certificate >> Export
- Import downloaded certificate in a keystore
If you do not have a keystore already and you want to create new keystore altogether.
- NOTE: In case of weblogic web program you may want to get existing keystore and then add new certificate instead of creating new keystore.
- keytool -import -file D:\jks\Cert_DER.cer -alias orakey -keypass mypassword -keystore D:\jks\Cert_DER.jks -storepass mypassword
This command will cerate a keystore (jks file) with a key (named orakey). Password for key and keystore is same as ‘mypassword’. Certificate is imported in this keystore.
If you already have keystore and you want to import certificate in it
- keytool -importcert -file D:\jks\Cert_DER.cer –keystore D:\oldjks\Cert_DER.jks -alias orakey -storepass <whatever-is-keystore-password>
If you want to list keystore data you can
- keytool -list -v -keystore D:\jks\Cert_DER.jks -storepass mypassword
Following above keystore steps your keystore should be ready with appropriate certificate.
Now we need to write a java code with RIDC apis and configure it to use jks file we just created.
NOTE: If you are using weblogic server then you should configure weblogic keystore instead of referring jks from code.
5. Java Code: Let say you have placed your jks as D:/jks/Cert_DER.jks and file that you want to import at D:/file/test.txt
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import oracle.stellent.ridc.IdcClient;
import oracle.stellent.ridc.IdcClientException;
import oracle.stellent.ridc.IdcClientManager;
import oracle.stellent.ridc.IdcContext;
import oracle.stellent.ridc.model.DataBinder;
import oracle.stellent.ridc.model.TransferFile;
import oracle.stellent.ridc.protocol.ServiceResponse;
public class RIDCUtil {
public static void main(String[] arg) throws Exception {
try {
IdcClientManager m_clientManager = new IdcClientManager();
IdcClient idcClient = m_clientManager.createClient("https://host.oracleoutsourcing.com/_dav/cs/idcplg");
IdcContext userContext = new IdcContext("username", "pwd");
userContext.setAuthScheme(IdcContext.HttpAuthScheme.BasicAuth);
System.setProperty("javax.net.ssl.trustStore", "D:/jks/Cert_DER.jks");
System.setProperty("javax.net.ssl.trustStorePassword","mypassword");
checkin(idcClient, userContext, "D:/file/test.txt", // Replace with fully qualified path to source file
"Document", // content type
"Test Document to check RIDC", // doc title
userContext.getUser(), // author
"FAFusionImportExport", // security group
"hcm$/dataloader$/import$", // account
"TEST_DOC_FOR_RIDC") // dDocName - this is the ContentId
;
} catch (IdcClientException e) {
e.printStackTrace();
}
}
/** * Method description * *
* @param idcClient *
* @param userContext *
* @param sourceFileFQP fully qualified path to source content *
* @param contentType content type *
* @param dDocTitle doc title *
* @param dDocAuthor author *
* @param dSecurityGroup security group *
* @param dDocAccount account *
* @param dDocName dDocName * *
* @throws IdcClientException */
public static void checkin(IdcClient idcClient, IdcContext userContext,
String sourceFileFQP, String contentType,
String dDocTitle, String dDocAuthor,
String dSecurityGroup, String dDocAccount,
String dDocName) throws IdcClientException {
InputStream is = null;
try {
String fileName = sourceFileFQP.substring(sourceFileFQP.lastIndexOf('/') + 1);
is = new FileInputStream(sourceFileFQP);
long fileLength = new File(sourceFileFQP).length();
TransferFile primaryFile = new TransferFile();
primaryFile.setInputStream(is);
primaryFile.setContentType(contentType);
primaryFile.setFileName(fileName);
primaryFile.setContentLength(fileLength); // note!!! when using HTTP protocol (not intradoc/jaxws) - one must explicitly // set the Content Length when supplying an InputStream to the transfer file // e.g.
primaryFile.setContentLength(fileLength); // otherwise, a 0-byte file results on the server
DataBinder request = idcClient.createBinder();
request.putLocal("IdcService", "CHECKIN_UNIVERSAL");
request.addFile("primaryFile", primaryFile);
request.putLocal("dDocTitle", dDocTitle);
request.putLocal("dDocAuthor", dDocAuthor);
request.putLocal("dDocType", contentType);
request.putLocal("dSecurityGroup", dSecurityGroup);
// if server is setup to use accounts - an account MUST be specified
// // even if it is the empty string; supplying null results in Content server
// // attempting to apply an account named "null" to the content!
request.putLocal("dDocAccount", dDocAccount == null ? "" : dDocAccount);
if (dDocName != null && dDocName.trim().length() > 0) {
request.putLocal("dDocName", dDocName);
}
// execute the request
ServiceResponse response = idcClient.sendRequest(userContext, request); // throws IdcClientException
// get the binder - get a binder closes the response automatically
DataBinder responseBinder = response.getResponseAsBinder(); // throws IdcClientException
} catch (IOException e) {
e.printStackTrace(System.out);
} finally {
if (is != null) {
try { is.close();
} catch (IOException ignore) {
}
}
}
}
}
If you are making your RIDC call using a web program deployed on weblogic server, you should not set javax.net.ssl.trustStore and javax.net.ssl.trustStorePassword property in java code but you should configure jks file in weblogic.
6. Above code can be improved in many way. You may
want to checkin file in a particular folder, you may want to see if file
already present, You may want to handle exception gracefully, you may want to
take all hardcoded strings (ucm-url, ucm-username/password,
keystore-location/password, content-type, security-group, account etc.) to a
property file. Here it is just a POC.
7. To run java program from command prompt, generate
a jar file containing RIDCUtil (say MyIntegrationJar.jar). Place all required
jars in one directory and run below command from that directory.
C:\jdk1.6.0_31_64\bin\java
-classpath
.\MyIntegrationJar.jar;.\oracle.ucm.ridc-11.1.1.jar;.\commons-httpclient-3.1.jar;.\commons-logging-1.0.4.jar;.\commons-codec-1.2.jar
RIDCUtil
8. You can verify document in UCM by login into UCM https://host.oracleoutsourcing.com/cs and searching the document
Below are the various issue that you may face while implementing it. Most of them are actually certificate related
Issues | Solutions |
Caused by: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
at java.security.cert.PKIXParameters.setTrustAnchors(PKIXParameters.java:183)
at java.security.cert.PKIXParameters.
at java.security.cert.PKIXBuilderParameters.
at sun.security.validator.PKIXValidator.
|
jks file cannot be located. Check if you have given correct path for jks file.NOTE: On windows you should use / instead of \\. |
Caused by: java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: com.sun.net.ssl.internal.ssl.DefaultSSLContextImpl)
at javax.net.ssl.DefaultSSLSocketFactory.throwException(SSLSocketFactory.java:179)
at javax.net.ssl.DefaultSSLSocketFactory.createSocket(SSLSocketFactory.java:212)
Caused by: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: com.sun.net.ssl.internal.ssl.DefaultSSLContextImpl)
at java.security.Provider$Service.newInstance(Provider.java:1245)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:220)
Caused by: java.io.IOException: Keystore was tampered with, or password was incorrect
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:771)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:38)
at java.security.KeyStore.load(KeyStore.java:1185)
Caused by: java.security.UnrecoverableKeyException: Password verification failed
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:769)
... 30 more
| keystore or key password is wrong. I generally keep keystore and key password same and provide that. |
oracle.stellent.ridc.protocol.http.HttpProtocolException: Http status: HTTP/1.1 302 Moved Temporarily
at oracle.stellent.ridc.protocol.http.IdcHttpProtocol.writeRequest(IdcHttpProtocol.java:407)
at oracle.stellent.ridc.IdcClient.sendRequest(IdcClient.java:179)
| Use _dav in url. By default cs/idcplg url is for web access and it redirects user to login page. If you add _dav in url, then you can pass username/password with request and they will be used to login. |
oracle.stellent.ridc.protocol.ProtocolException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at oracle.stellent.ridc.protocol.http.IdcHttpProtocol.sendRequest(IdcHttpProtocol.java:661)
at oracle.stellent.ridc.protocol.http.IdcHttpProtocol.sendRequest(IdcHttpProtocol.java:623)
at oracle.stellent.ridc.protocol.http.auth.BasicAuthHandler.sendAuthenticatedRequest(BasicAuthHandler.java:91)
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1731)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1206)
…..
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:323)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:217)
at sun.security.validator.Validator.validate(Validator.java:218)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1185)
... 31 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:318)
... 37 more
| It means, Java has located keystore but using that keystore its not able to make handshake. There could be multiple reasons for that
|
oracle.stellent.ridc.protocol.ServiceException: Content item 'TEST_DOC_FOR_RIDC' was not successfully checked in. The content item is not currently checked out.
at oracle.stellent.ridc.protocol.ServiceResponse.getResponseAsBinder(ServiceResponse.java:142)
at oracle.stellent.ridc.protocol.ServiceResponse.getResponseAsBinder(ServiceResponse.java:108)
| It means content is already present in server and it must be checkedout before check in. Ideally you should search content first using GET_SEARCH_RESULTS service and dIsCheckedOut metadata. If content is not checked out you should check-out first and then check-in. |
org.apache.commons.httpclient.auth.AuthChallengeProcessor selectAuthScheme
INFO: basic authentication scheme selected org.apache.commons.httpclient.HttpMethodDirector processWWWAuthChallenge INFO: No credentials available for BASIC 'UCM WebDAV Server'@host.oracleoutsourcing.com:443 | These are just warnings but its because you missed to set authentication scheme userContext.setAuthScheme(IdcContext.HttpAuthScheme.BasicAuth); |