mirror of
https://bitbucket.org/wisemapping/wisemapping-open-source.git
synced 2024-11-24 23:17:56 +01:00
Improve OAuth error handling.
This commit is contained in:
parent
7918bc20d0
commit
6acde03327
@ -13,8 +13,8 @@
|
|||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<org.springframework.version>5.3.24</org.springframework.version>
|
<org.springframework.version>5.3.26</org.springframework.version>
|
||||||
<org.springframework.addons>5.7.3</org.springframework.addons>
|
<org.springframework.addons>5.7.7</org.springframework.addons>
|
||||||
<hibernate.version>5.6.12.Final</hibernate.version>
|
<hibernate.version>5.6.12.Final</hibernate.version>
|
||||||
<hibernate-validator.version>6.0.21.Final</hibernate-validator.version>
|
<hibernate-validator.version>6.0.21.Final</hibernate-validator.version>
|
||||||
<spring-security-taglibs.version>5.6.1</spring-security-taglibs.version>
|
<spring-security-taglibs.version>5.6.1</spring-security-taglibs.version>
|
||||||
@ -36,7 +36,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.testng</groupId>
|
<groupId>org.testng</groupId>
|
||||||
<artifactId>testng</artifactId>
|
<artifactId>testng</artifactId>
|
||||||
<version>6.9.8</version>
|
<version>7.7.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -54,7 +54,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.postgresql</groupId>
|
<groupId>org.postgresql</groupId>
|
||||||
<artifactId>postgresql</artifactId>
|
<artifactId>postgresql</artifactId>
|
||||||
<version>42.5.1</version>
|
<version>42.5.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.security</groupId>
|
<groupId>org.springframework.security</groupId>
|
||||||
@ -358,7 +358,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<driver>com.mysql.jdbc.Driver</driver>
|
<driver>com.mysql.jdbc.Driver</driver>
|
||||||
<username>root</username>
|
<username>root</username>
|
||||||
<password></password>
|
<password/>
|
||||||
<url>jdbc:mysql://127.0.0.1:3306/?useUnicode=true&characterEncoding=UTF-8</url>
|
<url>jdbc:mysql://127.0.0.1:3306/?useUnicode=true&characterEncoding=UTF-8</url>
|
||||||
<autocommit>false</autocommit>
|
<autocommit>false</autocommit>
|
||||||
<srcFiles>
|
<srcFiles>
|
||||||
@ -556,11 +556,9 @@
|
|||||||
<useTestClasspath>true</useTestClasspath>
|
<useTestClasspath>true</useTestClasspath>
|
||||||
<useTestScope>true</useTestScope>
|
<useTestScope>true</useTestScope>
|
||||||
<scanIntervalSeconds>0</scanIntervalSeconds>
|
<scanIntervalSeconds>0</scanIntervalSeconds>
|
||||||
<daemon>true</daemon>
|
|
||||||
<waitForChild>false</waitForChild>
|
<waitForChild>false</waitForChild>
|
||||||
<maxStartupLines>200</maxStartupLines>
|
<maxStartupLines>200</maxStartupLines>
|
||||||
<jvmArgs>${integrationTestArgLine} -Ddatabase.base.url=${project.build.directory}
|
<jvmArgs>-Ddatabase.base.url=${project.build.directory} -Djetty.port=8080
|
||||||
-Djetty.port=8080
|
|
||||||
</jvmArgs>
|
</jvmArgs>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
@ -570,9 +568,6 @@
|
|||||||
<goals>
|
<goals>
|
||||||
<goal>stop</goal>
|
<goal>stop</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
|
||||||
<stopWait>1</stopWait>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright [2022] [wisemapping]
|
|
||||||
*
|
|
||||||
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
|
||||||
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
|
||||||
* "powered by wisemapping" text requirement on every single page;
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the license at
|
|
||||||
*
|
|
||||||
* http://www.wisemapping.org/license
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.wisemapping.exceptions;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class MultipleSessionsOpenException
|
|
||||||
extends ClientException
|
|
||||||
{
|
|
||||||
private static final String MSG_KEY = "MINDMAP_OUTDATED_BY_YOU";
|
|
||||||
|
|
||||||
public MultipleSessionsOpenException(@NotNull String techInfo)
|
|
||||||
{
|
|
||||||
super(techInfo,Severity.INFO);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected String getMsgBundleKey() {
|
|
||||||
return MSG_KEY;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.wisemapping.exceptions;
|
||||||
|
|
||||||
|
|
||||||
|
import com.wisemapping.service.google.http.HttpInvokerException;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class OAuthAuthenticationException extends WiseMappingException {
|
||||||
|
|
||||||
|
public OAuthAuthenticationException(@NotNull HttpInvokerException exception) {
|
||||||
|
super(exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@
|
|||||||
package com.wisemapping.rest;
|
package com.wisemapping.rest;
|
||||||
|
|
||||||
import com.wisemapping.exceptions.ClientException;
|
import com.wisemapping.exceptions.ClientException;
|
||||||
|
import com.wisemapping.exceptions.OAuthAuthenticationException;
|
||||||
import com.wisemapping.exceptions.Severity;
|
import com.wisemapping.exceptions.Severity;
|
||||||
import com.wisemapping.mail.NotificationService;
|
import com.wisemapping.mail.NotificationService;
|
||||||
import com.wisemapping.model.User;
|
import com.wisemapping.model.User;
|
||||||
@ -38,6 +39,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.lang.reflect.UndeclaredThrowableException;
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@ -95,6 +97,19 @@ public class BaseController {
|
|||||||
return new RestErrors(ex.getMessage(messageSource, locale), ex.getSeverity(), ex.getTechInfo());
|
return new RestErrors(ex.getMessage(messageSource, locale), ex.getSeverity(), ex.getTechInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(OAuthAuthenticationException.class)
|
||||||
|
@ResponseBody
|
||||||
|
public OAuthAuthenticationException handleOAuthErrors(@NotNull OAuthAuthenticationException ex, HttpServletResponse response) {
|
||||||
|
// @todo: Further research needed for this error. No clear why this happens.
|
||||||
|
// Caused by: com.wisemapping.service.http.HttpInvokerException: error invoking https://oauth2.googleapis.com/token, response: {
|
||||||
|
// "error": "invalid_grant",
|
||||||
|
// "error_description": "Bad Request"
|
||||||
|
//}, status: 400
|
||||||
|
//
|
||||||
|
response.setStatus(response.getStatus());
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
|
||||||
@ExceptionHandler(Exception.class)
|
@ExceptionHandler(Exception.class)
|
||||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@ -40,7 +40,7 @@ import javax.servlet.http.HttpSession;
|
|||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
public class Oauth2Controller extends BaseController {
|
public class OAuth2Controller extends BaseController {
|
||||||
@Qualifier("userService")
|
@Qualifier("userService")
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
@ -68,7 +68,7 @@ public class Oauth2Controller extends BaseController {
|
|||||||
@ResponseStatus(value = HttpStatus.OK)
|
@ResponseStatus(value = HttpStatus.OK)
|
||||||
public RestOath2CallbackResponse processGoogleCallback(@NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException {
|
public RestOath2CallbackResponse processGoogleCallback(@NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException {
|
||||||
User user = userService.createUserFromGoogle(code);
|
User user = userService.createUserFromGoogle(code);
|
||||||
if (user.getGoogleSync() != null && user.getGoogleSync().booleanValue()) {
|
if (user.getGoogleSync() != null && user.getGoogleSync()) {
|
||||||
doLogin(request, user.getEmail());
|
doLogin(request, user.getEmail());
|
||||||
}
|
}
|
||||||
RestOath2CallbackResponse response = new RestOath2CallbackResponse();
|
RestOath2CallbackResponse response = new RestOath2CallbackResponse();
|
@ -20,6 +20,7 @@ package com.wisemapping.service;
|
|||||||
|
|
||||||
import com.wisemapping.dao.UserManager;
|
import com.wisemapping.dao.UserManager;
|
||||||
import com.wisemapping.exceptions.InvalidMindmapException;
|
import com.wisemapping.exceptions.InvalidMindmapException;
|
||||||
|
import com.wisemapping.exceptions.OAuthAuthenticationException;
|
||||||
import com.wisemapping.exceptions.WiseMappingException;
|
import com.wisemapping.exceptions.WiseMappingException;
|
||||||
import com.wisemapping.mail.NotificationService;
|
import com.wisemapping.mail.NotificationService;
|
||||||
import com.wisemapping.model.*;
|
import com.wisemapping.model.*;
|
||||||
@ -27,6 +28,7 @@ import com.wisemapping.rest.model.RestResetPasswordAction;
|
|||||||
import com.wisemapping.rest.model.RestResetPasswordResponse;
|
import com.wisemapping.rest.model.RestResetPasswordResponse;
|
||||||
import com.wisemapping.service.google.GoogleAccountBasicData;
|
import com.wisemapping.service.google.GoogleAccountBasicData;
|
||||||
import com.wisemapping.service.google.GoogleService;
|
import com.wisemapping.service.google.GoogleService;
|
||||||
|
import com.wisemapping.service.google.http.HttpInvokerException;
|
||||||
import com.wisemapping.util.VelocityEngineUtils;
|
import com.wisemapping.util.VelocityEngineUtils;
|
||||||
import com.wisemapping.util.VelocityEngineWrapper;
|
import com.wisemapping.util.VelocityEngineWrapper;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -161,8 +163,13 @@ public class UserServiceImpl
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException {
|
public User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException {
|
||||||
|
GoogleAccountBasicData data;
|
||||||
try {
|
try {
|
||||||
GoogleAccountBasicData data = googleService.processCallback(callbackCode);
|
data = googleService.processCallback(callbackCode);
|
||||||
|
} catch (HttpInvokerException e) {
|
||||||
|
throw new OAuthAuthenticationException(e);
|
||||||
|
}
|
||||||
|
|
||||||
User existingUser = userManager.getUserBy(data.getEmail());
|
User existingUser = userManager.getUserBy(data.getEmail());
|
||||||
if (existingUser == null) {
|
if (existingUser == null) {
|
||||||
User newUser = new User();
|
User newUser = new User();
|
||||||
@ -175,7 +182,7 @@ public class UserServiceImpl
|
|||||||
newUser.setGoogleToken(data.getAccessToken());
|
newUser.setGoogleToken(data.getAccessToken());
|
||||||
existingUser = this.createUser(newUser, false, true);
|
existingUser = this.createUser(newUser, false, true);
|
||||||
} else {
|
} else {
|
||||||
// user exists and doesnt have confirmed account linking, I must wait for confirmation
|
// user exists and doesn't have confirmed account linking, I must wait for confirmation
|
||||||
if (existingUser.getGoogleSync() == null) {
|
if (existingUser.getGoogleSync() == null) {
|
||||||
existingUser.setGoogleSync(false);
|
existingUser.setGoogleSync(false);
|
||||||
existingUser.setSyncCode(callbackCode);
|
existingUser.setSyncCode(callbackCode);
|
||||||
@ -185,9 +192,7 @@ public class UserServiceImpl
|
|||||||
|
|
||||||
}
|
}
|
||||||
return existingUser;
|
return existingUser;
|
||||||
} catch (Exception e) {
|
|
||||||
throw new WiseMappingException("Cant create user", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException {
|
public User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException {
|
||||||
@ -207,7 +212,6 @@ public class UserServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Mindmap buildTutorialMindmap(@NotNull String firstName) throws InvalidMindmapException {
|
public Mindmap buildTutorialMindmap(@NotNull String firstName) throws InvalidMindmapException {
|
||||||
//To change body of created methods use File | Settings | File Templates.
|
//To change body of created methods use File | Settings | File Templates.
|
||||||
final Locale locale = LocaleContextHolder.getLocale();
|
final Locale locale = LocaleContextHolder.getLocale();
|
||||||
|
@ -1,17 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2022] [wisemapping]
|
||||||
|
*
|
||||||
|
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
||||||
|
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
||||||
|
* "powered by wisemapping" text requirement on every single page;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the license at
|
||||||
|
*
|
||||||
|
* http://www.wisemapping.org/license
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wisemapping.service.google;
|
package com.wisemapping.service.google;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.wisemapping.service.http.HttpInvoker;
|
import com.wisemapping.service.google.http.HttpInvoker;
|
||||||
import com.wisemapping.service.http.HttpInvokerContentType;
|
import com.wisemapping.service.google.http.HttpInvokerContentType;
|
||||||
import com.wisemapping.service.http.HttpInvokerException;
|
import com.wisemapping.service.google.http.HttpInvokerException;
|
||||||
import com.wisemapping.service.http.HttpMethod;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class GoogleService {
|
public class GoogleService {
|
||||||
@ -83,10 +98,10 @@ public class GoogleService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GoogleAccountBasicData processCallback(String code)
|
public GoogleAccountBasicData processCallback(final String code)
|
||||||
throws HttpInvokerException, JsonMappingException, JsonProcessingException {
|
throws HttpInvokerException {
|
||||||
Map<String, String> body = this.getOptinConfirmBody(code);
|
Map<String, String> body = this.getOptinConfirmBody(code);
|
||||||
JsonNode optinConfirmResponse = httpInvoker.invoke(
|
JsonNode optionConfirmResponse = httpInvoker.invoke(
|
||||||
optinConfirmUrl,
|
optinConfirmUrl,
|
||||||
HttpInvokerContentType.FORM_ENCODED,
|
HttpInvokerContentType.FORM_ENCODED,
|
||||||
HttpMethod.POST,
|
HttpMethod.POST,
|
||||||
@ -94,8 +109,8 @@ public class GoogleService {
|
|||||||
null,
|
null,
|
||||||
body);
|
body);
|
||||||
|
|
||||||
String accessToken = getNodeAsString(optinConfirmResponse, "access_token");
|
final String accessToken = getNodeAsString(optionConfirmResponse, "access_token");
|
||||||
String refreshToken = getNodeAsString(optinConfirmResponse, "refresh_token");
|
final String refreshToken = getNodeAsString(optionConfirmResponse, "refresh_token");
|
||||||
|
|
||||||
GoogleAccountBasicData data = this.getAccountBasicData(accessToken);
|
GoogleAccountBasicData data = this.getAccountBasicData(accessToken);
|
||||||
data.setAccessToken(accessToken);
|
data.setAccessToken(accessToken);
|
||||||
|
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2022] [wisemapping]
|
||||||
|
*
|
||||||
|
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
||||||
|
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
||||||
|
* "powered by wisemapping" text requirement on every single page;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the license at
|
||||||
|
*
|
||||||
|
* http://www.wisemapping.org/license
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.wisemapping.service.google.http;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.apache.http.NameValuePair;
|
||||||
|
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpDelete;
|
||||||
|
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.methods.HttpPut;
|
||||||
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class HttpInvoker {
|
||||||
|
|
||||||
|
protected static Logger logger = LogManager.getLogger(HttpInvoker.class);
|
||||||
|
|
||||||
|
private final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
public HttpInvoker() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonNode invoke(
|
||||||
|
@NotNull String url,
|
||||||
|
HttpInvokerContentType requestContentType,
|
||||||
|
HttpMethod method,
|
||||||
|
Map<String, String> headers,
|
||||||
|
String jsonPayload,
|
||||||
|
Map<String, String> formData)
|
||||||
|
throws HttpInvokerException {
|
||||||
|
String responseBody = null;
|
||||||
|
try {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("finalUrl: " + url);
|
||||||
|
logger.debug("method: " + method);
|
||||||
|
logger.debug("payload: " + jsonPayload);
|
||||||
|
logger.debug("header: " + headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||||
|
HttpRequestBase httpRequest;
|
||||||
|
|
||||||
|
// build request
|
||||||
|
if (method.equals(HttpMethod.POST))
|
||||||
|
httpRequest = new HttpPost(url);
|
||||||
|
else if (method.equals(HttpMethod.PUT))
|
||||||
|
httpRequest = new HttpPut(url);
|
||||||
|
else if (method.equals(HttpMethod.GET))
|
||||||
|
httpRequest = new HttpGet(url);
|
||||||
|
else if (method.equals(HttpMethod.DELETE))
|
||||||
|
httpRequest = new HttpDelete(url);
|
||||||
|
else
|
||||||
|
throw new HttpInvokerException("Method " + method + " not supported by http connector");
|
||||||
|
|
||||||
|
if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) {
|
||||||
|
HttpEntity entity = null;
|
||||||
|
if (requestContentType.equals(HttpInvokerContentType.JSON)) {
|
||||||
|
if (jsonPayload == null)
|
||||||
|
throw new HttpInvokerException("Json content is required");
|
||||||
|
entity = new StringEntity(jsonPayload, StandardCharsets.UTF_8);
|
||||||
|
((HttpEntityEnclosingRequestBase) httpRequest).setEntity(entity);
|
||||||
|
}
|
||||||
|
if (requestContentType.equals(HttpInvokerContentType.FORM_ENCODED)) {
|
||||||
|
List<NameValuePair> nameValuePairs = new ArrayList<>();
|
||||||
|
Set<String> keys = formData.keySet();
|
||||||
|
for (String key : keys) {
|
||||||
|
nameValuePairs.add(new BasicNameValuePair(key, formData.get(key).toString()));
|
||||||
|
}
|
||||||
|
entity = new UrlEncodedFormEntity(nameValuePairs);
|
||||||
|
((HttpEntityEnclosingRequestBase) httpRequest).setEntity(entity);
|
||||||
|
}
|
||||||
|
if (entity == null)
|
||||||
|
throw new HttpInvokerException("Cant build entity to send");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers != null) {
|
||||||
|
Set<String> keys = headers.keySet();
|
||||||
|
for (String key : keys) {
|
||||||
|
httpRequest.setHeader(key, headers.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestContentType != null)
|
||||||
|
httpRequest.setHeader("Content-Type", requestContentType.getHttpContentType());
|
||||||
|
|
||||||
|
// invoke
|
||||||
|
CloseableHttpResponse response = httpClient.execute(httpRequest);
|
||||||
|
// response process
|
||||||
|
JsonNode root = null;
|
||||||
|
responseBody = response.getEntity() != null && response.getEntity().getContent() != null
|
||||||
|
? IOUtils.toString(response.getEntity().getContent(), (String) null)
|
||||||
|
: null;
|
||||||
|
if (responseBody != null) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("response plain: " + responseBody);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
root = mapper.readTree(responseBody);
|
||||||
|
} catch (Exception e) {
|
||||||
|
int returnCode = response.getStatusLine().getStatusCode();
|
||||||
|
throw new HttpInvokerException("cant transform response to JSON. RQ: " + jsonPayload + ", RS: "
|
||||||
|
+ responseBody + ", status: " + returnCode, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getStatusLine().getStatusCode() >= 400) {
|
||||||
|
logger.error("error response: " + responseBody);
|
||||||
|
throw new HttpInvokerException("error invoking " + url + ", response: " + responseBody + ", status: "
|
||||||
|
+ response.getStatusLine().getStatusCode(), HttpStatus.SC_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
httpRequest.releaseConnection();
|
||||||
|
response.close();
|
||||||
|
httpClient.close();
|
||||||
|
|
||||||
|
return root;
|
||||||
|
} catch (HttpInvokerException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("cant invoke service " + url);
|
||||||
|
logger.error("response: " + responseBody, e);
|
||||||
|
throw new HttpInvokerException("cant invoke service " + url, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.wisemapping.service.http;
|
package com.wisemapping.service.google.http;
|
||||||
|
|
||||||
public enum HttpInvokerContentType {
|
public enum HttpInvokerContentType {
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ public enum HttpInvokerContentType {
|
|||||||
|
|
||||||
private String httpContentType;
|
private String httpContentType;
|
||||||
|
|
||||||
private HttpInvokerContentType(String type) {
|
HttpInvokerContentType(String type) {
|
||||||
this.httpContentType = type;
|
this.httpContentType = type;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2022] [wisemapping]
|
||||||
|
*
|
||||||
|
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
||||||
|
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
||||||
|
* "powered by wisemapping" text requirement on every single page;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the license at
|
||||||
|
*
|
||||||
|
* http://www.wisemapping.org/license
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.wisemapping.service.google.http;
|
||||||
|
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
|
public class HttpInvokerException extends Exception {
|
||||||
|
private final int statusCode;
|
||||||
|
|
||||||
|
public HttpInvokerException(String message) {
|
||||||
|
this(message, HttpStatus.SC_INTERNAL_SERVER_ERROR, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpInvokerException(String message, int statusCode) {
|
||||||
|
this(message, statusCode, null);
|
||||||
|
}
|
||||||
|
public HttpInvokerException(String message, Throwable cause) {
|
||||||
|
this(message, HttpStatus.SC_INTERNAL_SERVER_ERROR, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpInvokerException(String message, int statusCode, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStatusCode() {
|
||||||
|
return statusCode;
|
||||||
|
}
|
||||||
|
}
|
@ -1,147 +0,0 @@
|
|||||||
package com.wisemapping.service.http;
|
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.apache.http.HttpEntity;
|
|
||||||
import org.apache.http.NameValuePair;
|
|
||||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
|
||||||
import org.apache.http.client.methods.HttpDelete;
|
|
||||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
|
||||||
import org.apache.http.client.methods.HttpGet;
|
|
||||||
import org.apache.http.client.methods.HttpPost;
|
|
||||||
import org.apache.http.client.methods.HttpPut;
|
|
||||||
import org.apache.http.client.methods.HttpRequestBase;
|
|
||||||
import org.apache.http.entity.StringEntity;
|
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
|
||||||
import org.apache.http.impl.client.HttpClients;
|
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class HttpInvoker {
|
|
||||||
|
|
||||||
protected static Logger logger = LogManager.getLogger(HttpInvoker.class);
|
|
||||||
|
|
||||||
private ObjectMapper mapper = new ObjectMapper();
|
|
||||||
|
|
||||||
public HttpInvoker() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonNode invoke(
|
|
||||||
String url,
|
|
||||||
HttpInvokerContentType requestContentType,
|
|
||||||
HttpMethod method,
|
|
||||||
Map<String, String> headers,
|
|
||||||
String jsonPayload,
|
|
||||||
Map<String, String> formData)
|
|
||||||
throws HttpInvokerException {
|
|
||||||
String responseBody = null;
|
|
||||||
try {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("finalUrl: " + url);
|
|
||||||
logger.debug("method: " + method);
|
|
||||||
logger.debug("payload: " + jsonPayload);
|
|
||||||
logger.debug("header: " + headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
|
||||||
HttpRequestBase httpRequst = null;
|
|
||||||
|
|
||||||
// build request
|
|
||||||
if (method.equals(HttpMethod.POST))
|
|
||||||
httpRequst = new HttpPost(url);
|
|
||||||
else if (method.equals(HttpMethod.PUT))
|
|
||||||
httpRequst = new HttpPut(url);
|
|
||||||
else if (method.equals(HttpMethod.GET))
|
|
||||||
httpRequst = new HttpGet(url);
|
|
||||||
else if (method.equals(HttpMethod.DELETE))
|
|
||||||
httpRequst = new HttpDelete(url);
|
|
||||||
else
|
|
||||||
throw new HttpInvokerException("Method " + method + " not suppoprted by http connector");
|
|
||||||
|
|
||||||
if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) {
|
|
||||||
HttpEntity entity = null;
|
|
||||||
if (requestContentType.equals(HttpInvokerContentType.JSON)) {
|
|
||||||
if (jsonPayload == null)
|
|
||||||
throw new HttpInvokerException("Json content is required");
|
|
||||||
entity = new StringEntity(jsonPayload, Charset.forName("UTF-8"));
|
|
||||||
((HttpEntityEnclosingRequestBase) httpRequst).setEntity(entity);
|
|
||||||
}
|
|
||||||
if (requestContentType.equals(HttpInvokerContentType.FORM_ENCODED)) {
|
|
||||||
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
|
|
||||||
Set<String> keys = formData.keySet();
|
|
||||||
for (String key : keys) {
|
|
||||||
nameValuePairs.add(new BasicNameValuePair(key, formData.get(key).toString()));
|
|
||||||
}
|
|
||||||
entity = new UrlEncodedFormEntity(nameValuePairs);
|
|
||||||
((HttpEntityEnclosingRequestBase) httpRequst).setEntity(entity);
|
|
||||||
}
|
|
||||||
if (entity == null)
|
|
||||||
throw new HttpInvokerException("Cant build entity to send");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headers != null) {
|
|
||||||
Set<String> keys = headers.keySet();
|
|
||||||
for (String key : keys) {
|
|
||||||
httpRequst.setHeader(key, headers.get(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestContentType != null)
|
|
||||||
httpRequst.setHeader("Content-Type", requestContentType.getHttpContentType());
|
|
||||||
|
|
||||||
// invoke
|
|
||||||
CloseableHttpResponse response = httpClient.execute(httpRequst);
|
|
||||||
// response process
|
|
||||||
JsonNode root = null;
|
|
||||||
responseBody = response.getEntity() != null && response.getEntity().getContent() != null
|
|
||||||
? IOUtils.toString(response.getEntity().getContent(), (String) null)
|
|
||||||
: null;
|
|
||||||
if (responseBody != null) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("response plain: " + responseBody);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
root = mapper.readTree(responseBody);
|
|
||||||
} catch (Exception e) {
|
|
||||||
int returnCode = response.getStatusLine().getStatusCode();
|
|
||||||
throw new HttpInvokerException("cant transform response to JSON. RQ: " + jsonPayload + ", RS: "
|
|
||||||
+ responseBody + ", status: " + returnCode, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode() >= 400) {
|
|
||||||
logger.error("error response: " + responseBody);
|
|
||||||
throw new HttpInvokerException("error invoking " + url + ", response: " + responseBody + ", status: "
|
|
||||||
+ response.getStatusLine().getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
httpRequst.releaseConnection();
|
|
||||||
response.close();
|
|
||||||
httpClient.close();
|
|
||||||
|
|
||||||
return root;
|
|
||||||
} catch (HttpInvokerException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("cant invoke service " + url);
|
|
||||||
logger.error("response: " + responseBody, e);
|
|
||||||
throw new HttpInvokerException("cant invoke service " + url, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package com.wisemapping.service.http;
|
|
||||||
|
|
||||||
public class HttpInvokerException extends Exception {
|
|
||||||
|
|
||||||
public HttpInvokerException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpInvokerException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package com.wisemapping.service.http;
|
|
||||||
|
|
||||||
public enum HttpMethod {
|
|
||||||
POST, GET, DELETE, PUT
|
|
||||||
}
|
|
@ -41,7 +41,6 @@ ACCESS_HAS_BEEN_REVOKED=Deine Zugriffsrechte auf diese Mindmap sind zur
|
|||||||
MAP_CAN_NOT_BE_FOUND=Die Mindmap kann nicht gefunden werden. Sie muss gelöscht worden sein.
|
MAP_CAN_NOT_BE_FOUND=Die Mindmap kann nicht gefunden werden. Sie muss gelöscht worden sein.
|
||||||
LABEL_CAN_NOT_BE_FOUND=Das Label kann nicht gefunden werden. Es muss gelöscht worden sein.
|
LABEL_CAN_NOT_BE_FOUND=Das Label kann nicht gefunden werden. Es muss gelöscht worden sein.
|
||||||
MINDMAP_TIMESTAMP_OUTDATED=Es ist nicht möglich, deine Änderungen zu speichern, da deine Mindmap von {0} geändert wurde. Aktualisiere die Seite und versuche es erneut.
|
MINDMAP_TIMESTAMP_OUTDATED=Es ist nicht möglich, deine Änderungen zu speichern, da deine Mindmap von {0} geändert wurde. Aktualisiere die Seite und versuche es erneut.
|
||||||
MINDMAP_OUTDATED_BY_YOU=Deine Änderungen können nicht gespeichert werden, da die Mindmap veraltet ist. Hast du mehrere Tabs geöffnet? Aktualisiere die Seite und versuche es erneut.
|
|
||||||
MINDMAP_LOCKED=Mindmap wird bearbeitet von {0} <{1}>. Die Mindmap wird im schreibgeschützten Modus geöffnet.
|
MINDMAP_LOCKED=Mindmap wird bearbeitet von {0} <{1}>. Die Mindmap wird im schreibgeschützten Modus geöffnet.
|
||||||
MINDMAP_IS_LOCKED=Mindmap ist für die Bearbeitung gesperrt.
|
MINDMAP_IS_LOCKED=Mindmap ist für die Bearbeitung gesperrt.
|
||||||
# Confirmed
|
# Confirmed
|
||||||
|
@ -42,7 +42,6 @@ ACCESS_HAS_BEEN_REVOKED=Your access permissions to this map has been revoked. Co
|
|||||||
MAP_CAN_NOT_BE_FOUND=The map can not be found. It must have been deleted.
|
MAP_CAN_NOT_BE_FOUND=The map can not be found. It must have been deleted.
|
||||||
LABEL_CAN_NOT_BE_FOUND=The label can not be found. It must have been deleted.
|
LABEL_CAN_NOT_BE_FOUND=The label can not be found. It must have been deleted.
|
||||||
MINDMAP_TIMESTAMP_OUTDATED=It's not possible to save your changes because your mind map has been modified by ''{0}''. Refresh the page and try again.
|
MINDMAP_TIMESTAMP_OUTDATED=It's not possible to save your changes because your mind map has been modified by ''{0}''. Refresh the page and try again.
|
||||||
MINDMAP_OUTDATED_BY_YOU=It's not possible to save your changes because map is out of date. Do you have multiple tabs opened ?. Refresh the page and try again.
|
|
||||||
MINDMAP_LOCKED=Map is being edited by {0} <{1}>. Map is opened in read only mode.
|
MINDMAP_LOCKED=Map is being edited by {0} <{1}>. Map is opened in read only mode.
|
||||||
MINDMAP_IS_LOCKED=Min map is locked for edition.
|
MINDMAP_IS_LOCKED=Min map is locked for edition.
|
||||||
# Confirmed
|
# Confirmed
|
||||||
|
@ -41,7 +41,6 @@ ACCESS_HAS_BEEN_REVOKED = Los permisos de acceso al mapa han sido revocados. No
|
|||||||
MAP_CAN_NOT_BE_FOUND = No se puede encontrar el mapa. Debe haber sido borrado.
|
MAP_CAN_NOT_BE_FOUND = No se puede encontrar el mapa. Debe haber sido borrado.
|
||||||
LABEL_CAN_NOT_BE_FOUND = No se puede encontrar la etiqueta. Debe haber sido borrado.
|
LABEL_CAN_NOT_BE_FOUND = No se puede encontrar la etiqueta. Debe haber sido borrado.
|
||||||
MINDMAP_TIMESTAMP_OUTDATED = No es posible grabar sus cambios por que el mapa ha sido modificado por {0}''. Refresque la pagina y intentelo nuevamente.
|
MINDMAP_TIMESTAMP_OUTDATED = No es posible grabar sus cambios por que el mapa ha sido modificado por {0}''. Refresque la pagina y intentelo nuevamente.
|
||||||
MINDMAP_OUTDATED_BY_YOU = No es posible guardar los cambios porque el mapa no está actualizado. ¿Tienes varias pestañas abiertas?. Actualice la página y vuelva a intentarlo.
|
|
||||||
MINDMAP_LOCKED = El mapa esta siendo editado por {0} <{1}>. Mapa sera abierto en modo lectura.
|
MINDMAP_LOCKED = El mapa esta siendo editado por {0} <{1}>. Mapa sera abierto en modo lectura.
|
||||||
MINDMAP_IS_LOCKED = Mindmap está bloqueado para la edición.
|
MINDMAP_IS_LOCKED = Mindmap está bloqueado para la edición.
|
||||||
# Confirmed
|
# Confirmed
|
||||||
|
@ -41,7 +41,6 @@ ACCESS_HAS_BEEN_REVOKED=Vos autorisations d'accès à cette carte ont été rév
|
|||||||
MAP_CAN_NOT_BE_FOUND=La carte est introuvable. Il a dû être supprimé.
|
MAP_CAN_NOT_BE_FOUND=La carte est introuvable. Il a dû être supprimé.
|
||||||
LABEL_CAN_NOT_BE_FOUND=L'étiquette est introuvable. Il a dû être supprimé.
|
LABEL_CAN_NOT_BE_FOUND=L'étiquette est introuvable. Il a dû être supprimé.
|
||||||
MINDMAP_TIMESTAMP_OUTDATED=Il n''est pas possible d''enregistrer vos modifications car votre mindmap a été modifiée par ''{0}''. Actualisez la page et réessayez.
|
MINDMAP_TIMESTAMP_OUTDATED=Il n''est pas possible d''enregistrer vos modifications car votre mindmap a été modifiée par ''{0}''. Actualisez la page et réessayez.
|
||||||
MINDMAP_OUTDATED_BY_YOU=Il n'est pas possible d'enregistrer vos modifications car la carte n'est pas à jour. Avez-vous plusieurs onglets ouverts ?. Actualisez la page et réessayez.
|
|
||||||
MINDMAP_LOCKED=La carte est en cours de modification par {0} <{1}>. La carte est ouverte en mode lecture seule.
|
MINDMAP_LOCKED=La carte est en cours de modification par {0} <{1}>. La carte est ouverte en mode lecture seule.
|
||||||
MINDMAP_IS_LOCKED=Mindmap est verrouillé pour l'édition.
|
MINDMAP_IS_LOCKED=Mindmap est verrouillé pour l'édition.
|
||||||
# Confirmed
|
# Confirmed
|
||||||
|
@ -41,7 +41,6 @@ ACCESS_HAS_BEEN_REVOKED=Ваш доступ к карте был отозван.
|
|||||||
MAP_CAN_NOT_BE_FOUND=Карта не найдена. Вероятно, она удалена.
|
MAP_CAN_NOT_BE_FOUND=Карта не найдена. Вероятно, она удалена.
|
||||||
LABEL_CAN_NOT_BE_FOUND=Метка не найдена. Вероятно, она удалена.
|
LABEL_CAN_NOT_BE_FOUND=Метка не найдена. Вероятно, она удалена.
|
||||||
MINDMAP_TIMESTAMP_OUTDATED=Невозможно сохранить, карта была изменена ''{0}''. Обновите страницу и попробуйте еще раз.
|
MINDMAP_TIMESTAMP_OUTDATED=Невозможно сохранить, карта была изменена ''{0}''. Обновите страницу и попробуйте еще раз.
|
||||||
MINDMAP_OUTDATED_BY_YOU=Невозможно сохранить - карта устарела. У вас открыто несколько вкладок браузера?. Обновите страницу и попробуйте еще раз.
|
|
||||||
MINDMAP_LOCKED=Карта редактируется {0} <{1}>. Карта открыта в режиме чтения.
|
MINDMAP_LOCKED=Карта редактируется {0} <{1}>. Карта открыта в режиме чтения.
|
||||||
MINDMAP_IS_LOCKED=Карта доступна только для просмотра.
|
MINDMAP_IS_LOCKED=Карта доступна только для просмотра.
|
||||||
# Confirmed
|
# Confirmed
|
||||||
|
@ -41,7 +41,6 @@ ACCESS_HAS_BEEN_REVOKED=您对该脑图的访问权限已被撤销。联系脑
|
|||||||
MAP_CAN_NOT_BE_FOUND=找不到该脑图,应该是被删除了。
|
MAP_CAN_NOT_BE_FOUND=找不到该脑图,应该是被删除了。
|
||||||
LABEL_CAN_NOT_BE_FOUND=找不到该标签,应该是被删除了。
|
LABEL_CAN_NOT_BE_FOUND=找不到该标签,应该是被删除了。
|
||||||
MINDMAP_TIMESTAMP_OUTDATED=无法保存您的更改,因为您的思维导图已被''{0}''修改。刷新页面,然后重试。
|
MINDMAP_TIMESTAMP_OUTDATED=无法保存您的更改,因为您的思维导图已被''{0}''修改。刷新页面,然后重试。
|
||||||
MINDMAP_OUTDATED_BY_YOU=无法保存您的更改,因为脑图已经过期。您打开了多个页面吗?刷新页面,然后重试。
|
|
||||||
MINDMAP_LOCKED=脑图正在被{0}<{1}>编辑。脑图以只读模式打开。
|
MINDMAP_LOCKED=脑图正在被{0}<{1}>编辑。脑图以只读模式打开。
|
||||||
MINDMAP_IS_LOCKED=脑图被锁定编辑。
|
MINDMAP_IS_LOCKED=脑图被锁定编辑。
|
||||||
# Confirmed
|
# Confirmed
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<property name="velocityEngineWrapper" ref="velocityEngineWrapper"/>
|
<property name="velocityEngineWrapper" ref="velocityEngineWrapper"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="httpInvoker" class="com.wisemapping.service.http.HttpInvoker">
|
<bean id="httpInvoker" class="com.wisemapping.service.google.http.HttpInvoker">
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="googleService" class="com.wisemapping.service.google.GoogleService">
|
<bean id="googleService" class="com.wisemapping.service.google.GoogleService">
|
||||||
|
Loading…
Reference in New Issue
Block a user