I'm currently implements Comments on comments.
When a member views a given post, they should be able to see comments on comments and comments.
To satisfy the above conditions, you need the index value of the post and the index value of the comment. That is, two parameter values are required.
So I wrote the following logic.
boardDaoMapper.xml
<select id="selectCommentOfComment" parameterType="java.util.HashMap" resultType="java.util.HashMap">
select * from board_comment_of_comment where board_idx = #{board_idx} and board_comm_idx = #{board_comm_idx}
</select>
And the BoardDao code:
public List<HashMap<String, Object>> selectCommentOfComment(int board_idx, int board_comm_idx);
And the BoardService:
#Override
public List<HashMap<String, Object>> getBoardCommentOfComment(int board_idx, int board_comm_idx)
{
System.out.println("ServieImple getBoardCommentOfComment Function Call >> " + board_idx + " / " + board_comm_idx);
List<HashMap<String, Object>> c_of_c_list = boardDao.selectCommentOfComment(board_idx, board_comm_idx);
return c_of_c_list;
}
And BoardController code:
#RequestMapping("viewPage.do")
public String viewPage(HttpSession session,HttpServletRequest request, HttpServletResponse response,Model model,
#RequestParam(value="board_idx", defaultValue="1") int board_idx) throws IOException,IllegalStateException
{
checkLogin(request, response, session);
// 해당 게시물에 대한 댓글들의 idx값 조회 (board_comm_idx)
List<HashMap<String, Object>> commentList= bService.getBoardForComment(boardData.getBoard_idx());
System.out.println("Board_idx Value was : " + board_idx);
// 해당 게시물에 대한 댓글들의 개수만큼 for문을 돌고..
for(int i = 0; i < commentList.size(); i++)
{
// board_comm_idx 값을 변수에 저장
int board_comm_idx= (Integer) commentList.get(i).get("board_comm_idx");
List<HashMap<String, Object>> getCommentOfComentList =
bService.getBoardCommentOfComment(board_idx, board_comm_idx);
System.out.println("GetCommentOfCommentList : " + getCommentOfComentList);
model.addAttribute("cocList", getCommentOfComentList);
}
return "viewPage";
}
Values are good for two parameters.(at BoardService) Like that..
ServieImple getBoardCommentOfComment Function Call >> 2 / 3
However, this does not perform well and causes an error.
org.apache.ibatis.binding.BindingException: Parameter 'board_idx' not found. Available parameters are [0, 1, param1, param2]
at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:165)
at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:44)
at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:116)
at org.apache.ibatis.executor.BaseExecutor.createCacheKey(BaseExecutor.java:186)
at org.apache.ibatis.executor.CachingExecutor.createCacheKey(CachingExecutor.java:129)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:76)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:108)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:102)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358)
at com.sun.proxy.$Proxy11.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:198)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:119)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:63)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52)
at com.sun.proxy.$Proxy13.selectCommentOfComment(Unknown Source)
at service.BoardServiceImple.getBoardCommentOfComment(BoardServiceImple.java:367)
at controller.BoardController.viewPage(BoardController.java:175)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:180)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:440)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:428)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
I think Mapper has a problem.
Why is this happening and how should I fix it?
Your comments are valuable. Please tell me your opinion.
The problem with the current approach
You currently your mapping set up in a way that doesn't accept your parameters into the DAO layer parameterType="java.util.HashMap"
This being the case, iBatis is expecting a HashMap to be passed.
There are multiple ways that you can go about fixing your code.
1.) Using Named Parameters (Depending on your version) > 3
public List<HashMap<String, Object>> selectCommentOfComment(#Param("board_idx") int board_idx, #Param("board_comm_idx") int board_comm_idx);
And remove the parameter type from your mapping xml.
2.) Pass in the expected object
You can fix your DAO method signature to reflect your mapping. And make the corresponding changes in the service.
DAO:
public List<HashMap<String, Object>> selectCommentOfComment(HashMap<String, Object> parameter);
SERVICE:
public List<HashMap<String, Object>> getBoardCommentOfComment(int board_idx, int board_comm_idx)
{
HashMap<String, Object> parameters = new HashMap<String, Object>();
parameters.put("board_idx", board_idx);
parameters.put("board_comm_idx", board_comm_idx");
List<HashMap<String, Object>> c_of_c_list = boardDao.selectCommentOfComment(board_idx, board_comm_idx);
return c_of_c_list;
}
You can draw additional inspiration from their documentation.
http://www.mybatis.org/mybatis-3/sqlmap-xml.html
Related
I'm reading multiple files from the S3 bucket using MultiResourceItemReader, I'm getting ClassCastException before executing the myReader() method, Something wrong with MultiResourceItemReader not sure what's going wrong here.
Please find my code below:
#Bean
public MultiResourceItemReader<String> multiResourceReader()
{
String bucket = "mybucket;
String key = "/myfiles";
List<InputStream> resourceList = s3Client.getFiles(bucket, key);
List<InputStreamResource> inputStreamResourceList = new ArrayList<>();
for (InputStream s: resourceList) {
inputStreamResourceList.add(new InputStreamResource(s));
}
Resource[] resources = inputStreamResourceList.toArray(new InputStreamResource[inputStreamResourceList.size()]);
//InputStreamResource[] resources = inputStreamResourceList.toArray(new InputStreamResource[inputStreamResourceList.size()]);
// I'm getting all the stream content - I verified my stream is not null
for (int i = 0; i < resources.length; i++) {
try {
InputStream s = resources[i].getInputStream();
String result = IOUtils.toString(s, StandardCharsets.UTF_8);
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
MultiResourceItemReader<String> resourceItemReader = new MultiResourceItemReader<>();
resourceItemReader.setResources(resources);
resourceItemReader.setDelegate(myReader());
resourceItemReader.setDelegate((ResourceAwareItemReaderItemStream<? extends String>) new CustomComparator());
return resourceItemReader;
}
Exception:
Caused by: java.lang.ClassCastException: class CustomComparator cannot be cast to class org.springframework.batch.item.file.ResourceAwareItemReaderItemStream (CustomComparator and org.springframework.batch.item.file.ResourceAwareItemReaderItemStream are in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader #cc285f4)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 65 common frames omitted
Can someone please help me to resolve this issue. Appreciated your help in advance. Thanks.
The reason you see the NullPointerException is due to the default comparator used by the MultiResourceItemReader to sort the resources after loading them.
The default compare behavior calls the getFilename() method of the InputStreamResource.
Refer - https://github.com/spring-projects/spring-batch/blob/115c3022147692155d45e23cdd5cef84895bf9f5/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java#L82
But the InputStreamResource just inherits the getFileName() method from its parent AbstractResource, which just returns null.
https://github.com/spring-projects/spring-framework/blob/316e84f04f3dbec3ea5ab8563cc920fb21f49749/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java#L220
The solution is to provide a custom comparator for the MultiResourceItemReader. Here is a simple example, assuming you do not want to sort the resources in a specific way before processing:
public class CustomComparator implements Comparator<InputStream>{
#Override
public int compare(InputStream is1, InputStream is2) {
//comparing based on last modified time
return Long.compare(is1.hashCode(),is2.hashCode());
}
}
MultiResourceItemReader<String> resourceItemReader = new MultiResourceItemReader<>();
resourceItemReader.setResources(resources);
resourceItemReader.setDelegate(myReader());
//UPDATED with correction - set custom Comparator
resourceItemReader.setComparator(new CustomComparator());
Refer this answer for how a Comparator is used by Spring Batch MultiResourceItemReader.
File processing order with Spring Batch
I am currently trying to test a simple post method in a normal Controller which returns a Mono to redirect to a different page or in this case the home page. I have tried all sorts of different aproaches mocking components but I always seem to be returning a null Mono in the test all works normally via form submission.
#Controller
public class AddNewEntryController {
private final EntryService service;
#PostMapping("/add-new-entry")
public Mono<String> addNewEntrySubmit(#ModelAttribute("timeEntry") Entry entry) {
return service.addTimeKeepingEntry(Flux.just(entry)).then(Mono.just("redirect:/"));
}
}
And the Service Class Code
public Mono<Void> addTimeKeepingEntry(Flux<Entry> entry) {
return entry.flatMap(entry -> Mono.when(repository.save(entry).log("Save to DB"))
.log("add entry when")).then().log("done");
}
And Test Code
#RunWith(SpringRunner.class)
#WebFluxTest(controllers = AddNewEntryController.class)
#Import({ThymeleafAutoConfiguration.class})
public class AddNewEntryControllerTest {
#Autowired
WebTestClient webTestClient;
#MockBean
EntryService service;
#Test
public void addNewEntrySubmit() {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("month", month);
formData.add("dateOfMonth", Integer.toString(21));
formData.add("startTime", "09:00");
when(service.addEntry(Flux.just(entry1))).thenReturn(Mono.empty());
webTestClient.post().uri("/add-new-entry").body(BodyInserters.fromFormData(formData)).exchange().expectStatus().isSeeOther().expectHeader().valueEquals(HttpHeaders.LOCATION, "/");
Whenever I run the test I am always getting a Null Pointer and after debugging it is pointing to Mono as being Null. Problem is I am not sure which Mono or at which step.
The StackTrace I get is as follows.
java.lang.NullPointerException: null
at com.dbeer.timekeeping.UI.AddNewEntryController.addNewEntrySubmit(AddNewEntryController.java:47) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:243) ~[spring-webflux-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$invoke$0(InvocableHandlerMethod.java:138) ~[spring-webflux-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:118) [reactor-core-3.1.9.RELEASE.jar:3.1.9.RELEASE]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1083) ~[reactor-core-3.1.9.RELEASE.jar:3.1.9.RELEASE]
at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:247) ~[reactor-core-3.1.9.RELEASE.jar:3.1.9.RELEASE]
at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:329) ~[reactor-core-3.1.9.RELEASE.jar:3.1.9.RELEASE]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:185) ~[reactor-core-3.1.9.RELEASE.jar:3.1.9.RELEASE]
after looking at your project, the issue seems to be the consistency of naming of controllers and the html page.
e.g. in header.html you had a url to link add-entry but your controller had add-new-entry if you change the url in header to add-new-entry it works.
As a clean up, you should use thmyeleaf to generate the URL not a normal href, as if you ever add security later, thymeleaf will add the session id to the URL etc
***********Edit pulled the branch and could reproduce *******
The line
given(service.addTimeKeepingEntry(Flux.just(new TimeKeepingEntry(month, 21, "Tuesday", "09:00", "30", "17:00", "7.5", false)))).willReturn(Mono.empty());
is the issue, since Mockito matches on Object.equals here and you have not defined what equals means to your object.
another way is to capture the object passed into the mock
e.g.
#Captor
private ArgumentCaptor<Flux<TimeKeepingEntry>> captor;
#Test
public void addNewEntrySubmit() {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("month", month);
formData.add("dateOfMonth", Integer.toString(21));
formData.add("day", "Tuesday");
formData.add("startTime", "09:00");
formData.add("endTime", "17:00");
formData.add("breakLength", "30");
given(service.addTimeKeepingEntry(any())).willReturn(Mono.empty());
webTestClient.post().uri("/add-new-entry")
.body(BodyInserters.fromFormData(formData)).exchange().expectStatus().isSeeOther().expectHeader().valueEquals(HttpHeaders.LOCATION, "/");
verify(service).addTimeKeepingEntry(captor.capture());
TimeKeepingEntry timeKeepingEntry = captor.getValue().blockFirst();
assertThat(timeKeepingEntry.getMonth()).isEqualTo(month);
//and whatever else you want to test
}
I'm attempting to pull data from a SOAP service that I have no control over. The hierarchy contains ProductOrder -> ShipTo -> Item where there are one or more shipToes and one or more Items per shipto.
Their API uses a mock SQL like query language. I'm getting stack traces like the following when trying to pull data including the items. if I exclude item, I'm able to pull the ProductOrders along with ShipTo objects, but items is always an empty list.
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at
org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:779)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:760)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:747)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
edu.umich.oud.giftformatter.convioexport.Application.main(Application.java:39)
~[classes/:na] Caused by:
org.springframework.oxm.UncategorizedMappingException: Unknown JAXB
exception; nested exception is javax.xml.bind.JAXBException: Field
order for ShipTo.Item.ItemId does not match the schema definition for
record type ProductOrder at
org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:915)
~[spring-oxm-4.3.6.RELEASE.jar:4.3.6.RELEASE] at
edu.umich.oud.giftformatter.convioexport.CustJaxbUnMarshaller.unmarshal(CustJaxbUnMarshaller.java:37)
~[classes/:na] at
org.springframework.ws.support.MarshallingUtils.unmarshal(MarshallingUtils.java:62)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate$3.extractData(WebServiceTemplate.java:413)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:619)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:390)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:383)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
edu.umich.oud.giftformatter.convioexport.services.ConvioClient.queryInternal(ConvioClient.java:159)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.services.ConvioClient.query(ConvioClient.java:134)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.services.ProductOrderService.getProductOrders(ProductOrderService.java:87)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.services.ConvioService.load(ConvioService.java:82)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.Application.lambda$runner$0(Application.java:72)
~[classes/:na] at
org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:776)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] ... 4 common frames
omitted Caused by: javax.xml.bind.JAXBException: Field order for
ShipTo.Item.ItemId does not match the schema definition for record
type ProductOrder ... 17 common frames omitted
The product order service contains a method like so:
public List<ProductOrderObj> getProductOrders(final Date startDate, final Date endDate) {
final String query = String.format("SELECT siteId,orderId,transactionId,purchaseAmount,taxDeductibleValue,\n" +
"shippingCharge,additionalDonation,discountAmount,discountCode,\n" +
"creationDate,createdBy,modifyDate,lastChangeBy,storeId,payment,\n" +
"purchaser,interactionSource,shipTo,\n" +
"receiptNumber,shipTo.item FROM ProductOrder where creationDate > %s and creationDate < %s",
convertDate(startDate), convertDate(endDate));
log.info("query is " + query);
final Session session = convioClient.startSession();
final ArrayList<ProductOrderObj> events = new ArrayList<>();
for (int page = 1; page < 100; page++) {
final List<? extends RecordObj> items = convioClient.query(session, page, ConvioConfiguration.MAX_DOWNLOADS_PER_REQUEST, query);
if (items.size() < ConvioConfiguration.MAX_DOWNLOADS_PER_REQUEST) {
events.addAll((List<ProductOrderObj>) items);
break;
}
events.addAll((List<ProductOrderObj>) items);
}
return events;
}
Which in turn calls the convioService.query method that effectively does this
private List<? extends RecordObj> queryInternal(final Session session, final
int page, final int pageSize, final String q) {
// setup query
final Query query = new Query();
query.setPage(BigInteger.valueOf(page));
query.setPageSize(BigInteger.valueOf(pageSize));
query.setQueryString(q);
log.trace(q);
// perform query
try {
final Object obj = getWebServiceTemplate().marshalSendAndReceive(query,
new SoapActionExecutionIdCallback(session));
final QueryResponse response = (QueryResponse) obj;
if (response != null) {
log.debug("Response was a " + response.getClass().getName());
return response.getRecord();
}
} catch (final Exception e) {
log.error(e.getMessage());
throw e;
}
throw new NullPointerException("response was null");
}
There seemed to be two issues causing this not to work:
Bad field definition for the child object. shipTo.items vs shipTo.Items
Disabling validation of the dtd in the marshaller/unmarshaller
This is how I create "AbstractInputStreamContent" from inputstream of file:
final Long length = Long.valueOf(filesData.get(uploadedFileName).get("size")).longValue();
final InputStream fileStream = item.openStream(); //FileItemStream item
AbstractInputStreamContent fileContent = new AbstractInputStreamContent(uploadedFileMimeType) {
#Override
public boolean retrySupported() {
return false;
}
#Override
public long getLength() throws IOException {
return length;
}
#Override
public InputStream getInputStream() throws IOException {
return fileStream;
}
};
And "InputStreamContent" as:
InputStreamContent fileContent = new InputStreamContent(uploadedFileMimeType, item.openStream());
fileContent.setLength(Long.valueOf(filesData.get(uploadedFileName).get("size")).longValue());
To replace old file with new file I use(both files are of .docx format):
Drive.Files.Update update = driveService.files().update(fileIdOfFileToReplace,fileMeta,fileContent);
update.set("uploadType", "resumable");
update.getMediaHttpUploader().setDirectUploadEnabled(false);
update.getMediaHttpUploader().setChunkSize(MediaHttpUploader.DEFAULT_CHUNK_SIZE);
File updatedFile = update.execute();
Uploading a new file works fine whether I use InputStreamContent or AbstractInputStreamContent. But update gives "java.lang.IllegalArgumentException" with both
java.lang.IllegalArgumentException
at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument(Preconditions.java:111)
at com.google.api.client.util.Preconditions.checkArgument(Preconditions.java:37)
at com.google.api.client.googleapis.media.MediaHttpUploader.setInitiationRequestMethod(MediaHttpUploader.java:872)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.initializeMediaUpload(AbstractGoogleClientRequest.java:237)
at com.google.api.services.drive.Drive$Files$Update.<init>(Drive.java:3163)
at com.google.api.services.drive.Drive$Files.update(Drive.java:3113)
at com.util.DocumentsUtil.updateFile(DocumentsUtil.java:22)
at com.controllers.collab.documents.Documents.fileUpload(Documents.java:165)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:44)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
Solved it by giving exclusion of google-api-client from each google api maven dependency. Later added google-api-client-1.22.0-SNAPSHOT dependency from sonatype repo. It works fine
I have a simple Spring Boot application with 2 bean classes, a main class and a configuration class. Whenever I try to access the Spring managed Bank bean from a Groovy closure, I get an exception:
Exception in thread "main" java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:304)
at org.springframework.boot.SpringApplication.runCommandLineRunners(SpringApplication.java:675)
at com.example.closures.ClosuresApplication$_run_closure1.doCall(ClosuresApplication.groovy:22)
at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:690)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.boot.SpringApplication$run.call(Unknown Source)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:324)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:292)
at com.example.closures.ClosuresApplication.main(ClosuresApplication.groovy:27)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1016)
Caused by: groovy.lang.MissingPropertyException: No such property: bank for class: com.example.closures.ClosuresApplication$$EnhancerBySpringCGLIB$$44735576
at groovy.lang.Closure.call(Closure.java:423)
at groovy.lang.Closure.call(Closure.java:439)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2027)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:51)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2012)
at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:49)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2053)
at org.codehaus.groovy.runtime.dgm$162.invoke(Unknown Source)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:304)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
at com.example.closures.ClosuresApplication$_run_closure1.doCall(ClosuresApplication.groovy:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:122)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.example.closures.ClosuresApplication.run(ClosuresApplication.groovy:21)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
at org.springframework.boot.SpringApplication.runCommandLineRunners(SpringApplication.java:672)
... 9 common frames omitted
Bank.groovy
#Component
final class Bank {
final String name = 'MyBank'
final AutomatedTellerMachine insideAtm
final AutomatedTellerMachine outsideAtm
#Autowired
Bank(#Qualifier('insideAtm') final AutomatedTellerMachine insideAtm, #Qualifier('outsideAtm') final AutomatedTellerMachine outsideAtm) {
this.insideAtm = insideAtm
this.outsideAtm = outsideAtm
}
String getName() {
return name
}
AutomatedTellerMachine getInsideAtm() {
return insideAtm
}
AutomatedTellerMachine getOutsideAtm() {
return outsideAtm
}
}
AutomatedTellerMachine.groovy
final class AutomatedTellerMachine {
final String name
AutomatedTellerMachine(final String name) {
this.name = name
}
String getName() {
return name
}
}
AppConfig.groovy
#Configuration
class AppConfig {
#Bean(name = 'insideAtm')
AutomatedTellerMachine getInsideAtm() {
new AutomatedTellerMachine('insideAtm')
}
#Bean(name = 'outsideAtm')
AutomatedTellerMachine getOutsideAtm() {
new AutomatedTellerMachine('outsideAtm')
}
}
ClosuresApplication.groovy
#SpringBootApplication
class ClosuresApplication implements CommandLineRunner {
#Autowired
private Bank bank
#Override
void run(String... args) throws Exception {
for (def i = 0; i < 10; i++) {
printf 'Bank %02d: %s%n', (i + 1), bank
}
(1..10).each {
printf 'Bank %02d: %s%n', it, bank
}
}
static void main(String[] args) {
SpringApplication.run ClosuresApplication, args
}
}
The regular for loop works just fine, but the .each {} closure from Groovy gives an exception. Any ideas?
I have run into this problem at odd times and found that a simple work-around can fix it - add a reference to your bank variable in the run method:
def _bank = bank
(1..10).each {
printf 'Bank %02d: %s%n', it, _bank
}
It seems to be an odd scoping issue that I have never been able to determine the real cause of.
Another possible solution is converting bank field to groovy property by removing 'private' modifier.
#Autowired
Bank bank
This is slightly less visually hideous (especially if you having this issue with more than one field), but it does change access level of that field.
I've encountered a number of these issues, all seemingly related to using private fields inside various closures in spring-managed beans, specifically when the beans are 'enhanced' (proxied) by spring (EnhancerBySpringCGLIB).