Google Docs API V1 access document in Java Servlet - java

I am trying to access Google Docs API V1 document in Java Servlet using AbstractAuthorizationCodeServlet provide by google api client library but code give error it can't build object of Doc Builder. I have auth 2 creatential and Google doc api is enable from google developer console. below is my code.
#WebServlet("/GetGoogleDoc")
public class GetGoogleDoc extends AbstractAuthorizationCodeServlet {
private static final long serialVersionUID = 1L;
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final List<String> SCOPES = Collections.singletonList(DocsScopes.DOCUMENTS_READONLY);
private static final String DOCUMENT_ID = "";
private static final String CLIENT_ID="";
private static final String CLIENT_SECRET="";
public GetGoogleDoc() {
super();
// TODO Auto-generated constructor stub
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
Docs service = new Docs.Builder(HTTP_TRANSPORT, JSON_FACTORY, initializeFlow())
.build();
}
#Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
GenericUrl url = new GenericUrl(req.getRequestURL().toString());
url.setRawPath("/oauth2callback");
return url.build();
}
#Override
protected AuthorizationCodeFlow initializeFlow() throws IOException {
return new GoogleAuthorizationCodeFlow.Builder(
new NetHttpTransport(), JSON_FACTORY, CLIENT_ID,CLIENT_SECRET,SCOPES).build();
}
#Override
protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
return null;
// return user ID
}
static void replaceNamedRange(Docs service, String documentId, String rangeName, String newText)
throws IOException {
Document document = service.documents().get(documentId).execute();
NamedRanges namedRangeList = document.getNamedRanges().get(rangeName);
if (namedRangeList == null) {
throw new IllegalArgumentException("The named range is no longer present in the document.");
}
List<Range> allRanges = new ArrayList<>();
Set<Integer> insertIndexes = new HashSet<>();
for (NamedRange namedRange : namedRangeList.getNamedRanges()) {
allRanges.addAll(namedRange.getRanges());
insertIndexes.add(namedRange.getRanges().get(0).getStartIndex());
}
// Sort the list of ranges by startIndex, in descending order.
allRanges.sort(Comparator.comparing(Range::getStartIndex).reversed());
// Create a sequence of requests for each range.
List<Request> requests = new ArrayList<>();
for (Range range : allRanges) {
// Delete all the content in the existing range.
requests.add(
new Request().setDeleteContentRange(new DeleteContentRangeRequest().setRange(range)));
if (insertIndexes.contains(range.getStartIndex())) {
// Insert the replacement text.
requests.add(
new Request()
.setInsertText(
new InsertTextRequest()
.setLocation(
new Location()
.setSegmentId(range.getSegmentId())
.setIndex(range.getStartIndex()))
.setText(newText)));
// Re-create the named range on the new text.
requests.add(
new Request()
.setCreateNamedRange(
new CreateNamedRangeRequest()
.setName(rangeName)
.setRange(
new Range()
.setSegmentId(range.getSegmentId())
.setStartIndex(range.getStartIndex())
.setEndIndex(range.getStartIndex() + newText.length()))));
}
}
BatchUpdateDocumentRequest batchUpdateRequest =
new BatchUpdateDocumentRequest()
.setRequests(requests)
.setWriteControl(new WriteControl().setRequiredRevisionId(document.getRevisionId()));
service.documents().batchUpdate(documentId, batchUpdateRequest).execute();
}
}

Related

How to convert a simple method that returns the List<String> into Multi<String> based on Smallrye Mutiny?

I am developing an application that reads the XML file and creates the Hash ID based on the details present in XML. As of now, everything is working perfectly, and able to get the List<String>.
I would like to convert this application into Reactive Streams using the Smallrye Mutiny so I went through some of the documentation but did not understand clearly how to convert this application into Reactive Streams where I do not have to wait for the completion of all XML file to return the List<String>. Rather I can start returning the Multi<String> as and when the its generated.
Following is the simple XML that I am reading using SAX Parser to create the Hash ID:
<customerList>
<customer>
<name>Batman</name>
<age>25</age>
</customer>
<customer>
<name>Superman</name>
<age>28</age>
</customer>
</customerList>
Following is the Main application which will make a call to SaxHandler:
public Multi<String> xmlEventHashGenerator(final InputStream xmlStream) throws SAXException, ParserConfigurationException, IOException {
final SAXParserFactory factory = SAXParserFactory.newInstance();
final SaxHandler saxHandler = new SaxHandler();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.newSAXParser().parse(xmlStream, saxHandler);
return Multi.createFrom().emitter(em ->{
saxHandler.getRootNodes().forEach(contextNode -> {
final String preHashString = contextNode.toString();
try {
final StringBuilder hashId = new StringBuilder();
MessageDigest.getInstance("SHA-256").digest(preHashString.getBytes(StandardCharsets.UTF_8));
hashId.append(DatatypeConverter.printHexBinary(digest).toLowerCase());
em.emit(hashId.toString());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
});
em.complete();
});
}
Following is the SaxHandler which will read the XML and create HashIDs:
public class SaxHandler extends DefaultHandler {
#Getter
private final List<String> eventHashIds = new ArrayList<>();
#Getter
private final List<ContextNode> rootNodes = new ArrayList<>();
private final HashMap<String, String> contextHeader = new HashMap<>();
private final String hashAlgorithm;
private ContextNode currentNode = null;
private ContextNode rootNode = null;
private final StringBuilder currentValue = new StringBuilder();
public SaxHandler(final String hashAlgorithm) {
this.hashAlgorithm = hashAlgorithm;
}
#Override
public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) {
if (rootNode == null && qName.equals("customer")) {
rootNode = new ContextNode(contextHeader);
currentNode = rootNode;
rootNode.children.add(new ContextNode(rootNode, "type", qName));
}else if (currentNode != null) {
ContextNode n = new ContextNode(currentNode, qName, (String) null);
currentNode.children.add(n);
currentNode = n;
}
}
#Override
public void characters(char[] ch, int start, int length) {
currentValue.append(ch, start, length);
}
#Override
public void endElement(final String uri, final String localName, final String qName) {
if (rootNode != null && !qName.equals("customer")) {
final String value = !currentValue.toString().trim().equals("") ? currentValue.toString().trim() : null;
currentNode.children.add(new ContextNode(currentNode, qName, value));
}
if (qName.equals("customer")) {
rootNodes.add(rootNode);
rootNode = null;
}
currentValue.setLength(0);
}
}
Following is the Test:
#Test
public void xmlTest() throws Exception {
final HashGenerator eventHashGenerator = new HashGenerator();
final InputStream xmlStream = getClass().getResourceAsStream("/customer.xml");
final List<String> eventHashIds = eventHashGenerator.xmlHashGenerator(xmlStream, "sha3-256");
System.out.println("\nGenerated Event Hash Ids : \n" + eventHashIds);
}
Can someone please guide me to some example or provide some idea on how to convert this application into SmallRye Mutinty Multi<String> based application?
I think you can refactor xmlEventHashGenerator to
public Multi<String> xmlEventHashGenerator(final InputStream xmlStream) throws SAXException, ParserConfigurationException, IOException {
final SAXParserFactory factory = SAXParserFactory.newInstance();
final SaxHandler saxHandler = new SaxHandler();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.newSAXParser().parse(xmlStream, saxHandler);
return Multi.createFrom()
.iterable( saxHandler.getRootNodes() )
.map( RootNode::toString )
.map( this::convertDatatype );
}
private String convertDatatype(String preHashString) {
try {
// I think we could create the MessageDigest instance only once
byte[] digest = MessageDigest.getInstance( "SHA-256" )
.digest( preHashString.getBytes( StandardCharsets.UTF_8 ) );
return DatatypeConverter.printHexBinary( digest ).toLowerCase();
}
catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException( e );
}
}
The test method will look something like:
#Test
public void xmlTest() throws Exception {
final HashGenerator eventHashGenerator = new HashGenerator();
final InputStream xmlStream = getClass().getResourceAsStream("/customer.xml");
System.out.println("Generated Event Hash Ids: ");
eventHashGenerator
.xmlHashGenerator(xmlStream)
// Print all the hash codes
.invoke( hash -> System.out.println( hash )
.await().indefinitely();
}
But if you want to concatenate all the hash codes, you can do:
#Test
public void xmlTest() throws Exception {
final HashGenerator eventHashGenerator = new HashGenerator();
final InputStream xmlStream = getClass()
.getResourceAsStream("/customer.xml");
String hash = eventHashGenerator
.xmlHashGenerator(xmlStream)
// Concatenate all the results
.collect().with( Collectors.joining() );
// Print the hashcode
.invoke( hashcode -> System.out.println("\nGenerated Event Hash Ids : \n" + hashcode) )
.await().indefinitely();
}

Invalid grant when authenticating to Google

I'm trying to connect to the Google Calendar API. I am have followed the step in Google calendar quick start for Java
{ "error" : "invalid_grant", "error_description" : "Bad Request" }
Can you please advise on how to debug this? The error message is unfortunately not helpful and I already tried everything I could find about this particular error on Stack overflow or elsewhere
Every time I got the same access token for different credentials:
Access token: {user=Class{accessToken=null, refreshToken="" expirationTimeMilliseconds=null}}
code:
public class CalendarServiceImpl implements CalendarService {
public static final String APPLICATION_NAME = "GoogleCalenderApi";
public static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
public static final String TOKENS_DIRECTORY_PATH = "/data.json";
public static final List<String> SCOPES = Collections.singletonList(CalendarScopes.CALENDAR);
public static final String CREDENTIALS_FILE_PATH = "/data.json";
public Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT, HttpServletRequest request)
throws IOException {
InputStream in = CalendarServiceImpl.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
if (in == null) {
log.info("Resource not found: " + CREDENTIALS_FILE_PATH);
throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
}
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
System.out.println("Access token: " + flow.getCredentialDataStore());
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(80)
.build();
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}
public void createCalendarEvent(String candidateMailId, String companyEmailId, DateTime fromTime, DateTime toTime,
String summary, String description, HttpServletRequest request)
throws GeneralSecurityException, IOException {
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
Event event = new Event().setSummary(summary).setLocation("Test").setDescription(description);
EventDateTime start = new EventDateTime().setDateTime(fromTime).setTimeZone("Asia/Kolkata");
event.setStart(start);
EventDateTime end = new EventDateTime().setDateTime(toTime).setTimeZone("Asia/Kolkata");
event.setEnd(end);
String[] recurrence = new String[] { "RRULE:FREQ=DAILY" };
event.setRecurrence(Arrays.asList(recurrence));
EventAttendee[] attendees = new EventAttendee[] { new EventAttendee().setEmail(candidateMailId),
new EventAttendee().setEmail(companyEmailId) };
event.setAttendees(Arrays.asList(attendees));
EventReminder[] reminderOverrides = new EventReminder[] { new EventReminder().setMethod("email").setMinutes(10),
new EventReminder().setMethod("popup").setMinutes(10), };
Event.Reminders reminders = new Event.Reminders().setUseDefault(false)
.setOverrides(Arrays.asList(reminderOverrides));
event.setReminders(reminders);
// Build service account credential.
Credential credential = getCredentials(HTTP_TRANSPORT, request);
log.info("got credential:" + event);
Calendar service = new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName(APPLICATION_NAME).build();
String calendarId = "primary";
try {
System.out.printf("Event started" + event);
event = service.events().insert(calendarId, event).setSendUpdates("all").execute();
} catch (IOException e) {
log.info("event IOException:" + e);
e.getMessage();
}
log.info("Event created:" + event.getHtmlLink());
}
}
You appear to be using AuthorizationCodeInstalledApp which as it stats is for installed applications. for web applications you need to use AuthorizationCodeFlow.
The official example can be found here
Web server application
public class CalendarServletSample extends AbstractAuthorizationCodeServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// do stuff
}
#Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
GenericUrl url = new GenericUrl(req.getRequestURL().toString());
url.setRawPath("/oauth2callback");
return url.build();
}
#Override
protected AuthorizationCodeFlow initializeFlow() throws IOException {
return new GoogleAuthorizationCodeFlow.Builder(
new NetHttpTransport(), JacksonFactory.getDefaultInstance(),
"[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]",
Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(
DATA_STORE_FACTORY).setAccessType("offline").build();
}
#Override
protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
// return user ID
}
}
public class CalendarServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet {
#Override
protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
throws ServletException, IOException {
resp.sendRedirect("/");
}
#Override
protected void onError(
HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
throws ServletException, IOException {
// handle error
}
#Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
GenericUrl url = new GenericUrl(req.getRequestURL().toString());
url.setRawPath("/oauth2callback");
return url.build();
}
#Override
protected AuthorizationCodeFlow initializeFlow() throws IOException {
return new GoogleAuthorizationCodeFlow.Builder(
new NetHttpTransport(), JacksonFactory.getDefaultInstance()
"[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]",
Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(
DATA_STORE_FACTORY).setAccessType("offline").build();
}
#Override
protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
// return user ID
}
}
AuthorizationCodeFlow opens the consent browser window in the machine that the code is running on. When you are running something as a website it needs to open on the client machine so that the user can see it.

Unit test a private method via a protected method in Java

I've these two methods from my MetadataManagement class which I'd like to unit test:
#Override
protected void doPut(final HttpServletRequest request, final HttpServletResponse response,
final MetadataResource resource)
throws IOException {
ServiceCommon.checkRole(getSubject(request));
if (resource.getType() != Type.CONTAINER) {
final String err = "Request not allowed for " + request.getURI();
throw new ServiceApiException(ServiceApiError.METHOD_NOT_ALLOWED, err);
}
final String name = getContainerName(resource);
final ServiceApiMetadata config = getConfig(request, PATH);
final StorageLocation storageLocation = getStorageLocation(conf.getStorageLocation());
if (config.getNotifications() != null) {
checkMethodSupported(id);
checkService(id);
}
}
private ServiceApiMetadata getConfig(final HttpServletRequest request, final String path)
throws IOException {
final Schema schema;
try (final InputStream inStream = this.getClass().getResourceAsStream(path)) {
final JSONObject origSchema = new JSONObject(new JSONTokener(inStream));
if (isGoldStar()) {
origSchema.getJSONObject("properties")
.getJSONObject("notifications")
.getJSONObject("properties")
.getJSONObject("topic")
.put("pattern", "^[0-9A-Za-z-.]*$");
}
schema = SchemaLoader.load(origSchema);
}
final ServiceApiMetadata config;
try (final BufferedReader reader = request.getReader()) {
final JSONObject json = new JSONObject(new JSONTokener(reader));
schema.validate(json);
config = ServiceApiMetadata.read(json);
} catch (final ValidationException e) {
_logger.debug(e.getMessage());
if (e.getLocation().contains("#/properties/notifications")) {
throw new ServiceApiException(ServiceApiError.MALFORMED_NOTIFICATIONS_ERROR,
ServiceApiErrorMessage.MALFORMED_JSON);
} else {
throw new ServiceApiException(ServiceApiError.MALFORMED_JSON);
}
} catch (final JSONException e) {
_logger.debug(e.getMessage());
throw new ServiceApiException(ServiceApiError.MALFORMED_JSON);
}
return config;
}
As I understand it I can not directly call getConfig in my test because the method is private. I believe using reflection is an option but is not advised. Based on that, any test of getConfig should be done through doPut.
What I'm most interested in checking is if getConfig.isGoldStar is true, the origSchema pattern updates to ^[0-9A-Za-z]*$ and if it is false it remains at ^[0-9A-Za-z-._]*$.
To call doPut in my test I will need HttpServletRequest, HttpServletResponse and MetadataResource objects. I'm not sure how I generate these. HttpServletRequest and HttpServletResponse are from javax.servlet.ServletRequest and MetadataResource comes from within my project. It takes HttpServletRequest and an enum as parameters.
How do I do this test? I think I should be OK once I can call the doPut method but I'm struggling to do that.

Google spreadsheet create a new sheet with permission for all users have link

I tried to user the google sheet API v4 to create a new sheet. I create it successfully. However, I found that the sheet is setting as I am the only one is able to access. My goal is to allow users have the url link can access the sheet in Java request.
I tried to set google credidential to null when create spreadsheet, but it use as to create the spreadsheet in drive. Thus, this is not work.
I browser it library class and api, but I cannot found anything related to the permission.
I am not so sure the way I create the spreadsheet is on right way. Here is my code:
public class CreateSpreadSheetTask extends AsyncTask<Void, Void, Void> {
private com.google.api.services.sheets.v4.Sheets mService = null;
private Exception mLastError = null;
private String title = null;
private CreateSpreadsheetListener listener = null;
public CreateSpreadSheetTask(GoogleAccountCredential credential, String title, CreateSpreadsheetListener listener) {
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
mService = new Sheets.Builder(
transport, jsonFactory, credential)
.setApplicationName("Google Sheets API Android Quickstart")
.build();
this.title = title;
this.listener = listener;
}
#Override
protected Void doInBackground(Void... params) {
try {
this.listener.onRecvCreateSpreadsheet(getSpreadsheetFromApi());
} catch (Exception e) {
e.printStackTrace();
mLastError = e;
cancel(true);
}
return null;
}
private Spreadsheet getSpreadsheetFromApi() throws IOException {
Spreadsheet spreadsheet = new Spreadsheet();
SpreadsheetProperties properties = new SpreadsheetProperties();
properties.setTitle(this.title);
spreadsheet.setProperties(properties);
Sheets.Spreadsheets.Create request = mService.spreadsheets().create(spreadsheet);
return request.execute();
}
}
As #tehhowch said the permission should handle via the Drive API. Here is just update an answer for this.
private void updateProperty(com.google.api.services.drive.Drive driveService, String fileId, String email) throws IOException {
BatchRequest batch = driveService.batch();
Permission clientPermission = new Permission();
clientPermission.setEmailAddress(email);
clientPermission.setRole("writer");
clientPermission.setType("user");
driveService.permissions().create(fileId, clientPermission)
.setFields("id")
.setSendNotificationEmail(true)
.queue(batch, new JsonBatchCallback() {
#Override
public void onSuccess(Object o, HttpHeaders responseHeaders) throws IOException {
Log.e("permissione success", String.valueOf(o));
listener.onRecvShareSpreadsheet(true);
}
#Override
public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) throws IOException {
Log.e("permissione error", e.getMessage());
listener.onRecvShareSpreadsheet(false);
}
});
batch.execute();
}
ref: Google api permission

how variables accessible in anonymous class?

I have following code :
class A extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletRespons
response) throws ServletException, IOExceptio
{
String str= "String In doGet()";
JsonBatchCallback<Users> callback = new
JsonBatchCallback<Users>(){
String inThisClass = str; // Showing me error
}
}
}
str is not accessible. How i can access "str".
My Actual Code is as follows :
public class SyncTask extends HttpServlet {
private static final long serialVersionUID = 1L;
final Logger logger = Logger.getLogger(this.getClass().getName());
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String domain = request.getParameter("DomainName");
String reqAdminEmail = request.getParameter("AdminEmail");
String searchRegex = request.getParameter("searchRegex");
Boolean manualSync = false;
if(reqAdminEmail != null){
manualSync = true;
}
String adminEmail = "";
try{
Date startTime = new Date();
Manager mangerObj = new Manager("MASTER");
final String SERVICE_ACCOUNT_EMAIL = Constant.getServiceAccountEmail();
final String SERVICE_ACCOUNT_PKCS12_FILE_PATH = Constant.getServiceAccountPkcs12FilePath();
Collection<String> SCOPES = new ArrayList<String>();
SCOPES.add("https://www.googleapis.com/auth/admin.directory.user");
SCOPES.add("https://www.googleapis.com/auth/admin.directory.orgunit");
String nextToken = null;
int noOfUsers = 0;
mangerObj = new Manager(domain);
Configuration config = mangerObj.getConfiguration();
if(config==null)
return;
else
adminEmail = config.getAdminEmail();
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(SCOPES)
.setServiceAccountUser(adminEmail)
.setServiceAccountPrivateKeyFromP12File(
new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
.build();
Directory directoryService = new Directory.Builder(httpTransport, jsonFactory, credential).setApplicationName("Directory ").build();
BatchRequest batch = directoryService.batch();
do{
List list = directoryService.users().list();
list.setCustomer("my_customer");
list.setMaxResults(500);
list.setPageToken(nextToken);
list.setFields("nextPageToken,users(relations,orgUnitPath,primaryEmail,name)");
if(searchRegex != null ){
if(searchRegex.isEmpty() == false){
list.setQuery("email:"+searchRegex+"*");
}
}
JsonBatchCallback<Users> callback = new JsonBatchCallback<Users>() {
#Override
public void onSuccess(Users users, HttpHeaders responseHeaders) {
nextToken = users.getNextPageToken(); // i'm not able to access nextToken
}
public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
System.out.println("Error Message: " + e.getMessage());
}
};
list.queue(batch, callback);
}while(nextToken != null);
try {
batch.execute();
} catch(Exception ex){
ErrorHandler.errorHandler(this.getClass().getName(), ex);
}
}catch(Exception ex){
ErrorHandler.errorHandler(this.getClass().getName(), ex);
}
}
I have updated my code where actually i am getting error. I want to access nextToken into anonymous class,but i not able to accesss.
Error as follows :
nextToken cannot be resolved to a variable
You need to make the str variable final.
As a matter of fact the inThisClass variable is redundant, at least in what you posted so far.
But your actual code shows a different error message from your sample code. Your actual code cannot be written, because you don't have write-access to local variables in enclosing scopes from anonymous classes. You will have to think of something else, such as a final StringBuilder.
Declare str in doGet as final:
final String str = "String In doGet()";
Anonymous classes can access only final local variables of an outer method. In Java 8 it was changed.
Also your IDE should assist you with fixes of such errors.

Categories