I have these classes:
public class OfferList {
public List<Offer> offers;
}
public class Offer {
public String offer_id;
public String group_id;
public String n_hours;
public boolean is_new;
}
And defined this service:
#Rest(rootUrl = "http://MYURL/json", converters = { GsonHttpMessageConverter.class })
//if defined, the url will be added as a prefix to every request
public interface EscolasPertoDeMimRESTService {
#Get("/offers/{user_id}/{date}")
#Accept(MediaType.APPLICATION_JSON)
OfferList getOffers(String user_id, long date);
}
Which is called here:
(...)
#RestService
OfferRestService offersService;
(...)
#Background
void loadListItems() {
mLoadingOffers = true;
showProgressDialog();
OfferList ol = null;
try {
ol = offersService.getOffers(myPrefs.userID().get(), myPrefs.lastCheckedForOffers().get());
showDebug("ol.offers.size(): " + ol.offers.size()); //OK shows ol.offers.size(): 24
Offer o = ol.offers.get(0); //After exporting: Crash
showDebug("ol.offers[0].group_id" + o.group_id);
} catch (Exception e) {
showDebug(e.getMessage()); //After exporting shows: "com.google.gson.internal.StringMap cannot be cast to com.humihara.escolaspertodemim.Offer"
}
setupAdapter(ol);
mLoadingOffers = false;
}
(...)
Everything works fine in debug, but when I export and sign, the app crashes.
The GET is sent and the response from the server is a valid JSON offer list.
Apparently the result I get is an OfferList with StringMap(s) instead of Offer(s).
This is my proguard-project.txt (I'm using the android sdk defaults plus these):
-keep public class com.humihara.escolaspertodemim.** { *; }
-keep public class org.springframework.** { *; }
-keep public class com.google.gson.** { *; }
-keep public class com.google.ads.** { *; }
-keep public class com.androidannotations.** { *; }
-keep public class org.acra.** { *; }
-keep public class * extends android.support.v4.app.FragmentActivity
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet); }
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int); }
-dontwarn org.simpleframework.**
-dontnote org.simpleframework.**
-dontwarn org.codehaus.jackson.**
-dontnote org.codehaus.jackson.**
-dontwarn com.fasterxml.jackson.**
-dontnote com.fasterxml.jackson.**
-dontwarn com.google.code.rome.**
-dontnote com.google.code.rome.**
-dontwarn org.apache.commons.httpclient.**
-dontnote org.apache.commons.httpclient.**
I have searched the mailing list plus stackoverflow where I picked up the extra definitions for proguard, but now I don't know what else to do.
Can someone please tell me what I must change in my code or proguard-project.txt to make it work?
UPDATE:
After checking Jackson Json parser returns nulls on all object members on Android after optimizing with Proguard
I guess I was missing:
-dontskipnonpubliclibraryclassmembers
The default android proguard defines:
-dontskipnonpubliclibraryclasses
and I didn't noticed they aren't the same expression.
I ended up with this proguard-project.txt:
-keepattributes Signature,RuntimeVisibleAnnotations,AnnotationDefault
-dontskipnonpubliclibraryclassmembers
-keep class com.humihara.escolaspertodemim.** { *; }
-keep public class * extends android.support.v4.app.FragmentActivity
-dontwarn org.simpleframework.**
-dontnote org.simpleframework.**
-dontwarn org.codehaus.jackson.**
-dontnote org.codehaus.jackson.**
-dontwarn com.fasterxml.jackson.**
-dontnote com.fasterxml.jackson.**
-dontwarn com.google.code.rome.**
-dontnote com.google.code.rome.**
-dontwarn org.apache.commons.httpclient.**
-dontnote org.apache.commons.httpclient.**
And now everything works fine.
For code that uses reflection to access annotations, you should preserve the annotations:
-keepattributes *Annotation*
Related
I am using IntelliJ and Gradle for a sample project. There are two modules.
demo-core module
It has entity and repository classes. build.gradle file is like the below.
apply plugin: 'java'
group 'com.example'
version '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'com.h2database:h2:1.4.200'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.3'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
test {
useJUnitPlatform()
}
I added CustomerRepository class for Customer entity in demo-core module.
package example.springboot.entity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}
demo-web module
This is a web module and uses the repository interface like the below.
#SpringBootApplication
public class DemoWebApp {
public static void main(String[] args) {
SpringApplication.run(DemoWebApp.class, args);
}
#Bean
public CommandLineRunner demo(CustomerRepository repository) {
return (args) -> {
// save a few customers
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
repository.save(new Customer("Kim", "Bauer"));
repository.save(new Customer("David", "Palmer"));
repository.save(new Customer("Michelle", "Dessler"));
};
}
}
This is build.gradle file for demo-web module.
plugins {
id 'org.springframework.boot' version '2.5.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group 'com.example'
version '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation project(':demo-core')
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
test {
useJUnitPlatform()
}
but I can't use JpaRepsitory methods with this error.
/Users/dgkim/Downloads/demo/demo-web/src/main/java/example/springboot/DemoWebApp.java:19: error: cannot access JpaRepository
repository.save(new Customer("Jack", "Bauer"));
^
class file for org.springframework.data.jpa.repository.JpaRepository not found
So, I created a new interface wrapping the CustomerRepository in demo-core module like this.
#Service
public class CustomerFinder {
#Autowired
private CustomerRepository customerRepository;
public Optional<Customer> findCustomer(Long id) {
return customerRepository.findById(id);
}
}
My Controller class uses the wrapper interface like the below.
#RestController
public class CustomerController {
#Autowired
private CustomerFinder finder;
#GetMapping("/customer/{id}")
public String customer(#PathVariable Long id) {
Optional<Customer> customerOptional = finder.findCustomer(id);
if(customerOptional.isPresent()) return "find customer. " + customerOptional.get().getLastName();
else return "no entity";
}
}
It works. JpaRepository methods can be accessed in the same module but demo-web module that has a dependency on demo-core can not access it. DemoWebApp class can access CustomerRepository interface itself but can not access the super interface (JpaRepository).
How can I resolve this issue?
try to change core module dependencies from implementation to api
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.3'
to
api 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.3'
You can see the difference between api and implementation here
I am running jee-2019-06 version of Eclipse. Here is my Model-Bean-Facade structure:
I am not including getters/setters for brevity.
My Identifiable:
/** Identifiable interface for Entities; used for DAO - Service transitions. */
public interface Identifiable<T extends Serializable> extends Serializable {
public T getId(); // identifiable field
public String getTitle(); // user friendly name (maybe different from actual entity's name)
public String getName(); // every entity has a name
public String getDescription(); // every entity should have a description
}
My Abstract Bean:
public abstract class AbstractBean<T extends Identifiable<?>> {
protected final transient Logger log = Logger.getLogger(this.getClass());
private final Class<T> clazz;
private T model;
public AbstractBean(final Class<T> clazz) {
this.clazz = clazz;
}
protected T createInstance() {
try {
return this.clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
this.log.error("[" + this.getClass().getSimpleName() + ".createInstance()] : Error : {} {}", e.getMessage(), e);
return null;
}
}
protected AbstractFacade<T> getFacade() {
return null;
}
}
My Abstract Facade:
#Transactional
public abstract class AbstractFacade<T extends Identifiable<?>> {
protected final transient Logger log = Logger.getLogger(this.getClass());
protected final Class<T> clazz;
public AbstractFacade(final Class<T> clazz) {
this.clazz = clazz;
}
}
My Bean:
#Named
#ViewScoped
public class CarBean extends AbstractBean<Car> {
#Inject
private CarFacade facade;
public CarBean(){
super(Car.class);
}
#Override
public CarFacade getFacade() {
return this.facade;
}
}
My AbstractEntity:
#MappedSuperclass
public abstract class AbstractEntity implements Identifiable<Integer> {
private Integer id;
private String name;
private String description;
public AbstractEntity() {
}
}
My Entity:
public class Car extends AbstractEntity {
public Car() {
}
}
I have no problems in showing the value to the user.
I have problems in validation and hyperlink in Eclipse:
<h:outputText value="#{carBean.model.name}" />
Facelet validator cannot validate name of model. It yellow underlines name. Also, I cannot Ctrl + click to activate hyperlink on name.
I saw on another developer's eclipse that both of my problems were not issues at all. I compared all the tools installed in both Eclipses and could not find anything relevant.
My question: what tools do I have to install or what settings/adjustments am I missing?
Please note: I do not want to disable the validator and I want to be able to hyperlink fields in facelet so that I will access the field using Ctrl + click.
Thank you.
This is my param converter
import org.springframework.data.domain.Sort;
public class MyParamConverter implements ParamConverter<Sort> {
#Override
public Sort fromString(String s){
return new Sort(new Sort.Order(Sort.Direction.ASC, "ds"));
}
#Override
public String toString(Sort mo){
return mo.toString();
}
}
this is my paramconverter provider
#Provider
public class MyParamConverterProvider implements ParamConverterProvider {
#Override
public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
if(rawType.equals(Sort.class)){
return (ParamConverter<T>) new MyParamConverter();
}
return null;
}
I am trying to use in my API as
#GET
#Path("/")
Response read(#QueryParam("sort") Sort order);
I am expecting the jax to map string that I pass in my url e.g. &sort="asc" to Sort object. But I am getting an compile time error that have a registered implementation of paramconverter provider. I need to find a way when I pass a query param as &sort="somethung" it gets convert to SORT automatically either by using custom annotation or by using Param Converter.
with reference to your comment, try registering your provider like:
#ApplicationPath("/")
public class MyApplication extends ResourceConfig {
#Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(MyParamConverterProvider.class);
return classes;
}
}
or, if you are using Jersey
#ApplicationPath("/")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("my.package");
// or without package scanning
register(MyParamConverterProvider.class);
}
}
I have an application which uses social authentication (Twitter and Facebook). Before publishing it in app store I used proguard to obfuscate my code. All features work fine except authentication of Facebook and Twitter. As soon as I click on Login with facebook or login with twitter button the application crashes with message Unfortunately "APP Name" has stopped.
My twitter authentication class extends Activity and TwiterProfile.java class extends fragments.
here is my proguard configuration
-injars bin/classes
-injars libs
-outjars bin/classes-processed.jar
-libraryjars libs\ksoap2-android-assembly-2.4-jar-with-dependencies.jar
-libraryjars libs\twitter4j-core-4.0.1.jar
-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
-keepattributes *Annotation*
-dontwarn twitter4j.**
-dontwarn org.xmlpull.v1.**
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.support.v4.app.ListFragment
-keep public class * extends android.app.Fragment
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.content.Context {
public void *(android.view.View);
public void *(android.view.MenuItem);
}
-keepclassmembers class * implements android.os.Parcelable {
static ** CREATOR;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
-keepclassmembers class * {
#android.webkit.JavascriptInterface <methods>;
}
Here is twitterauthentication.java which is called from TwitterProfile.java class
public class TwitterAuthenticate extends Activity{
private WebView webView;
public static String EXTRA_URL = "extra_url";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
setTitle("Twitter");
final String url = this.getIntent().getStringExtra(EXTRA_URL);
if (null == url) {
Log.e("Twitter", "URL cannot be null");
finish();
}
webView = (WebView) findViewById(R.id.webView);
webView.setWebViewClient(new MyWebViewClient());
webView.loadUrl(url);
}
class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains(getResources().getString(R.string.twitter_callback))) {
Uri uri = Uri.parse(url);
/* Sending results back */
String verifier = uri.getQueryParameter(getString(R.string.twitter_oauth_verifier));
Intent resultIntent = new Intent();
resultIntent.putExtra(getString(R.string.twitter_oauth_verifier), verifier);
setResult(RESULT_OK, resultIntent);
/* closing webview */
finish();
return true;
}
return false;
}
}
}
I have a class JSBridge (an inner class) which is a javascript interface:
private class JsBridge implements JsCallback {
/**
* #param handlerName method required
* #param jsonData data passed through from javascript
* #param jsCallback A callback to trigger when handler specified by handlername has finished, could be null
*/
#JavascriptInterface
public void callHandler(final String handlerName, final String jsonData, final String jsCallback) {
Log.d(App.TAG, "Bridge call from JS, received " + handlerName);
}
#JavascriptInterface
public void onPageLoad(final String pageName) {
Log.d(App.TAG, "Bridge call from JS, received onPageLoad - we have the page name " + pageName);
}
This works fine until I do a release build with proguard. I've tried following some other SO answers and have added the following lines to my proguard file, but it has not helped. The result is the debug version I get the callbacks, the release version I get no callbacks.
-keep public class * implements com.mixcloud.player.view.JsCallback
-keepclassmembers class * implements com.mixcloud.player.view.JsCallback {
<methods>;
}
-keep public class * implements com.mixcloud.player.view.JsCallback
-keepattributes *Annotation*
-keepattributes JavascriptInterface
-keep public class com.mixcloud.player.view.JSRefreshWebView
-keep public class com.mixcloud.player.view.JSRefreshWebView$JsBridge
-keep public class * implements com.mixcloud.player.view.JSRefreshWebView$JsBridge
-keepclassmembers class * implements com.mixcloud.player.view.JSRefreshWebView$JsBridge {
<methods>;
}
If your Javascript interface methods are annotated with #JavascriptInterface, you can preserve them with
-keepclassmembers class * {
#android.webkit.JavascriptInterface <methods>;
}