Custom JWT expiration in micronaut security - java

The expiration time of a JWT can be set by configuring micronaut.security.token.jwt.generator.access-token-expiration.
Is it possible to have a custom value for individually issued JWT tokens? Searching the documentation I haven't found any useful information except you can replace the BearerTokenRenderer and return a custom response.
public AccessRefreshToken render(Integer expiresIn, String accessToken, #Nullable String refreshToken) {
return new AccessRefreshToken(accessToken, refreshToken, BEARER_TOKEN_TYPE, ***customValue**);
}
Will returning a different value for expires_in work in this case?

According to micronaut documentation - pt 9.3.5
You can replace ClaimGenerator with your own:
ClaimGenerator
Which has a method with parameter Integer expiration
You can replace classes by using #Replaces annotation like described here:
https://micronaut-projects.github.io/micronaut-core/latest/guide/#replaces
I hope that solves your issue

The solution is to extend the JwTClaimsSetGenerator
#Replaces(JWTClaimsSetGenerator.class)
#Singleton
public class CustomClaimsGenerator extends JWTClaimsSetGenerator {
private static final Logger LOG = LoggerFactory.getLogger(CustomClaimsGenerator.class);
private static final String ROLES_KEY = "rolesKey";
private final TokenConfiguration tokenConfiguration;
private final JwtIdGenerator jwtIdGenerator;
private final ClaimsAudienceProvider claimsAudienceProvider;
private final String appName;
/**
* #param tokenConfiguration Token Configuration
* #param jwtIdGenerator Generator which creates unique JWT ID
* #param claimsAudienceProvider Provider which identifies the recipients that the JWT is intended for.
* #param applicationConfiguration The application configuration
*/
public CustomClaimsGenerator(TokenConfiguration tokenConfiguration,#Nullable JwtIdGenerator jwtIdGenerator, #Nullable ClaimsAudienceProvider claimsAudienceProvider, ApplicationConfiguration applicationConfiguration) {
super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider, applicationConfiguration);
this.tokenConfiguration = tokenConfiguration;
this.jwtIdGenerator = jwtIdGenerator;
this.claimsAudienceProvider = claimsAudienceProvider;
this.appName = applicationConfiguration != null ? applicationConfiguration.getName().orElse(Environment.MICRONAUT) : Environment.MICRONAUT;
}
/**
* #param authentication Authenticated user's representation.
* #param expiration expiration time in seconds
* #return The authentication claims
*/
#Override
public Map<String, Object> generateClaims(Authentication authentication, #Nullable Integer expiration) {
expiration = 1000; //works with this value
JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
populateIat(builder);
populateExp(builder, 15);
populateJti(builder);
populateIss(builder);
populateAud(builder);
populateNbf(builder);
populateWithAuthentication(builder, authentication);
if (LOG.isDebugEnabled()) {
LOG.debug("Generated claim set: {}", builder.build().toJSONObject());
}
return builder.build().getClaims();
}
/**
* Populates iss claim.
*
* #param builder The Claims Builder
* #see iss (Issuer) Claim
*/
protected void populateIss(JWTClaimsSet.Builder builder) {
if (appName != null) {
builder.issuer(appName); // iss
}
}
/**
* Populates sub claim.
*
* #param builder The Claims Builder
* #param authentication Authenticated user's representation.
* #see sub (Subject) Claim
*/
protected void populateSub(JWTClaimsSet.Builder builder, Authentication authentication) {
builder.subject(authentication.getName()); // sub
}
/**
* Populates aud claim.
*
* #param builder The Claims Builder
* #see aud (Audience) Claim
*/
protected void populateAud(JWTClaimsSet.Builder builder) {
if (claimsAudienceProvider != null) {
builder.audience(claimsAudienceProvider.audience()); // aud
}
}
/**
* Populates exp claim.
*
* #param builder The Claims Builder
* #param expiration expiration time in seconds
* #see exp (ExpirationTime) Claim
*/
protected void populateExp(JWTClaimsSet.Builder builder, #Nullable Integer expiration) {
if (expiration != null) {
LOG.debug("Setting expiration to {}", expiration);
System.out.print(Date.from(Instant.now().plus(expiration, ChronoUnit.SECONDS)));
builder.expirationTime(Date.from(Instant.now().plus(expiration, ChronoUnit.SECONDS))); // exp
}
}
/**
* Populates nbf claim.
*
* #param builder The Claims Builder
* #see nbf (Not Before) Claim
*/
protected void populateNbf(JWTClaimsSet.Builder builder) {
builder.notBeforeTime(new Date()); // nbf
}
/**
* Populates iat claim.
*
* #param builder The Claims Builder
* #see iat (Issued At) Claim
*/
protected void populateIat(JWTClaimsSet.Builder builder) {
builder.issueTime(new Date()); // iat
}
/**
* Populates jti claim.
*
* #param builder The Claims Builder
* #see jti (JWT ID) Claim
*/
protected void populateJti(JWTClaimsSet.Builder builder) {
if (jwtIdGenerator != null) {
builder.jwtID(jwtIdGenerator.generateJtiClaim()); // jti
}
}
/**
* Populates Claims with Authentication object.
*
* #param builder the Claims Builder
* #param authentication Authenticated user's representation.
*/
protected void populateWithAuthentication(JWTClaimsSet.Builder builder, Authentication authentication) {
populateSub(builder, authentication);
authentication.getAttributes().forEach(builder::claim);
String rolesKey = tokenConfiguration.getRolesName();
if (!rolesKey.equalsIgnoreCase(TokenConfiguration.DEFAULT_ROLES_NAME)) {
builder.claim(ROLES_KEY, rolesKey);
}
builder.claim(rolesKey, authentication.getRoles());
}
/**
* #param oldClaims The old claims to use as a base in the new token generation.
* #param expiration expiration time in seconds
* #return Instance of {#link JWTClaimsSet}
*/
#Override
public Map<String, Object> generateClaimsSet(Map<String, ?> oldClaims, Integer expiration) {
JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
List<String> excludedClaims = Arrays.asList(JwtClaims.EXPIRATION_TIME, JwtClaims.ISSUED_AT, JwtClaims.NOT_BEFORE);
for (String k : oldClaims.keySet()
.stream()
.filter(p -> !excludedClaims.contains(p))
.collect(Collectors.toList())) {
builder.claim(k, oldClaims.get(k));
}
populateExp(builder, expiration);
populateIat(builder);
populateNbf(builder);
return builder.build().getClaims();
}
}

Related

Getting unexpected element error when trying to communcate to soap client

I'm trying to consume wsdl client that can be found on this link https://tim-erp.com/ERPX_WEB/awws/ErpX.awws?wsdl
My java code looks like this
public class SoapServicesImpl extends WebServiceGatewaySupport implements SoapServices {
#Override
public String doAuth(AuthDto authDto){
AuthStrct authStrct = new AuthStrct();
authStrct.setFirmaNaziv(authDto.getNazivFirme());
authStrct.setGodina(authDto.getGodina());
authStrct.setPassword(authDto.getPassword());
authStrct.setUsername(authDto.getUsername());
return callService(new ObjectFactory().createAuthStrct(authStrct));
}
#SuppressWarnings("unchecked")
public <T> T callService(JAXBElement<?> jaxbRequest) {
jaxbRequest.getValue();
JAXBElement<T> jaxbResponse;
try {
jaxbResponse = (JAXBElement<T>) getWebServiceTemplate().marshalSendAndReceive(jaxbRequest);
} catch (WebServiceException e) {
e.printStackTrace();
throw e;
}
return jaxbResponse.getValue();
}
}
package-info looks like this
#javax.xml.bind.annotation.XmlSchema(namespace = "urn:ErpX")
package rs.sweetchoice.commonservice.generated;
Generated ObjectFactory snippet:
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
// See https://javaee.github.io/jaxb-v2/
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2020.09.05 at 01:28:42 PM CEST
//
package rs.sweetchoice.commonservice.generated;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.namespace.QName;
/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the rs.sweetchoice.commonservice.generated package.
* <p>An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
* content can consist of schema derived interfaces
* and classes representing the binding of schema
* type definitions, element declarations and model
* groups. Factory methods for each of these are
* provided in this class.
*
*/
#XmlRegistry
public class ObjectFactory {
private final static QName _WSID_QNAME = new QName("urn:ErpX", "WSID");
private final static QName _AUTH_QNAME = new QName("urn:ErpX", "authStrct");
/**
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: rs.sweetchoice.commonservice.generated
*
*/
public ObjectFactory() {
}
/**
* Create an instance of {#link AutoCompleteStrct }
*
*/
public AutoCompleteStrct createAutoCompleteStrct() {
return new AutoCompleteStrct();
}
/**
* Create an instance of {#link KorpaStrct }
*
*/
public KorpaStrct createKorpaStrct() {
return new KorpaStrct();
}
/**
* Create an instance of {#link CmbStrct }
*
*/
public CmbStrct createCmbStrct() {
return new CmbStrct();
}
/**
* Create an instance of {#link AuthStrct }
*
*/
#XmlElementDecl(namespace = "urn:ErpX", name = "authStrct")
public JAXBElement<AuthStrct> createAuthStrct(AuthStrct value) {
return new JAXBElement<>(_AUTH_QNAME, AuthStrct.class, null, value);
}
/**
* Create an instance of {#link FilterStrct }
*
*/
public FilterStrct createFilterStrct() {
return new FilterStrct();
}
/**
* Create an instance of {#link ProfakStavStrct }
*
*/
public ProfakStavStrct createProfakStavStrct() {
return new ProfakStavStrct();
}
/**
* Create an instance of {#link ProfakStrct }
*
*/
public ProfakStrct createProfakStrct() {
return new ProfakStrct();
}
/**
* Create an instance of {#link RacunStavStrct }
*
*/
public RacunStavStrct createRacunStavStrct() {
return new RacunStavStrct();
}
/**
* Create an instance of {#link RacunStrct }
*
*/
public RacunStrct createRacunStrct() {
return new RacunStrct();
}
/**
* Create an instance of {#link UplKomitStrct }
*
*/
public UplKomitStrct createUplKomitStrct() {
return new UplKomitStrct();
}
/**
* Create an instance of {#link UplListaStavStrct }
*
*/
public UplListaStavStrct createUplListaStavStrct() {
return new UplListaStavStrct();
}
/**
* Create an instance of {#link UplListaStrct }
*
*/
public UplListaStrct createUplListaStrct() {
return new UplListaStrct();
}
/**
* Create an instance of {#link WsGetRoba }
*
*/
public WsGetRoba createWsGetRoba() {
return new WsGetRoba();
}
/**
* Create an instance of {#link WsGetKomit }
*
*/
public WsGetKomit createWsGetKomit() {
return new WsGetKomit();
}
/**
* Create an instance of {#link WsGetGrpRoba }
*
*/
public WsGetGrpRoba createWsGetGrpRoba() {
return new WsGetGrpRoba();
}
/**
* Create an instance of {#link WsGetGrpKomit }
*
*/
public WsGetGrpKomit createWsGetGrpKomit() {
return new WsGetGrpKomit();
}
/**
* Create an instance of {#link WsGetManuf }
*
*/
public WsGetManuf createWsGetManuf() {
return new WsGetManuf();
}
/**
* Create an instance of {#link WsUplProfak }
*
*/
public WsUplProfak createWsUplProfak() {
return new WsUplProfak();
}
/**
* Create an instance of {#link WsGetMagacin }
*
*/
public WsGetMagacin createWsGetMagacin() {
return new WsGetMagacin();
}
/**
* Create an instance of {#link WsGetAkcijskeCene }
*
*/
public WsGetAkcijskeCene createWsGetAkcijskeCene() {
return new WsGetAkcijskeCene();
}
/**
* Create an instance of {#link WsUplKomit }
*
*/
public WsUplKomit createWsUplKomit() {
return new WsUplKomit();
}
/**
* Create an instance of {#link WsUplKomitResponse }
*
*/
public WsUplKomitResponse createWsUplKomitResponse() {
return new WsUplKomitResponse();
}
/**
* Create an instance of {#link WsUplRacun }
*
*/
public WsUplRacun createWsUplRacun() {
return new WsUplRacun();
}
/**
* Create an instance of {#link JAXBElement }{#code <}{#link byte[]}{#code >}
*
* #param value
* Java instance representing xml element's value.
* #return
* the new instance of {#link JAXBElement }{#code <}{#link byte[]}{#code >}
*/
#XmlElementDecl(namespace = "urn:ErpX", name = "WSID")
#XmlJavaTypeAdapter(HexBinaryAdapter.class)
public JAXBElement<byte[]> createWSID(byte[] value) {
return new JAXBElement<byte[]>(_WSID_QNAME, byte[].class, null, ((byte[]) value));
}
}
And i get error
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault"). Expected elements are <{urn:ErpX}WSID>,<{urn:ErpX}authStrct>
Using java 11 and spring boot 2.3.3.
EDIT this is my config class
#Configuration
public class WsConfig {
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("rs.sweetchoice.commonservice.generated");
return marshaller;
}
#Bean
public SoapServicesImpl soapServicesImpl(Jaxb2Marshaller marshaller) {
SoapServicesImpl client = new SoapServicesImpl();
client.setDefaultUri("https://tim-erp.com/ERPX_WEB/awws/ErpX.awws");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
Try to setCheckConnectionForFault to false on WebServiceTemplate. For example, replace the method:
public <T> T callService(JAXBElement<?> jaxbRequest) {
jaxbRequest.getValue();
JAXBElement<T> jaxbResponse;
try {
WebServiceTemplate webServiceTemplate = getWebServiceTemplate();
webServiceTemplate.setCheckConnectionForFault(false);
jaxbResponse = (JAXBElement<T>) webServiceTemplate.marshalSendAndReceive(jaxbRequest);
} catch (WebServiceException e) {
e.printStackTrace();
throw e;
}
return jaxbResponse.getValue();
}

How to add multiple reactions to embed message with JDA

I am trying to make a bot respond to a command with an embed message that has multiple reactions under it.
I have got it working to the point that it adds 1 reaction but I need to add different reactions as well, like this: (http://prntscr.com/qd8da4) There are a lot of tutorials online that add 1 reaction, but none that add multiple.
I am using the latest version of the Discord JDA.
The code I currently have is:
public void onGuildMessageReceived(GuildMessageReceivedEvent event) {
String[] args = event.getMessage().getContentRaw().split("\\s+");
if (args[0].equalsIgnoreCase(DiscordBot.prefix + "info")) {
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss z");
Date date = new Date(System.currentTimeMillis());
MessageChannel channel = event.getChannel(); // Channel the command was sent in.
EmbedBuilder info = new EmbedBuilder();
info.setTitle("Server Info");
info.setDescription("Info About the bot.");
info.addField("Creator", "Name", false);
info.setColor(0xf45642);
info.setTimestamp(Instant.now());
channel.sendMessage(info.build()).queue(message -> message.addReaction("✔️").queue());
}
}
I had the same problem myself as there wasn't really a native way to do what I want which was to have certain bot actions when a reaction was clicked.
I ended up creating a couple helper classes that allow the program to have callbacks when reactions are clicked:
The following is the reaction which will listen for reactions to be clicked:
public class ReactionListener<T> {
private final Map<String, Consumer<Message>> reactions;
private final long userId;
private volatile T data;
private Long expiresIn, lastAction;
private boolean active;
public ReactionListener(long userId, T data) {
this.data = data;
this.userId = userId;
reactions = new LinkedHashMap<>();
active = true;
lastAction = System.currentTimeMillis();
expiresIn = TimeUnit.MINUTES.toMillis(5);
}
public boolean isActive() {
return active;
}
public void disable() {
this.active = false;
}
/**
* The time after which this listener expires which is now + specified time
* Defaults to now+5min
*
* #param timeUnit time units
* #param time amount of time units
*/
public void setExpiresIn(TimeUnit timeUnit, long time) {
expiresIn = timeUnit.toMillis(time);
}
/**
* Check if this listener has specified emote
*
* #param emote the emote to check for
* #return does this listener do anything with this emote?
*/
public boolean hasReaction(String emote) {
return reactions.containsKey(emote);
}
/**
* React to the reaction :')
*
* #param emote the emote used
* #param message the message bound to the reaction
*/
public void react(String emote, Message message) {
if (hasReaction(emote)) reactions.get(emote).accept(message);
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
/**
* Register a consumer for a specified emote
* Multiple emote's will result in overriding the old one
*
* #param emote the emote to respond to
* #param consumer the behaviour when emote is used
*/
public void registerReaction(String emote, Consumer<Message> consumer) {
reactions.put(emote, consumer);
}
/**
* #return list of all emotes used in this reaction listener
*/
public Set<String> getEmotes() {
return reactions.keySet();
}
/**
* updates the timestamp when the reaction was last accessed
*/
public void updateLastAction() {
lastAction = System.currentTimeMillis();
}
/**
* When does this reaction listener expire?
*
* #return timestamp in millis
*/
public Long getExpiresInTimestamp() {
return lastAction + expiresIn;
}
public long getUserId() {
return userId;
}
}
Next we want to handle the reactions as they are clicked. This method requires the current logged in userId to make sure only that users actions will be recorded and no one elses. It can be modified to be global if others can react too.:
public class ReactionHandler {
private final ConcurrentHashMap<Long, ConcurrentHashMap<Long, ReactionListener<?>>> reactions;
private ReactionHandler() {
reactions = new ConcurrentHashMap<>();
}
public synchronized void addReactionListener(long guildId, Message message, ReactionListener<?> handler) {
addReactionListener(guildId, message, handler, true);
}
public synchronized void addReactionListener(long guildId, Message message, ReactionListener<?> handler, boolean queue) {
if (handler == null) {
return;
}
if (message.getChannelType().equals(ChannelType.TEXT)) {
if (!PermissionUtil.checkPermission(message.getTextChannel(), message.getGuild().getSelfMember(), Permission.MESSAGE_ADD_REACTION)) {
return;
}
}
if (!reactions.containsKey(guildId)) {
reactions.put(guildId, new ConcurrentHashMap<>());
}
if (!reactions.get(guildId).containsKey(message.getIdLong())) {
for (String emote : handler.getEmotes()) {
RestAction<Void> action = message.addReaction(emote);
if (queue) action.queue(); else action.complete();
}
}
reactions.get(guildId).put(message.getIdLong(), handler);
}
public synchronized void removeReactionListener(long guildId, long messageId) {
if (!reactions.containsKey(guildId)) return;
reactions.get(guildId).remove(messageId);
}
/**
* Handles the reaction
*
* #param channel TextChannel of the message
* #param messageId id of the message
* #param userId id of the user reacting
* #param reaction the reaction
*/
public void handle(TextChannel channel, long messageId, long userId, MessageReaction reaction) {
ReactionListener<?> listener = reactions.get(channel.getGuild().getIdLong()).get(messageId);
if (!listener.isActive() || listener.getExpiresInTimestamp() < System.currentTimeMillis()) {
reactions.get(channel.getGuild().getIdLong()).remove(messageId);
} else if ((listener.hasReaction(reaction.getReactionEmote().getName())) && listener.getUserId() == userId) {
reactions.get(channel.getGuild().getIdLong()).get(messageId).updateLastAction();
Message message = channel.retrieveMessageById(messageId).complete();
listener.react(reaction.getReactionEmote().getName(), message);
}
}
/**
* Do we have an event for a message?
*
* #param guildId discord guild-id of the message
* #param messageId id of the message
* #return do we have an handler?
*/
public boolean canHandle(long guildId, long messageId) {
return reactions.containsKey(guildId) && reactions.get(guildId).containsKey(messageId);
}
public synchronized void removeGuild(long guildId) {
reactions.remove(guildId);
}
/**
* Delete expired handlers
*/
public synchronized void cleanCache() {
long now = System.currentTimeMillis();
for (Iterator<Map.Entry<Long, ConcurrentHashMap<Long, ReactionListener<?>>>> iterator = reactions.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<Long, ConcurrentHashMap<Long, ReactionListener<?>>> mapEntry = iterator.next();
mapEntry.getValue().values().removeIf(listener -> !listener.isActive() || listener.getExpiresInTimestamp() < now);
if (mapEntry.getValue().values().isEmpty()) {
reactions.remove(mapEntry.getKey());
}
}
}
}
Then with both of these libraries in hand we can implement them in our messages as such.
channel.sendMessage(info.build()).queue((msg) -> {
ReactionListener<String> handler = new ReactionListener<>(userId, msg.getId());
handler.setExpiresIn(TimeUnit.MINUTES, 1);
handler.registerReaction("✔️", (ret) -> foo());
handler.registerReaction("X", (ret) -> bar());
reactionHandler.addReactionListener(guild.getIdLong(), msg, handler);
});

Recommendation with optimization

How can I achieve more optimization in following code?, I don't like my last check "Objects.isNull" ? Thanks in advance.
/**
* Tries to get the service request DAO from the ItemHolder, if it is not present there, it will try to get it from the argument.
* If no service request found then CancellationException is thrown.
*
* In case both are present takes precedence the one in the ItemHolder object.
*
* #param itemHolderParameter the holder to be checked.
* #param serviceRequestDAO in case no service request dao is found in the holder, this will be used.
* #return ServiceRequestDAO guaranteed not null object.
* #throws CancellationException in case no service request DAO was retrieved.
* #throws IllegalArgumentException in case itemHolderParameter is null.
*/
public static ServiceRequestDAO getServiceRequestDAO(final ItemHolder itemHolderParameter, final ServiceRequestDAO serviceRequestDAO){
AtomicReference<ServiceRequestDAO> atomicReference = new AtomicReference<>(serviceRequestDAO);
final ItemHolder itemHolder = Optional.ofNullable(itemHolderParameter).orElseThrow(() -> new IllegalArgumentException("ItemHolder must not be null"));
Optional.ofNullable(itemHolder.getServiceRequestDAO()).ifPresent(atomicReference::set);
//Final validation.
final ServiceRequestDAO requestDAO = atomicReference.get();
if(Objects.isNull(requestDAO)){
throw new CancellationException("Unable to get ServiceRequestDAO (null)");
}
return requestDAO;
}
~M
There seems to be no good reason to bring Optional or AtomicReference into it:
if (itemHolderParameter == null) {
throw new IllegalArgumentException(...)
}
ServiceRequestDao dao = itemHolder.getServiceRequestDAO();
if (dao != null) {
return dao;
}
if (serviceRequestDao != null) {
return serviceRequestDao;
}
throw new CancellableException(...);
I have done my best to follow the semantics of your code, but honestly, it is very unclear.
Thanks Boris,
/**
* Tries to get the service request DAO from the ItemHolder, if it is not present there, it will try to get it from the argument.
* If no service request found then CancellationException is thrown.
*
* In case both are present takes precedence the one in the ItemHolder object.
*
* #param itemHolderParameter the holder to be checked.
* #param serviceRequestDAO in case no service request dao is found in the holder, this will be used.
* #return ServiceRequestDAO guaranteed not null object.
* #throws CancellationException in case no service request DAO was retrieved.
* #throws IllegalArgumentException in case itemHolderParameter is null.
*/
public static ServiceRequestDAO getServiceRequestDAO(final ItemHolder itemHolderParameter, final ServiceRequestDAO serviceRequestDAO){
return Optional.ofNullable(Optional.ofNullable(itemHolderParameter).map(ItemHolder::getServiceRequestDAO).orElse(serviceRequestDAO)).orElseThrow(() -> new CancellationException("No service request could be retrieved."));
}
The test cases:
public class HolderUtilsTest {
#Test(expected = CancellationException.class)
public void testHolderParameterNulls(){
HolderUtils.getServiceRequestDAO(null, null);
}
#Test(expected = CancellationException.class)
public void testHolderParameterHolderNull(){
HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().build(), null);
}
#Test
public void testHolderParameterNullAndSRNotNull(){
final ServiceRequestDAO serviceRequestDAO = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAO1 = HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().build(), serviceRequestDAO);
assertThat(serviceRequestDAO1).isNotNull();
assertThat(serviceRequestDAO1).isEqualTo(serviceRequestDAO);
}
#Test
public void testHolderParameterNotNullWithObjectAndSRNull(){
final ServiceRequestDAO serviceRequestDAO = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAO1 = HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().srDao(serviceRequestDAO).build(), null);
assertThat(serviceRequestDAO1).isNotNull();
assertThat(serviceRequestDAO1).isEqualTo(serviceRequestDAO);
}
#Test
public void testHolderBothPresent(){
final ServiceRequestDAO serviceRequestDAO0 = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAO1 = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAOResult = HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().srDao(serviceRequestDAO0).build(), serviceRequestDAO1);
assertThat(serviceRequestDAOResult).isNotNull();
assertThat(serviceRequestDAOResult).isEqualTo(serviceRequestDAO0);
assertThat(serviceRequestDAOResult).isNotEqualTo(serviceRequestDAO1);
}

Generate APK constructor issue on my app

I developed some app and it's working fine without any problem. No problem on debug. I created some custom class with the ShareActionProvider class methods for check to user actions from ActionShareProvider menu. When I want to generate APK from Android Studio it gives me this error;
Error:(87) Error: This class should provide a default constructor (a public constructor with no arguments) (com.esmobileinc.vetmapp.ShareActionProvider) [Instantiatable]
There is the ShareActionProvider class;
public class ShareActionProvider extends ActionProvider {
public void setOnShareListener(OnShareListener listener) {
mOnShareListener = listener;
setActivityChooserPolicyIfNeeded();
}
/**
* Listener for the event of selecting a share target.
*/
public interface OnShareTargetSelectedListener {
/**
* Called when a share target has been selected. The client can
* decide whether to perform some action before the sharing is
* actually performed.
* <p>
* <strong>Note:</strong> Modifying the intent is not permitted and
* any changes to the latter will be ignored.
* </p>
* <p>
* <strong>Note:</strong> You should <strong>not</strong> handle the
* intent here. This callback aims to notify the client that a
* sharing is being performed, so the client can update the UI
* if necessary.
* </p>
*
* #param source The source of the notification.
* #param intent The intent for launching the chosen share target.
* #return The return result is ignored. Always return false for consistency.
*/
public boolean onShareTargetSelected(ShareActionProvider source, Intent intent);
}
private OnShareListener mOnShareListener; //also need to add getter and setter
public interface OnShareListener {
/**
* Called when a share target has been selected. The client can
* decide whether to perform some action before the sharing is
* actually performed OR handle the action itself.
*
* #param source The source of the notification.
* #param intent The intent for launching the chosen share target.
* #return Return true if you have handled the intent.
*/
public boolean willHandleShareTarget(ShareActionProvider source, Intent intent);
}
/**
* The default for the maximal number of activities shown in the sub-menu.
*/
private static final int DEFAULT_INITIAL_ACTIVITY_COUNT = 4;
/**
* The the maximum number activities shown in the sub-menu.
*/
private int mMaxShownActivityCount = DEFAULT_INITIAL_ACTIVITY_COUNT;
/**
* Listener for handling menu item clicks.
*/
private final ShareMenuItemOnMenuItemClickListener mOnMenuItemClickListener =
new ShareMenuItemOnMenuItemClickListener();
/**
* The default name for storing share history.
*/
public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
/**
* Context for accessing resources.
*/
private final Context mContext;
/**
* The name of the file with share history data.
*/
private String mShareHistoryFileName = DEFAULT_SHARE_HISTORY_FILE_NAME;
private OnShareTargetSelectedListener mOnShareTargetSelectedListener;
private OnChooseActivityListener mOnChooseActivityListener;
/**
* Creates a new instance.
*
* #param context Context for accessing resources.
*/
public ShareActionProvider(Context context) {
super(context);
mContext = context;
}
/**
* Sets a listener to be notified when a share target has been selected.
* The listener can optionally decide to handle the selection and
* not rely on the default behavior which is to launch the activity.
* <p>
* <strong>Note:</strong> If you choose the backing share history file
* you will still be notified in this callback.
* </p>
* #param listener The listener.
*/
public void setOnShareTargetSelectedListener(OnShareTargetSelectedListener listener) {
mOnShareTargetSelectedListener = listener;
setActivityChooserPolicyIfNeeded();
}
/**
* {#inheritDoc}
*/
#Override
public View onCreateActionView() {
// Create the view and set its data model.
ActivityChooserView activityChooserView = new ActivityChooserView(mContext);
if (!activityChooserView.isInEditMode()) {
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
activityChooserView.setActivityChooserModel(dataModel);
}
// Lookup and set the expand action icon.
TypedValue outTypedValue = new TypedValue();
mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true);
Drawable drawable = TintManager.getDrawable(mContext, outTypedValue.resourceId);
activityChooserView.setExpandActivityOverflowButtonDrawable(drawable);
activityChooserView.setProvider(this);
// Set content description.
activityChooserView.setDefaultActionButtonContentDescription(
R.string.abc_shareactionprovider_share_with_application);
activityChooserView.setExpandActivityOverflowButtonContentDescription(
R.string.abc_shareactionprovider_share_with);
return activityChooserView;
}
/**
* {#inheritDoc}
*/
#Override
public boolean hasSubMenu() {
return true;
}
/**
* {#inheritDoc}
*/
#Override
public void onPrepareSubMenu(SubMenu subMenu) {
// Clear since the order of items may change.
subMenu.clear();
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
PackageManager packageManager = mContext.getPackageManager();
final int expandedActivityCount = dataModel.getActivityCount();
final int collapsedActivityCount = Math.min(expandedActivityCount, mMaxShownActivityCount);
// Populate the sub-menu with a sub set of the activities.
for (int i = 0; i < collapsedActivityCount; i++) {
ResolveInfo activity = dataModel.getActivity(i);
subMenu.add(0, i, i, activity.loadLabel(packageManager))
.setIcon(activity.loadIcon(packageManager))
.setOnMenuItemClickListener(mOnMenuItemClickListener);
}
if (collapsedActivityCount < expandedActivityCount) {
// Add a sub-menu for showing all activities as a list item.
SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount,
collapsedActivityCount,
mContext.getString(R.string.abc_activity_chooser_view_see_all));
for (int i = 0; i < expandedActivityCount; i++) {
ResolveInfo activity = dataModel.getActivity(i);
expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager))
.setIcon(activity.loadIcon(packageManager))
.setOnMenuItemClickListener(mOnMenuItemClickListener);
}
}
}
/**
* Sets the file name of a file for persisting the share history which
* history will be used for ordering share targets. This file will be used
* for all view created by {#link #onCreateActionView()}. Defaults to
* {#link #DEFAULT_SHARE_HISTORY_FILE_NAME}. Set to <code>null</code>
* if share history should not be persisted between sessions.
* <p>
* <strong>Note:</strong> The history file name can be set any time, however
* only the action views created by {#link #onCreateActionView()} after setting
* the file name will be backed by the provided file. Therefore, if you want to
* use different history files for sharing specific types of content, every time
* you change the history file {#link #setShareHistoryFileName(String)} you must
* call {#link android.app.Activity#invalidateOptionsMenu()} to recreate the
* action view. You should <strong>not</strong> call
* {#link android.app.Activity#invalidateOptionsMenu()} from
* {#link android.app.Activity#onCreateOptionsMenu(Menu)}."
* <p>
* <code>
* private void doShare(Intent intent) {
* if (IMAGE.equals(intent.getMimeType())) {
* mShareActionProvider.setHistoryFileName(SHARE_IMAGE_HISTORY_FILE_NAME);
* } else if (TEXT.equals(intent.getMimeType())) {
* mShareActionProvider.setHistoryFileName(SHARE_TEXT_HISTORY_FILE_NAME);
* }
* mShareActionProvider.setIntent(intent);
* invalidateOptionsMenu();
* }
* <code>
*
* #param shareHistoryFile The share history file name.
*/
public void setShareHistoryFileName(String shareHistoryFile) {
mShareHistoryFileName = shareHistoryFile;
setActivityChooserPolicyIfNeeded();
}
/**
* Sets an intent with information about the share action. Here is a
* sample for constructing a share intent:
* <p>
* <pre>
* <code>
* Intent shareIntent = new Intent(Intent.ACTION_SEND);
* shareIntent.setType("image/*");
* Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
* shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());
* </pre>
* </code>
* </p>
*
* #param shareIntent The share intent.
*
* #see Intent#ACTION_SEND
* #see Intent#ACTION_SEND_MULTIPLE
*/
public void setShareIntent(Intent shareIntent) {
if (shareIntent != null) {
final String action = shareIntent.getAction();
if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) {
updateIntent(shareIntent);
}
}
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
mShareHistoryFileName);
dataModel.setIntent(shareIntent);
}
/**
* Reusable listener for handling share item clicks.
*/
private class ShareMenuItemOnMenuItemClickListener implements OnMenuItemClickListener {
#Override
public boolean onMenuItemClick(MenuItem item) {
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
mShareHistoryFileName);
final int itemId = item.getItemId();
Intent launchIntent = dataModel.chooseActivity(itemId);
if (launchIntent != null) {
final String action = launchIntent.getAction();
if (Intent.ACTION_SEND.equals(action) ||
Intent.ACTION_SEND_MULTIPLE.equals(action)) {
updateIntent(launchIntent);
}
mContext.startActivity(launchIntent);
}
return true;
}
}
/**
* Set the activity chooser policy of the model backed by the current
* share history file if needed which is if there is a registered callback.
*/
private void setActivityChooserPolicyIfNeeded() {
if (mOnShareListener == null) {
return;
}
if (mOnChooseActivityListener == null) {
mOnChooseActivityListener = new ShareActivityChooserModelPolicy();
}
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
dataModel.setOnChooseActivityListener(mOnChooseActivityListener);
}
/**
* Policy that delegates to the {#link OnShareTargetSelectedListener}, if such.
*/
private class ShareActivityChooserModelPolicy implements OnChooseActivityListener {
#Override
public boolean onChooseActivity(ActivityChooserModel host, Intent intent) {
if (mOnShareListener != null) {
boolean result = mOnShareListener.willHandleShareTarget(
ShareActionProvider.this, intent);
return result;
}
return false;
}
}
private void updateIntent(Intent intent) {
if (Build.VERSION.SDK_INT >= 21) {
// If we're on Lollipop, we can open the intent as a document
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
} else {
// Else, we will use the old CLEAR_WHEN_TASK_RESET flag
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
}
}
There is a constructor here in my ShareActionProvider class;
public ShareActionProvider(Context context) {
super(context);
mContext = context;
}
Why it gives me this error ? How can I solve this problem. Thank you !
I renamed my custom ActionShareProvider.class to CustomActionShareProvider.class and problem resolved.

private static method access from public static

I have a question regarding static method access. I have a class within i have 4 static method. as shown in code:
package com.itrucking.util;
public class ZKUtil implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
* #author Shekhar
* #param _class
* #param listbox
* To make Listbox sorting enabled
* #throws NoSuchMethodException
* #throws SecurityException
*/
public static void setSortingEnabled(Class<?> _class, Listbox listbox){
Map<Listheader, String> sortingPair = new HashMap<Listheader, String>();
sortingPair = getMapForSorting(_class, listbox);
if (!sortingPair.isEmpty()) {
for (Map.Entry<Listheader, String> entry : sortingPair.entrySet()) {
entry.getKey().setSortAscending(
new FieldComparator(entry.getValue(), true));
entry.getKey().setSortDescending(
new FieldComparator(entry.getValue(), false));
}
}
}
/**
* #author Shekhar
* #param _class
* #param listbox
* #return Map<Listheader, String>
*/
private static Map<Listheader, String> getMapForSorting(Class<?> _class,Listbox listbox) {
List<Listheader> headerList = getListHeaderList(listbox);
Map<Listheader, String> sortingPair = new HashMap<Listheader, String>();
System.out.println(_class);
Field[] fields = _class.getDeclaredFields();
for (Field f : fields) {
// System.out.println(f.getName()+":"+f.getType());
for (Listheader lh : headerList) {
if (f.getName().equals(getId(lh)))
sortingPair.put(lh, f.getName());
}
}
System.out.println(sortingPair);
return sortingPair;
}
private static String getId(Listheader listheader) {
String listheaderId = null;
if (listheader.getId().contains("_")) {
listheaderId = listheader.getId().split("_")[1];
// System.out.println("listheaderId->"+listheaderId);
}
return listheaderId;
}
/**
* #author Shekhar
* #param listbox
* #return List<Listheader>
*/
#SuppressWarnings("unchecked")
private static List<Listheader> getListHeaderList(Listbox listbox) {
List<Listheader> headerList = new ArrayList<Listheader>();
Listhead listhead = null;
List<Component> listboxComponentList = listbox.getChildren();
for (Component listboxComponent : listboxComponentList) {
if (listboxComponent instanceof Listhead) {
listhead = (Listhead) listboxComponent;
break;
}
}
List<Component> listOfComp = listhead.getChildren();
if (listhead != null) {
for (Component c : listOfComp) {
if (c instanceof Listheader)
headerList.add((Listheader) c);
}
}
return headerList;
}
}
and i am calling setSortingEnabled() method from onLoadShipperDetailsListCtrl() from code bellow :
package com.itrucking.webui.controller;
public class ShipperDetailsListCtrl{
/**
* #param e
* #return void
*/
public void onCreate$window_shipperDetailsList(Event e){
onLoadShipperDetailsListCtrl();
}
/**
* #return void
*/
public void onLoadShipperDetailsListCtrl(){
System.out.println("onLoadShipperDetailsListCtrl called.");
shipperList = shipperService.getShipperList();
doRenderListboxShipperDetailsList(shipperList);
ZKUtil.setSortingEnabled(ShipperMaster.class, listbox_shipperDetailsList);
}
}
so what i think if i am calling setSortingEnabled() method from other class so i kept is as public and other method's i kept as private but it's giving me error as :
java.lang.NoSuchMethodError: com/itrucking/util/ZKUtil.getMapForSorting(Ljava/lang/Class;Lorg/zkoss/zul/Listbox;)Ljava/util/Map;
Why there is error NoSuchMethodError for ZKUtil.getMapForSorting() call in setSortingEnabled()
I know we can call private method from public in the same class. So i am not able to understand what is the problem.
Thanks in advance.
A NoSuchMethodError (the runtime error saying a method can't be found, instead of a compiler error) usually means that the .class files you're using are of a different version than the files you compiled against. In this case, you probably made changes to ZKUtil.java, but the JVM is loading an outdated version of ZKUtil.class. Clean and rebuild all of your .class files.

Categories

Resources