Applies to:
SAP NetWeaver CE 7.2 (SP 03) / CE 7.1 Ehp 1 (SP 03), JDK 1.6/ JDK 1.5, XML, JAXB.
Summary:
In many custom business application we are using CAF Business objects their might be requirement of archive data of CAF Business Objects for auditing and future purposes. Here I am discussing step by step configuration of NetWeaver Scheduler and corresponding custom java coding.
Author(s): Biplab Ray
Company: Tata Consultancy Services
Created on: 22 January 2015
Author Bio:
Biplab Ray is working for Tata Consultancy Service as Assistant Consultant and development of SAP EP , composite applications using CAF, BPM, BRM, WebDynpro for Java. He also works in technologies like Java/J2EE and has depth understanding on eSOA, Webservice, Enterprise Service, XML.
Introduction
In custom composite application for data storing, normally we are using CAF Business Objects. But we required archiving them after certain time of period for auditing or future purposes. For this requirement I have made a custom application via which we can easily archive our CAF BO data and restore them when required to CAF BO. The data archive application will be triggered via NetWeaver Scheduler. The mechanism I have used to do the custom application as follows: a) read the desired CAF BO and generate one xml file with help of JAXB api into the Hard disk of the server and delete the content of the CAF BO b ) then using java util zip api to create one zip file and put the xml file into it and delete the xml file from the hard disk of the server c) and store the name of the CAF BO with actual path where the zip file stored into server into another CAF BO. d) When restore mechanism will triggered from the zip file read the content and data will be back to the actual CAF BO and entry will be delete from the History CAF BO.
Create Projects
For this custom application we required one CAF project. With the name: example/ce/archive
Implementation
CAF Project
To do the CAF project please follow the below instruction.
Steps | Screen Shots |
---|---|
Step 1: Choose File -> New - > Project of your Netweaver Developer Studio of CAF perspective. | ![]() |
Step 2: One popup window come and goes to the Development Infrastructure - > Development Component and press next button in the below. | ![]() |
Step 3: Then one popup will be come and choose Development Component type: Composite Application. And press next button. | ![]() |
Step 4: Then choose the software component type where you are going to create your development component. | ![]() |
Step 5: Then another new popup will be come and provide the details as per requirement. | ![]() |
Step 6: The press next button and then finish button. It will create a CAF project into your workspace. | ![]() |
Step 7: Now our project is ready. | ![]() |
Step 8 : Right click on the modeled package | ![]() |
Step 9: Now we are going to create a CAF BO with name BOToArchive. | ![]() |
Step 10: The go to the Permission tab of CAF BO and unchecked the Permission check Enabled option | ![]() |
Step 11: Then go to the structure tab of CAF BO and press the button Edit Main Structure | ![]() |
Step 12: The create two fields with name a)Id data type String cardinality 0 -1 b) Value data type xLongText cardinality 0 -1 | ![]() |
Step 13: We have to create another CAF BO for storing archive path and CAF BO names. Provide the new CAF BO name : History and create two fields a)Id data type string b)Path data type xLongText | ![]() |
Step 14: Now we have to create one Application service with name : DataArchiveApp | ![]() |
Step 15: Then one popup will be come and provide the name of the application service. Then press finish button. | ![]() |
Step 16: Then it will generate the application service and check the Generate remote Interface option. Under general tab of application service. | ![]() |
Step 17: Now we have to create one custom structure for input parameter of delete_ZIP method. Structure Name : BODeleteStruct | ![]() |
Step 18: After you create the custom structure it should look like as screenshot. Cardinality ; 0 - n | ![]() |
Step 19: Now we have to create custom method to our application service. To do those go to the operations tab of our application service. Then press the Add button one popup will be come and choose the first radio button with option Custom operation and then provide the name of operation.
| |
Step 20: Now we are adding two CAF BO objects to our Application service via dependency tab of application service. | ![]() |
Step 21: Now we have to create few general java class for archive our data. Please go to the Java perspective and create name : JAXB_Data_Structure with package : com.tcs.jaxb.data.structure | ![]() |
Step 22: Now we have to create another java class name : JaxbUtil with lot of methods with package : com.tcs.jaxb.util | package com.tcs.jaxb.util;
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import java.util.Calendar; import java.util.Random; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream;
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException;
/** *@author * */ publicclass JaxbUtil {
/** *Thismethodcreatethezipfile andreturntheAbsolutefilepath * *@paramstringsFileName- * PasstheFileNametobeZIP *@return-Astringobjectwithabsolutepath */ publicstatic String createZIP (String stringsFileName,String stringBOName) {
String zipFileName = ""; zipFileName = createDirectory() + "/" + stringBOName+"-"+createId() + ".zip";
try { File file = new File(zipFileName); ZipOutputStream out = new ZipOutputStream( new FileOutputStream(file)); out.putNextEntry (new ZipEntry(stringsFileName));
BufferedReader bufferedReader = new BufferedReader (new FileReader(stringsFileName)); String string; while ((string = bufferedReader.readLine()) != null) { out.write(string.getBytes()); } bufferedReader.close(); out.closeEntry(); out.flush(); out.close(); zipFileName = file.getAbsolutePath(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
return zipFileName; }
/** *ThisMethodCreateUniqueID * *@return-AStringobjectwiththeID */ publicstatic String createId() { return Long.toHexString (Calendar.getInstance().getTimeInMillis()) + "-" + Integer.toHexString ((new Random()).nextInt() % 65536); }
/** *ThismethodReadtheZIPFileand returnsContentoftheFileinaString *object * *@paramstringFileName- * PasstheFilenametoberead *@return-ContentofthefileinaStringobject */ publicstatic String readZipFile(String stringFileName) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ZipFile zipFile = new ZipFile(new File(stringFileName));
ZipInputStream zipInputStream = new ZipInputStream( new FileInputStream(stringFileName));
ZipEntry zipEntry = zipInputStream.getNextEntry();
BufferedReader reader = new BufferedReader(new FileReader( stringFileName)); byte[] bs = reader.readLine().getBytes(); reader.close();
zipInputStream.closeEntry(); zipInputStream.close();
InputStream inputStream = zipFile.getInputStream(zipEntry);
int len; while ((len = inputStream.read(bs)) > 0) {
byteArrayOutputStream .write(bs, 0, len); } inputStream.close(); byteArrayOutputStream.close();
} catch (ZipException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return byteArrayOutputStream.toString();
}
/** *ThismethodDeletethepassedfile. * *@paramstringFileName- * Passthefilepathtobedelete */ publicstaticvoid deleteFile(String stringFileName) { File file = new File(stringFileName); if (file.exists() && file.isFile()) { file.delete(); } }
/** *ThismethodcreateaNewFile andwritecontentstotheNewlyCreated *file * *@paramstringFileContent- * Passthecontentofthefiletobewritten *@return-Astringobjectwith AbsoluteFileDirectory */ publicstatic String writeToHD(String stringFileContent) { String string = null; try { string = createDirectory() + "/" + createId() + ".xml"; File file = new File(string);
if (!file.exists()) { file.createNewFile(); } BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter( file)); bufferedWriter.write(stringFileContent); bufferedWriter.close(); string = file.getAbsolutePath(); } catch (IOException e) { // TODO Auto-generated catch block
} return string; }
/** *ThisMethodconvertsJavaObject toXMLTreeandReturntoStringObject * *@paramobject- * forJavaObjectDataStructure(WithData) *@paramclass1- * forJavaClass(DataStructureClassInstance) *@return-AStringObjectWithXMLFormat */ publicstatic String jaxbMarshalConvertJavaObjectToXMLTree(Object object, Class<?> class1) { StringWriter writerPage = new StringWriter(); try { JAXBContext jaxbContextPage = JAXBContext.newInstance(class1); jaxbContextPage .createMarshaller().marshal(object, writerPage); } catch (JAXBException e) { // TODO Auto-generated catch block e.printStackTrace(); } return writerPage.toString(); } /** *ThismethodconvertsXMLTreeObjecttoJavaObject. * *@paramstringReadPage- * PassDataStreamlikeaStringobject *@paramclass1- * PassTheDataStructureClassInstance *@return-ObjectofJavaDataStructure, youhavetoCastwhereyou * callingthismethodwithJavaDataStructure */ public static Object jaxbUNMarshalConvertXMLTreeToJavaObject( String stringReadPage, Class<?> class1) { Object dataStructureReadPage = null; try { JAXBContext jaxbContextPage = JAXBContext.newInstance(class1); dataStructureReadPage = (Object) jaxbContextPage .createUnmarshaller().unmarshal( new StringReader(stringReadPage)); } catch (JAXBException e) { // TODO Auto-generated catch block e.printStackTrace(); } return dataStructureReadPage; }
/** *ThismethodcreateaDirectoryintheHD inthedefineddirectorye.g: *"/usr/sap/"+SystemName+"/XML_DAS/" * *@return-AStringObjectwithdetailsof AbsolutePathofDirectory */ public static String createDirectory() { File file = null;
String sysNam = System.getProperty("SAPSYSTEMNAME"); String stringDirPathName = "/usr/sap/" + sysNam + "/" + "XML_DAS" + "/"; if (stringDirPathName != null && !"".equals(stringDirPathName)) { file = new File(stringDirPathName); if (!file.mkdirs()) { file.mkdirs(); } } if (null != file.getAbsolutePath()) { stringDirPathName = file.getAbsolutePath(); } return stringDirPathName; } } |
Step 23: Now we have to add DC reference to use SAP NetWeaver Scheduler mechanism into our project to do that open the project into component properties and go to the dependency tab and press the add button one popup will be come and chose ENGFACADE[sap.com] and from this tree select tc/je/scheduler/api and press next button and select deploy time and build time options. And press the finish button. | ![]() |
Step 24: Now it will come as screenshot. | ![]() |
Step 25: Now we have to create Message driven bean for scheduler. Create a message driven bean of name : Example_Shcedule with supper class MDBJobImplementation with destination name : MyDestinations and then press next button and then press the finish button without changing any default configuatraion. Package : com.tcs.nw.scheduler | ![]() |
Step 26: Now we have to access the Application service due to that we have to use EJB annotation. | package com.tcs.nw.scheduler;
import javax.ejb.ActivationConfigProperty; import javax.ejb.EJB; import javax.ejb.MessageDriven; import javax.jms.MessageListener;
import com.tcs.example.ce.archive.modeled .appsrv.dataarchiveapp. DataArchiveAppServiceLocal; import com.sap.scheduler. runtime.JobContext; import com.sap.scheduler. runtime.mdb.MDBJobImplementation;
/** *Message-DrivenBean implementationclassfor:Example_Shedule * */ @MessageDriven( activationConfig = {@ActivationConfigProperty( propertyName="messageSelector", propertyValue="JobDefinition='Example_JobBean'"), @ActivationConfigProperty( propertyName = "destinationType", propertyValue = "javax.jms.Queue" ) }, mappedName = "MyDestination") publicclass Example_Shedule extends MDBJobImplementation implements MessageListener {
privatestaticfinallongserialVersionUID = 4485561670312885280L;
@EJB(name="com.tcs.example.ce.archive.modeled. appsrv.dataarchiveapp.DataArchiveApp") DataArchiveAppServiceLocal dataArchiveAppServiceLocal; /** *@seeMDBJobImplementation #MDBJobImplementation() */ public Example_Shedule() { super(); // TODO Auto-generated constructor stub }
@Override publicvoid onJob(JobContext arg0) throws Exception { // TODO Auto-generated method stub dataArchiveAppServiceLocal.dataArchive(); }
} |
Step 27: Now we have to create job-defination.xml file into META-INF folder of ejb module of CAF project for searching the job from NetWeaver Administrator. | ![]() |
Step 28: Now we have to make few entries into ejb-j2ee-engine.xml under META-INF of ejb module of CAF project | ![]() |
Step 29 : Now we have to create few entries into application-j2ee-engine.xml file under META-INF folder of ear project of CAF for NetWeaver Scheduler | ![]() |
Step 30: Now we have to write our Application service implementation methods. Below are the methods we are going to implement.
| import java.util.ArrayList; import java.util.Calendar; import java.util.List;
import com.tcs.example.ce.archive.modeled.BOToArchive; import com.tcs.example.ce.archive.modeled.History; import com.tcs.example.ce.archive.modeled.bonode .botoarchive.botoarchive.BOToArchiveServiceLocal; import com.tcs.example.ce.archive.modeled .bonode.history.history.HistoryServiceLocal; import com.tcs.jaxb.data.structure.JAXB_Data_Structure; import com.tcs.jaxb.util.JaxbUtil; import com.sap.caf.rt.exception.CAFCreateException; import com.sap.caf.rt.exception.CAFDeleteException; import com.sap.caf.rt.exception.CAFFindException; import com.sap.caf.rt.exception.CAFLockException; import com.sap.caf.rt.exception.CAFUpdateException; ------------------------------------------------------------------------------------------------------------- public void dataArchive() { JAXB_Data_Structure data_Structure = new JAXB_Data_Structure(); List<BOToArchive> list_BOToArchive_OUT = new ArrayList<BOToArchive>();
BOToArchiveServiceLocal archiveServiceLocal = this.getBOToArchiveService(); try { List<BOToArchive> list_BOToArchive = archiveServiceLocal.findAll(); if(list_BOToArchive != null && list_BOToArchive.size() > 0){ for (BOToArchive toArchive : list_BOToArchive) { list_BOToArchive_OUT.add(toArchive); archiveServiceLocal.delete(toArchive); } } } catch (CAFFindException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CAFDeleteException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CAFLockException e) { // TODO Auto-generated catch block e.printStackTrace(); }
Calendar calendar = Calendar.getInstance(); // Set the Data to JAXB Data Structure data_Structure. setList_BOToArchive(list_BOToArchive_OUT); // Convert Java Object to XML Tree String string_JavaObjectToXMLTree = JaxbUtil. jaxbMarshalConvertJavaObjectToXMLTree (data_Structure, JAXB_Data_Structure.class); // Write Content To HD String string_ZIP_FILE_NAME_IN_HD = JaxbUtil.writeToHD(string_JavaObjectToXMLTree); //Create ZIP File String string_ZIP_FILE_NAME = JaxbUtil.createZIP (string_ZIP_FILE_NAME_IN_HD, "BOToArchive-" +String.valueOf(calendar.getTime())); //Delete The HD File JaxbUtil.deleteFile(string_ZIP_FILE_NAME_IN_HD);
// Store Actual BO & Path Name in HIstroty BO HistoryServiceLocal historyServiceLocal = this.getHistoryService(); try { History history = historyServiceLocal.create(); history.setId(("BOToArchive"+"-"+ String.valueOf(calendar.getTime())+"-"+JaxbUtil.createId())); history.setPath(string_ZIP_FILE_NAME); historyServiceLocal.update(history); } catch (CAFCreateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CAFUpdateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CAFLockException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
------------------------------------------------------------------------------------------------------ public void restoreDATTOBO(java.lang.String nameofTheArchiveBO, java.lang.String merge_Indicator) {
HistoryServiceLocal historyServiceLocal = this.getHistoryService(); BOToArchiveServiceLocal archiveServiceLocal = this.getBOToArchiveService();
try {
List<History> list_History = historyServiceLocal.findAll(); if(merge_Indicator != null && !"".equals(merge_Indicator) && merge_Indicator.equalsIgnoreCase("Y")){ this.dataArchive(); } for (History history : list_History) { if(nameofTheArchiveBO !=null && !"".equals(nameofTheArchiveBO) && nameofTheArchiveBO.equalsIgnoreCase(history.getId())){ //Convert XML Tree to Java Object String string_ZipFile = JaxbUtil.readZipFile(history.getPath()); JAXB_Data_Structure object_JAXB_Data_Structure = (JAXB_Data_Structure)JaxbUtil .jaxbUNMarshalConvertXMLTreeToJavaObject(string_ZipFile, JAXB_Data_Structure.class); List<BOToArchive> list_BOToArchive = object_JAXB_Data_Structure.getList_BOToArchive(); for (BOToArchive toArchive : list_BOToArchive) { BOToArchive archive = archiveServiceLocal.create(); archiveServiceLocal.update(archive); toArchive.setCreatedAt(archive.getCreatedAt()); toArchive.setCreatedBy(archive.getCreatedBy()); toArchive.setKey(archive.getKey()); toArchive.setModifiedAt(archive.getModifiedAt()); toArchive.setModifiedBy(archive.getModifiedBy()); archiveServiceLocal.update(toArchive); } historyServiceLocal.delete(history); break; }
} } catch (CAFFindException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CAFCreateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CAFUpdateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CAFLockException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CAFDeleteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } -------------------------------------------------------------------------------------------------------- public void deleteZIP(com.tcs.example.ce. archive.modeled.BODeleteStruct delete) {
HistoryServiceLocal historyServiceLocal = this.getHistoryService();
if(delete != null){ List<String> list_String = delete.getPaths(); try { List<History> list_History = historyServiceLocal.findAll(); if(list_String != null && list_String.size() > 0){ for (String string : list_String) { for (History history : list_History) { if(history.getId().equalsIgnoreCase(string)){ //Delete The File JaxbUtil.deleteFile(history.getPath()); historyServiceLocal.delete(history); } } } } } catch (CAFFindException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CAFDeleteException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CAFLockException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
Step 31: Now we have to deploy our project to SAP Application server.
| ![]() |
Step 32: After deployment we have to go CAF service browser to insert few data to our BO. BO : BOToArchive | ![]() |
Step 33: Now we have to configure our scheduler to archive our BO data in specified time. To do that we have to log on to the SAP WAS Application server. Under NWA | ![]() |
Step 34: Then select Example_MDB under Job Definitions Tab | ![]() |
Step 35: Then we have to create a new task for our job. Please go to the Tasks tab and press the button Add. Then select your Job. | Then press the Next Button and provide the data as below Then press Next button. Then press Next button. And provide the start time of your scheduler. Then press Add button. And then press finish button. It will go to the Tasks tab. And you will see your task in the task list as below. And press the refresh button on the above screen if your task is started then it will go to the under Jobs tab. |
Step 36: Our scheduler completed its task. Now we have to go to the CAF service browser to see whether our Data is archived or not from BO: BOToArchive, it should be blank at this time and our another CAF BO : History will contain with single entry. | Below Screen Shoot of History BO
|
Step 37: If we want restore our data from the archive file to CAF BO we have to execute the custom method: restore_DAT_TO_BO of our application service. | Please go the Web Service Navigator of your SAP WAS Application server. And find your web service. Then press Next button and select the specified method to execute. Now we have to provide the credentials the name of BO to restore with merge indicator Y or N. If Y then the current data of the BO will merge with old data of the BO and entry from the History Bo will be deleted. If N then if the BO if some data is their first archive the data and restore the old data if no data is their then it will load the old data and entry form the History BO will be deleted.
Please collect the BO name from the History BO the Id filed value. Then press Next button. Now to check whether the data will restore or not go to the CAF service browser and double click the BOToArchive CAF BO. Now if we go to the History BO it will be empty. |
Note : | To get the SAP WAS Application server name via code you have to writhe below code. String sysNam = System.getProperty("SAPSYSTEMNAME"); |
Useful Links: | http://jaxb.java.net |