1 package net.sf.sapjcosupport;
2
3 import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
4 import com.sap.mw.jco.IFunctionTemplate;
5 import com.sap.mw.jco.JCO;
6 import org.apache.commons.beanutils.BeanUtils;
7 import org.apache.log4j.Logger;
8 import org.springframework.beans.factory.InitializingBean;
9 import org.springframework.dao.DataRetrievalFailureException;
10 import org.springframework.util.MethodInvoker;
11
12 import java.lang.reflect.Constructor;
13 import java.lang.reflect.Field;
14 import java.lang.reflect.InvocationTargetException;
15 import java.util.*;
16
17 /**
18 * Abstract superclass for all SAP data access objects.
19 *
20 * @author Niki Driessen
21 * @since Jan 18, 2006 - 3:07:41 PM
22 */
23 public abstract class SapJcoInterface {
24 private static final Logger logger = Logger.getLogger(SapJcoInterface.class);
25 private static final Logger sapJcoXmlLogger = Logger.getLogger("SapJcoXML");
26 private static SimpleReflectionCache reflectionCache = new SimpleReflectionCache();
27 private static Map cache;
28 private static final String FUNCTIONTEMPLATE_CACHE_KEY = "_functiontemplate";
29 private static final char SELECTED = 'X';
30
31 static {
32 cache = Collections.synchronizedMap(new HashMap());
33 }
34
35 protected SapDataSource dataSource;
36 protected int maxParallelCalls = 5;
37
38 /**
39 * Sets the maximum number of parallel calls to SAP.
40 *
41 * @param calls maximum number of parallel SAP calls
42 */
43 public void setMaxParallelCalls(int calls) {
44 this.maxParallelCalls = calls;
45 }
46
47 /**
48 * Sets the SAP data source this data access object is working on.
49 *
50 * @param source SAP data source
51 */
52 public void setDataSource(SapDataSource source) {
53 this.dataSource = source;
54 }
55
56 /**
57 * This method will resolve the FunctionTemplate for the given name.
58 * It will search the local cache and if not found, retrieve it from SAP
59 * via the repository. It then caches that and returns it.
60 * <p/>
61 * This method NEVER returns null! An exception is thrown when the function can not be resolved.
62 *
63 * @param name the name of the function to find
64 * @return the function template
65 * @throws SapJcoMappingException when the requested function can not be found in the configured repositories
66 */
67 protected JCO.Function getFunction(String name) {
68 String cacheKey = name + FUNCTIONTEMPLATE_CACHE_KEY;
69 IFunctionTemplate template = (IFunctionTemplate) cache.get(cacheKey);
70 if (template == null) {
71 template = dataSource.getRepository().getFunctionTemplate(name);
72 if (template == null) {
73 throw new SapJcoMappingException(new StringBuffer("Could not find function '").append(name).append(
74 "'").toString());
75 }
76 cache.put(cacheKey, template);
77 }
78 return template.getFunction();
79 }
80
81 private class AsyncRetrieveCall extends AsyncMethodCall {
82 private Class persistentObject;
83 private Map input;
84 private Object result;
85 private String profile;
86
87 public AsyncRetrieveCall(Class persistentObject, Map input, String profile) {
88 this.persistentObject = persistentObject;
89 this.input = input;
90 this.profile = profile;
91 }
92
93 protected void executeMethod() {
94 result = retrieve(persistentObject, input, profile);
95 }
96
97 protected Object getMethodReturnValue() {
98 return result;
99 }
100 }
101
102 /**
103 * Actually calls a BAPI.
104 */
105 public Object performSapCall(SapMapping sapMapping, Map input, Class persistentObject, String profile) {
106 List inputListMappings = sapMapping.getInputLists();
107 SapListMapping outputListMapping = sapMapping.getOutputList();
108 SapListMapping fieldSelection = sapMapping.getFieldSelection();
109
110 SapBapiMapping sapBapiMapping = sapMapping.getBapi();
111 JCO.Function function = getFunction(sapBapiMapping.getName());
112 JCO.Client connection = dataSource.getConnection();
113
114 if (logger.isInfoEnabled()) {
115 logger.info("Starting SAP backend call: " + sapBapiMapping.getName());
116 }
117
118
119 prepareFunctionInput(sapBapiMapping, input, function);
120
121 prepareFunctionInputList(inputListMappings, function, input);
122
123 handleFieldSelection(fieldSelection, function, sapBapiMapping, profile);
124
125 try {
126
127 long currTime = System.currentTimeMillis();
128 if (sapJcoXmlLogger.isDebugEnabled()) {
129 String tempFile = System.getProperty("java.io.tmpdir") + function
130 .getName() + "_Request-" + currTime + ".xml";
131 function.writeXML(tempFile);
132 sapJcoXmlLogger.debug("BAPI function input written to location: " + tempFile);
133 }
134 connection.execute(function);
135 if (sapJcoXmlLogger.isDebugEnabled()) {
136 String tempFile = System.getProperty("java.io.tmpdir") + function
137 .getName() + "_Response-" + currTime + ".xml";
138 function.writeXML(tempFile);
139 sapJcoXmlLogger.debug("BAPI function output written to location: " + tempFile);
140 }
141 try {
142 checkReturnStructure(function);
143 } catch (DataRetrievalFailureException e) {
144 logger.error("The backend call returned no data.", e);
145 return null;
146 }
147 JCO.ParameterList output = function.getExportParameterList();
148 if (output == null) {
149 output = function.getTableParameterList();
150 if (output == null) {
151 throw new DataRetrievalFailureException("No results returned");
152 }
153 }
154
155 if (outputListMapping != null) {
156 return parseOutputList(function, outputListMapping, sapBapiMapping, fieldSelection, persistentObject);
157 } else {
158
159 return parseOutput(sapBapiMapping, fieldSelection, function, persistentObject);
160 }
161 } catch (JCO.Exception e) {
162 throw new DataRetrievalFailureException("Underlying JCO bridge threw an exception", e);
163 } catch (IllegalAccessException e) {
164 throw new DataRetrievalFailureException("Instantiation of new value object instance failed", e);
165 } catch (InstantiationException e) {
166 throw new DataRetrievalFailureException("Instantiation of new value object instance failed", e);
167 } finally {
168 dataSource.release(connection);
169 }
170 }
171
172 private Object parseOutput(SapBapiMapping sapBapiMapping, SapListMapping fieldSelection, JCO.Function function,
173 Class persistentObject) throws IllegalAccessException, InstantiationException {
174 Object entity = persistentObject.newInstance();
175 for (int j = 0; j < sapBapiMapping.getStructures().size(); j++) {
176 SapStructureMapping sapStructureMapping = (SapStructureMapping) sapBapiMapping.getStructures().get(j);
177 if (sapStructureMapping.isExcluded() || (fieldSelection != null && sapStructureMapping.getName().equals(
178 fieldSelection.getName()))) {
179
180 continue;
181 }
182 JCO.Field field = getField(function, sapStructureMapping.getName());
183 if (field == null) {
184 throw new DataRetrievalFailureException(new StringBuffer("Field ").append(sapStructureMapping.getName())
185 .append(" not found in resultset").toString());
186 }
187 if (sapStructureMapping.isMap()) {
188 if (!field.isTable()) {
189 throw new SapJcoMappingException(new StringBuffer("Field ").append(sapStructureMapping.getName())
190 .append(" is mapped as table but ist one").toString());
191 }
192 JCO.Table table = field.getTable();
193 if (table.getNumRows() > 0) {
194 convertMap(table, entity, sapStructureMapping, persistentObject);
195 } else {
196 if (logger.isDebugEnabled()) {
197 logger.debug("Single result table contains no rows: " + table.getName());
198 }
199 }
200 } else if (sapStructureMapping.isEntity()) {
201 if (!field.isTable()) {
202 throw new SapJcoMappingException(new StringBuffer("Field ").append(sapStructureMapping.getName())
203 .append(" is mapped as table but ist one").toString());
204 }
205 JCO.Table table = field.getTable();
206 if (table.getNumRows() > 0) {
207 if (sapStructureMapping.getCollectionName() == null) {
208 throw new SapJcoMappingException(new StringBuffer("Entity mapping defined for structure ")
209 .append(sapStructureMapping.getName()).append(
210 " but no collection specified in parent entity").toString());
211 }
212 List collection = new ArrayList(table.getNumRows());
213 try {
214 Class entityClass = Class.forName(sapStructureMapping.getEntityClass());
215 table.firstRow();
216 do {
217 Object childEntity = instantiate(entityClass);
218 mapRecordToEntity(entityClass, childEntity, table, sapStructureMapping);
219 collection.add(childEntity);
220 } while (table.nextRow());
221 BeanUtils.setProperty(entity, sapStructureMapping.getCollectionName(), collection);
222 } catch (InvocationTargetException e) {
223 throw new SapJcoMappingException(
224 "Error mapping entity collection " + sapStructureMapping.getCollectionName(), e);
225 } catch (ClassNotFoundException e) {
226 throw new SapJcoMappingException(
227 "Can not find entity class " + sapStructureMapping.getEntityClass(), e);
228 }
229 } else {
230 logger.info("No rows for entity collection " + sapStructureMapping.getName());
231 }
232 } else {
233 JCO.Record record;
234 if (!field.isStructure()) {
235 field.getTable().firstRow();
236 record = field.getTable();
237 } else {
238 record = field.getStructure();
239 }
240 mapRecordToEntity(persistentObject, entity, record, sapStructureMapping);
241 }
242 }
243 if (entity != null && entity instanceof InitializingBean) {
244 try {
245 ((InitializingBean) entity).afterPropertiesSet();
246 } catch (Exception e) {
247 throw new DataRetrievalFailureException(
248 "Encountered problems while executing 'afterPropertiesSet' on value bean", e);
249 }
250 }
251 return entity;
252 }
253
254 private List parseOutputList(JCO.Function function, SapListMapping outputListMapping,
255 SapBapiMapping sapBapiMapping, SapListMapping fieldSelection, Class persistentObject)
256 throws IllegalAccessException, InstantiationException {
257 JCO.Field outputField = getField(function, outputListMapping.getName());
258 JCO.Table outputList = checkAndGetTable(outputField);
259 outputList.firstRow();
260 List results = new ArrayList();
261 do {
262 Object entity = persistentObject.newInstance();
263 String pkField = outputListMapping.getPrimaryKey();
264 if (outputList.getNumRows() == 0) {
265 return results;
266 }
267 String pkValue = outputList.getString(pkField);
268 if (pkValue == null) {
269 continue;
270 }
271 for (int j = 0; j < sapBapiMapping.getStructures().size(); j++) {
272 SapStructureMapping sapStructureMapping = (SapStructureMapping) sapBapiMapping.getStructures().get(j);
273 if (sapStructureMapping.isExcluded() || (fieldSelection != null && sapStructureMapping.getName().equals(
274 fieldSelection.getName()))) {
275
276 continue;
277 }
278 JCO.Field field = getField(function, sapStructureMapping.getName());
279 if (field == null) {
280 throw new DataRetrievalFailureException(new StringBuffer("Field ").append(
281 sapStructureMapping.getName()).append(" not found in resultset").toString());
282 }
283
284 if (sapStructureMapping.getName().equalsIgnoreCase(outputListMapping.getName())) {
285
286 mapRecordToEntity(persistentObject, entity, outputList, sapStructureMapping);
287 } else {
288
289 if (sapStructureMapping.isListOfMaps()) {
290 JCO.Table table = checkAndGetTable(field);
291 if (table.getNumRows() == 0) {
292 if (logger.isDebugEnabled()) {
293 logger.debug("Multiple result table contains no rows: " + table.getName());
294 }
295 continue;
296 }
297
298
299 JCO.Table filteredTable = filterTable(table, pkField, pkValue);
300 if (filteredTable.getNumRows() == 0) {
301 if (logger.isDebugEnabled()) {
302 logger.debug("Filtered result table contains no rows: " + table
303 .getName() + "; filtered on key: " + pkField + "=" + pkValue);
304 }
305 } else {
306
307 convertMap(filteredTable, entity, sapStructureMapping, persistentObject);
308 }
309 } else if (sapStructureMapping.isList()) {
310
311 JCO.Table record = checkAndGetTable(field);
312 record.setRow(outputList.getRow());
313 mapRecordToEntity(persistentObject, entity, record, sapStructureMapping);
314 } else {
315 throw new SapJcoMappingException(
316 "Output list defined, structure type must be 'list' or 'list' with 'key-field' and 'pk' attribute defined");
317 }
318 }
319 }
320 if (entity != null && entity instanceof InitializingBean) {
321 try {
322 ((InitializingBean) entity).afterPropertiesSet();
323 } catch (Exception e) {
324 throw new DataRetrievalFailureException(
325 "Encountered problems while executing 'afterPropertiesSet' on value bean", e);
326 }
327 }
328 results.add(entity);
329 } while (outputList.nextRow());
330 return results;
331 }
332
333 private void handleFieldSelection(SapListMapping fieldSelection, JCO.Function function,
334 SapBapiMapping sapBapiMapping, String profile) {
335 if (fieldSelection != null) {
336 JCO.Structure fieldSelectionStructure = function.getImportParameterList().getStructure(
337 fieldSelection.getName());
338 Collection selectedFields = sapBapiMapping.getFieldSelection(profile);
339 if (selectedFields == null) {
340
341 JCO.FieldIterator iterator = fieldSelectionStructure.fields();
342 while (iterator.hasNextFields()) {
343 JCO.Field field = iterator.nextField();
344 field.setValue(SELECTED);
345 }
346 } else {
347 for (Iterator i = selectedFields.iterator(); i.hasNext();) {
348 Object value = i.next();
349 fieldSelectionStructure.setValue(SELECTED, String.valueOf(value));
350 }
351 }
352 }
353 }
354
355 private void prepareFunctionInputList(List inputListMappings, JCO.Function function, Map input) {
356 if (inputListMappings != null) {
357 JCO.ParameterList tableList = function.getTableParameterList();
358 for (Iterator inputListIterator = inputListMappings.iterator(); inputListIterator.hasNext();) {
359 SapListMapping inputListMapping = (SapListMapping) inputListIterator.next();
360 JCO.Table inputTable = tableList.getTable(inputListMapping.getName());
361 if (inputTable == null) {
362 throw new SapJcoMappingException("Input table '" + inputListMapping.getName() + "' doesn't exist");
363 }
364 Collection inputValues = (Collection) input.get(inputListMapping.getName());
365 if (inputValues != null) {
366 for (Iterator valueIterator = inputValues.iterator(); valueIterator.hasNext();) {
367 Object value = valueIterator.next();
368 inputTable.appendRow();
369 if (value instanceof Map) {
370
371
372
373 Map fields = (Map) value;
374 for (Iterator entrySetIterator = fields.entrySet().iterator(); entrySetIterator.hasNext();)
375 {
376 Map.Entry entry = (Map.Entry) entrySetIterator.next();
377 inputTable.setValue(String.valueOf(entry.getValue()), String.valueOf(entry.getKey()));
378 }
379 } else {
380
381
382 inputTable.setValue(String.valueOf(value), inputListMapping.getPrimaryKey());
383 }
384 }
385 }
386 }
387 }
388 }
389
390 private void prepareFunctionInput(SapBapiMapping sapBapiMapping, Map input, JCO.Function function) {
391
392 for (int j = 0; j < sapBapiMapping.getInput().size(); j++) {
393 SapInput sapInput = (SapInput) sapBapiMapping.getInput().get(j);
394 String value = (String) input.get(sapInput.getName());
395 if ((value == null || value.trim().length() == 0) && sapInput.isRequired()) {
396 if (sapInput.getDefaultValue() != null && sapInput.getDefaultValue().length() > 0) {
397 value = sapInput.getDefaultValue();
398 } else {
399 throw new SapJcoMappingException("Missing input parameter '" + sapInput
400 .getName() + "' (required and no default value supplied)");
401 }
402 }
403 if (logger.isDebugEnabled()) {
404 logger.debug(new StringBuffer("Setting import parameter '").append(sapInput.getName()).append("' to '")
405 .append(value).append("'"));
406 }
407 function.getImportParameterList().setValue(value, sapInput.getName());
408 }
409 }
410
411
412 /**
413 * This helper method creates a new <code>JCO.Table</code> instance that contains
414 * all rows of the parent table which have a value of <code>pkValue</code> for field <code>pkField</code>.
415 *
416 * @param table table to sort
417 * @param pkField name of the primary key field
418 * @param pkValue value against which rows are filtered
419 * @return new instance of JCO.Table containing all but the filtered rows
420 */
421 private JCO.Table filterTable(JCO.Table table, String pkField, String pkValue) {
422 JCO.Table subTable = new JCO.Table(table.getMetaData());
423 table.firstRow();
424 do {
425 if (pkValue.equals(table.getString(pkField))) {
426
427 subTable.appendRow();
428 for (int counter = 0; counter < table.getNumColumns(); counter++) {
429 subTable.setValue(table.getValue(counter), counter);
430 }
431 }
432 } while (table.nextRow());
433 return subTable;
434 }
435
436 /**
437 * This helper method checks if a JCO.Field instance that is mapped as a table is actually a JCO.Table
438 *
439 * @param field field to check
440 * @return JCO.Field cast to JCO.Table
441 * @throws SapJcoMappingException if the <code>JCO.Field</code> is not a <code>JCO.Table</code>
442 */
443 private JCO.Table checkAndGetTable(JCO.Field field) throws SapJcoMappingException {
444 if (!field.isTable()) {
445 throw new SapJcoMappingException(new StringBuffer("Field ").append(field.getName()).append(
446 " is mapped as table but isn't one").toString());
447 }
448 return field.getTable();
449 }
450
451 private JCO.Field getField(JCO.Function function, String name) {
452 JCO.ParameterList output = function.getExportParameterList();
453 if (output != null && output.hasField(name)) {
454 return output.getField(name);
455 }
456 output = function.getTableParameterList();
457 if (output != null && output.hasField(name)) {
458 return output.getField(name);
459 }
460 return null;
461 }
462
463 protected List retrieve(Class persistentObject, List inputMaps) {
464 return retrieve(persistentObject, inputMaps, null);
465 }
466
467 protected List retrieve(Class persistentObject, List inputMaps, String profile) {
468 if (persistentObject == null) {
469 throw new IllegalArgumentException("'persistentObject' parameter is required.");
470 }
471 if (inputMaps == null || inputMaps.size() == 0) {
472
473 return new ArrayList(0);
474 }
475
476
477 PooledExecutor pool = new PooledExecutor(maxParallelCalls);
478 pool.setMaximumPoolSize(maxParallelCalls);
479 pool.setMinimumPoolSize(1);
480 pool.waitWhenBlocked();
481
482 logger.warn("Using internal thread pool to execute " + inputMaps.size() + " requests in parallel");
483 try {
484 List results = new ArrayList(inputMaps.size());
485 List calls = new ArrayList(inputMaps.size());
486 for (int i = 0; i < inputMaps.size(); i++) {
487 Map input = (Map) inputMaps.get(i);
488 AsyncRetrieveCall call = new AsyncRetrieveCall(persistentObject, input, profile);
489 calls.add(call);
490 pool.execute(call);
491 }
492 for (int i = 0; i < calls.size(); i++) {
493 AsyncRetrieveCall asyncRetrieveCall = (AsyncRetrieveCall) calls.get(i);
494 Object result = asyncRetrieveCall.getMethodReturnValue();
495 if (result != null) {
496 results.add(result);
497 }
498 }
499 return results;
500 } catch (InterruptedException e) {
501 throw new DataRetrievalFailureException("The request has been interrupted by the system", e);
502 }
503 }
504
505 protected Object retrieve(Class persistentObject, Map input) {
506 return retrieve(persistentObject, input, null);
507 }
508
509 /**
510 * Retrieves value objects of type <code>persistentObject</code>.
511 * <p/>
512 * The <code>input</code> parameter specifies the input paramters that should be passed to SAP.
513 * If the result of this call will be one value object, the entries in the Map will be mapped to SAP call import parameters.
514 * If the result of this call is a List of value Objects, and a SAP input table is used rather than import parameters,
515 * the Map contains a Collection as well. The key of this Collection is the same as the SAP input table.
516 * <p/>
517 * This Collection can contain two types of values.
518 * <ol>
519 * <li>
520 * The elements of the collection contain a value that should be assigned to the primary key field of the table,
521 * which is defined in the input list mapping. Each value of in the collection will be assigned to the primary
522 * key of a new <code>JCO.Table</code> row
523 * </li>
524 * <li>
525 * The elements of the collection contain a Map. The keys of this Map should correspond exactly to the SAP
526 * fields and the values will be assigned to these fields. Every Map in the collection correspond to
527 * one <code>JCO.Table</code> row.
528 * </li>
529 * </ol>
530 * <p/>
531 * Here is an example of the second situation: a <code>Collection of Maps</code>.
532 * Each map can contain a different number of field/value pairs to process.
533 * <pre>
534 * Collection
535 * 0 - Map
536 * * DOCUMENT_NUMBER -> value
537 * 1 - Map
538 * * DOCUMENT_NUMBER -> value
539 * * DOCUMENT_TYPE -> value
540 * 2 - Map
541 * * MATERIAL -> value
542 * * DOCUMENT_NUMBER -> value
543 * * DOCUMENT_TYPE -> value
544 * etc.
545 * </pre>
546 *
547 * @param persistentObject type of the value object
548 * @param input input map
549 * @param profile name to retrieve fields for
550 * @return - one single value object of type <code>persistentObject</code>
551 * <br/> - or a List of value objects of type <code>persistentObject</code> if the SAP call supports input/output tables
552 */
553 protected Object retrieve(Class persistentObject, Map input, String profile) {
554 if (persistentObject == null) {
555 throw new IllegalArgumentException("'persistentObject' parameter is required.");
556 }
557 if (input == null) {
558 input = new HashMap(0);
559 }
560 try {
561
562 SapMapping sapMapping = SapMappingParser.getInfo(persistentObject);
563 return performSapCall(sapMapping, input, persistentObject, profile);
564 } catch (Exception e) {
565 throw new DataRetrievalFailureException("Error fetching data", e);
566 }
567 }
568
569 protected void checkReturnStructure(JCO.Function function) throws DataRetrievalFailureException {
570 if (function.getExportParameterList() != null && function.getExportParameterList().hasField("RETURN")) {
571 JCO.Structure returnStructure = function.getExportParameterList().getStructure("RETURN");
572 if (! ("".equals(returnStructure.getString("TYPE")) ||
573 "S".equals(returnStructure.getString("TYPE")) ||
574 "W".equals(returnStructure.getString("TYPE")))) {
575 throw new DataRetrievalFailureException(new StringBuffer(returnStructure.getString("TYPE"))
576 .append(": ").append(returnStructure.getString("MESSAGE")).toString());
577 }
578 }
579 }
580
581 protected void mapRecordToEntity(Class entityDef, Object entity, JCO.Record record,
582 SapStructureMapping structureMapping) {
583 for (Iterator fieldsIt = structureMapping.getFields().iterator(); fieldsIt.hasNext();) {
584 SapFieldMapping sapFieldMapping = (SapFieldMapping) fieldsIt.next();
585 mapOneField(entityDef, entity, record, sapFieldMapping);
586 }
587 }
588
589 protected void mapOneField(Class entityDef, Object entity, JCO.Record record, SapFieldMapping sapFieldMapping) {
590 try {
591 if (logger.isDebugEnabled()) {
592 logger.debug(
593 "Mapping property '" + sapFieldMapping.getPropertyName() + "' from field '" + sapFieldMapping
594 .getFieldName() + "'");
595 }
596 MethodInvoker invoker = reflectionCache.getCachedInvoker(sapFieldMapping);
597
598 JCO.Field sapField = record.getField(sapFieldMapping.getFieldName());
599 if (invoker == null) {
600 SapType type = SapType.getEnum(sapFieldMapping.getType());
601 if (type == null) {
602 throw new SapJcoMappingException(new StringBuffer("Invalid type '").append(
603 sapFieldMapping.getType()).append("'").toString());
604 }
605 invoker = new MethodInvoker();
606 invoker.setTargetClass(sapField.getClass());
607 invoker.setTargetMethod("get" + type.getName());
608 reflectionCache.cacheInvoker(sapFieldMapping, invoker);
609 }
610 Object value = null;
611 synchronized (invoker) {
612 invoker.setTargetObject(sapField);
613 invoker.prepare();
614 try {
615 value = invoker.invoke();
616 } catch (InvocationTargetException e) {
617 String stringValue = String.valueOf(value);
618 if (JCO.ConversionException.class.equals(e.getTargetException().getClass())) {
619
620 if (value == null || stringValue.length() == 0 || "?".equalsIgnoreCase(stringValue)) {
621 value = null;
622 } else {
623 throw (JCO.ConversionException) e.getTargetException();
624 }
625 }
626 }
627 }
628 String stringValue = String.valueOf(value);
629 if (value != null && stringValue.length() > 0 && !"?".equals(stringValue)) {
630 Field classField = reflectionCache.getCachedField(sapFieldMapping);
631
632 if (classField == null) {
633 classField = entityDef.getDeclaredField(sapFieldMapping.getPropertyName());
634 classField.setAccessible(true);
635 reflectionCache.cacheField(sapFieldMapping, classField);
636 }
637 classField.set(entity, value);
638 }
639 } catch (Exception e) {
640 String msg = new StringBuffer("Error mapping property '")
641 .append(sapFieldMapping.getPropertyName())
642 .append("'").toString();
643 logger.error(msg, e);
644 throw new SapJcoMappingException(msg, e);
645 }
646 }
647
648 protected void convertMap(JCO.Table table, Object entity, SapStructureMapping sapStructureMapping,
649 Class persistentObject) {
650
651
652
653 table.firstRow();
654 do {
655 String currentKey = table.getString(sapStructureMapping.getKeyField());
656 SapFieldMapping fieldMapping = (SapFieldMapping) sapStructureMapping.getFieldsMap().get(currentKey);
657 if (fieldMapping != null) {
658 mapOneField(persistentObject, entity, table, fieldMapping);
659 } else {
660 if (logger.isDebugEnabled()) {
661 logger.debug(
662 "No field mapping found for map structure or field is primary key of list_of_maps; key=" + currentKey);
663 }
664 }
665 } while (table.nextRow());
666 }
667
668 private Object instantiate(Class clazz) throws InstantiationException {
669 try {
670 if (clazz.getName().indexOf("$") > 0) {
671 String parentClassname = clazz.getName().substring(0, clazz.getName().indexOf("$"));
672 Class parentClass = Class.forName(parentClassname);
673
674 Constructor constructor = clazz.getConstructor(new Class[]{parentClass});
675 if (constructor == null) {
676 throw new InstantiationException("Error instantiating inner class");
677 }
678 return constructor.newInstance(new Object[]{parentClass.newInstance()});
679 } else {
680 return clazz.newInstance();
681 }
682 } catch (Exception e) {
683 logger.error("Error creating new instance", e);
684 throw new InstantiationException("Error creating new instance");
685 }
686 }
687 }