Using Struts 2, I have this interceptor:
public class AccessInterceptor extends AbstractInterceptor {
#Override
public String intercept(ActionInvocation ai) throws Exception {
ActionContext actionContext = ai.getInvocationContext();
Map<String, Object> session = actionContext.getSession();
Boolean logged = (Boolean) session.get("logged");
if(logged == null) {
session.put("logged", false);
logged = false;
}
if (!logged) {
// If exception is here, it IS thrown, so I am sure "login" is returned on /homepage
return "login";
}
return ai.invoke();
}
}
And this struts.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="loginInterceptor" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="access"
class="logininterceptor.interceptors.AccessInterceptor"/>
<interceptor-stack name="appDefault">
<interceptor-ref name="access" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="appDefault" />
<global-results>
<result name="login">/WEB-INF/content/login.jsp</result>
</global-results>
</package>
</struts>
I have also /WEB-INF/content/homepage.jsp JSP and /WEB-INF/content/login.jsp and mypackage.LoginAction.java class. There is not an acton class for homepage
The problem is, that when I go to the /homepage page, it is NOT redirected to login.jsp and I see the homepage.jsp content. What is wrong?
PS: I use struts2-convention-plugin
This is happening because the conventions plugin uses the "convention-default" package which does not know about your interceptor or interceptor stack.
If you could create a package which extends conventions-default doing just what your interceptor stack does (defines the interceptor, interceptor stack and defines the default result type) and then make the conventions plugin apply that stack as the default for all actions wouldn't that be great? You can.
After the <struts> tag define a constant like so:
<constant name="struts.convention.default.parent.package" value="loginInterceptor"/>
The other suggestion, was to use struts.xml to override the conventions, which is fine but a little verbose.
Another option is to use the parent package annotation, which is good when you need to override the default behavior but as you pointed out you want this to be the default behavior so you should reserve annotations for overriding this, not to implement it.
It may be worth browsing the following list of struts2 conventions plugin constants for the future: http://struts.apache.org/2.1.8/docs/convention-plugin.html#ConventionPlugin-Overwritingpluginclasses
PS: Just naming preference but I would call your package login-package rather than loginInterceptor... not a big deal.
If you want to force to execute the interceptor every time anyone is accessing to homepage.jsp, the correct way of doing this is to put an action that returns the jsp. For example:
<struts>
...
<package name="loginInterceptor" namespace="/" extends="struts-default">
<interceptors>
...
</interceptors>
<default-interceptor-ref name="appDefault" />
<global-results>
...
</global-results>
<action name="homepage">
<result>/WEB-INF/content/homepage.jsp</result>
</action>
</package>
</struts>
After that, when a user tries to access to http://.../homepage.action, the interceptor will be executed and will be redirected to the login page if the user is not logged in.
Related
I'm desperately trying to adhere to the MVVM design pattern in my App. So I'm trying to use the EventToCommandBehavior behavior from the MCT. (I'm also using the CommunityToolkit.Mvvm for [RelayCommand]) I've attached it to an Entry and am trying to forward the TextChanged event to my command. However, my command doesn't execute.
XAML:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="MyApp.View.Accounts.AddAccountPage"
xmlns:viewmodel="clr-namespace:MyApp.ViewModel.AccountsViewModel"
Title="Add Account">
<VerticalStackLayout>
<Entry x:Name="entryAccountName"
Placeholder="Account Name"
PlaceholderColor="Black"
TextColor="{StaticResource Tertiary}"
BackgroundColor="{StaticResource Primary}"
WidthRequest="125"
HorizontalOptions="Center"
Keyboard="Text"
ClearButtonVisibility="WhileEditing"
ReturnType="Next">
<Entry.Behaviors>
<toolkit:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding AccountTextChangedCommand}" />
</Entry.Behaviors>
</Entry>
....More XAML....
AccountsViewModel code:
[RelayCommand]
public void AccountTextChanged()
{
Application.Current.MainPage.DisplayAlert("Text changed", "Account Text Changed", "OK");
}
I've got a breakpoint set to the Method and it just never gets called. Any ideas as to what am I doing wrong?
If by "reference in the header", you mean the line xmlns:viewmodel=...,
all that can do is declare a namespace that can be used in the following xaml. It can't refer to a class in that namespace - the line you show is ignored because it is not a valid namespace - you need:
<ContentPage
xmlns:viewmodel="clr-namespace:MyApp.ViewModel" <--- NAMESPACE, NOT CLASS!
/>
<ContentPage.BindingContext>
<viewmodel:AccountsViewModel/>
</ContentPage.BindingContext>
lines in your xaml, to say which viewmodel is the binding context. This is equivalent to the c# code you put in constructor.
OR There is an alternative technique, using Dependency Injection.
In MauiProgram.CreateMauiApp(), add lines similar to:
mauiAppBuilder.Services.AddTransient<MyApp.ViewModel.AccountsViewModel>();
mauiAppBuilder.Services.AddTransient<MyApp.View.Accounts.AddAccountPage>();
Exact details of those lines depend on your existing CreateMauiApp code, and your namespaces.
I finally figured it out. I had forgotten to set the BindingContext to the AccountsViewModel in the constructor of the page:
using MyApp.Services;
using MyApp.ViewModel.AccountsViewModel;
namespace MyApp.View.Accounts;
public partial class AddAccountPage : ContentPage
{
public AddAccountPage()
{
InitializeComponent();
DataService dataService = new();
AccountsViewModel accountsViewModel = new(dataService);
BindingContext = accountsViewModel;
}
}
I've been having to set the BindingContext via constructors on various pages to get the data bindings to work, even though I reference the ViewModels in the XAML header. Is this intended or am I going about it wrong?
I have a problem with internationalization. I'm trying to implement support two languages in my GWT application. Unfortunately I never found a complete example how to do it with the help of UiBinder. That is what I did:
My module I18nexample.gwt.xml:
<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='i18nexample'>
<inherits name="com.google.gwt.user.User" />
<inherits name='com.google.gwt.user.theme.clean.Clean' />
<inherits name="com.google.gwt.i18n.I18N" />
<inherits name="com.google.gwt.i18n.CldrLocales" />
<entry-point class='com.myexample.i18nexample.client.ExampleI18N' />
<servlet path="/start" class="com.myexample.i18nexample.server.StartServiceImpl" />
<extend-property name="locale" values="en, fr" />
<set-property-fallback name="locale" value="en" />
</module>
My interface Message.java:
package com.myexample.i18nexample.client;
import com.google.gwt.i18n.client.Constants;
public interface Message extends Constants {
String greeting();
}
The same package com.myexample.i18nexample.client has three properties file:
Message.properties:
greeting = hello
Message_en.properties:
greeting = hello
Message_fr.properties:
greeting = bonjour
My UiBinder file Greeting.ui.xml:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder
xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
ui:generateFormat="com.google.gwt.i18n.rebind.format.PropertiesFormat"
ui:generateKeys="com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator"
ui:generateLocales="default" >
<ui:with type="com.myexample.i18nexample.client.Message" field="string" />
<g:HTMLPanel>
<ui:msg key="greeting" description="greeting">Default greeting</ui:msg>
</g:HTMLPanel>
</ui:UiBinder>
When the application starts, I always get the output in the browser:
Default greeting
Why? What am I doing wrong?
I tried to run the application from different URL:
http://127.0.0.1:8888/i18nexample.html?gwt.codesvr=127.0.0.1:9997
http://127.0.0.1:8888/i18nexample.html?locale=en&gwt.codesvr=127.0.0.1:9997
http://127.0.0.1:8888/i18nexample.html?locale=fr&gwt.codesvr=127.0.0.1:9997
The result does not change. Although I expected in last case a message bonjour.
If for example I use a g:Buttton instead of the message ui:msg:
<g:HTMLPanel>
<g:Button text="{string.greeting}" />
</g:HTMLPanel>
Then I get as a result of the button with text "hello"
And if I enter the URL:
http://127.0.0.1:8888/i18nexample.html?locale=fr&gwt.codesvr=127.0.0.1:9997
The text on the button changes to "bonjour". Here everything works as expected. But why internationalization is not working in my first case?
And whether there is a difference between the following:
<ui:msg description="greeting">Default greeting</ui:msg>
<ui:msg description="greeting">hello</ui:msg>
<ui:msg description="greeting"></ui:msg>
Should there be different results in these cases? How to write properly?
Please explain to me the principles of internationalization in GWT and why my example does not work.
Any suggestions would be greatly appreciated.
First, the files should be named Message_fr.properties (resp. Message_en.properties), not Message.properties_fr (resp. Message.properties_en).
Then ui:msg et al. in UiBinder will generate an interface (extending com.google.gwt.i18n.client.Messages)), not use one that you defined. For that, you have to use {string.greeting} (where string is the ui:field you gave to your ui:with). The UiBinder generator will do a GWT.create() on the type class of your ui:with, which is what you'd have done in Java code:
Message string = GWT.create(Message.class);
String localizedGreeting = string.greeting();
In the implicit Messages interface (generated by UiBinder), the various ui:generateXxx attributes on the ui:UiBinder will be transformed into annotations on the interface (properties of the #Generate annotation, or the value of the #GenerateKeys annotation).
Then, one method will be generated for each ui:msg, where the attributes generate equivalent annotations (#Key, #Description) and the content of the ui:msg element is the value of the #DefaultMessage annotation. When you have or widgets inside the content, they'll be turned into arguments to the method and placeholders in the #DefaultMessage text (the values will be filled by UiBinder).
I'd suggest you make something working without UiBinder first, and understand how it works; then try the ui:msg in UiBinder, using -gen in DevMode or the compiler so you can see exactly what code does UiBinder generate (yes, it really only generates code that you could have written yourself by hand).
Also, you should add a <set-property name="locale" value="en, fr" /> or you'll still have the default locale around, despite the set-property-fallback (it'd just never be used)).
I want to manage a table with a VARCHAR primary key, that in the
mapped java object should be a UUID.
i have my sql-map-config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="database.properties"/>
<typeHandlers>
<typeHandler
handler="[...].persistence.typehandlers.UuidTypeHandler"
javaType="java.util.UUID"
jdbcType="VARCHAR"/>
</typeHandlers>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="user.xml" />
</mappers>
</configuration>
and the user.xml is like that:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="[...].persistence.mappers.UserMapper">
<select id="selectUserByUUID" parameterType="java.util.UUID"
resultMap="userResultMap">
SELECT * FROM user WHERE uuid = #{uuid, jdbcType=VARCHAR,
typeHandler=[...].persistence.typehandlers.UuidTypeHandler}
</select>
<resultMap id="userResultMap" type="[...].model.User">
<id property="uuid" column="uuid" jdbcType="OTHER"
typeHandler="[...].persistence.typehandlers.UuidTypeHandler"/>
...
</resultMap>
</mapper>
anyway, i got this exception:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause:
org.apache.ibatis.reflection.ReflectionException: There is no getter
for property named 'uuid' in 'class java.util.UUID'
### The error may involve
[...].persistence.mappers.UserMapper.selectUserByUUID-Inline
### The error occurred while setting parameters
### Cause: org.apache.ibatis.reflection.ReflectionException: There is
no getter for property named 'uuid' in 'class java.util.UUID'
it seems that my typehandler never gets called (i have it logging a
bit, but never prints anything).
Is there something wrong? Thanks.
Your problem is right there in your exception....
There is no getter for property named 'uuid' in 'class java.util.UUID'
Use a parameter type of String, and pass in unique id as an argument. You don't need a type handler.
Seems kinda odd answering my own question, but i got some help on the mybatis-users list, so i'd like to share some hints here:
Mybatis was trying to "get" a non-existing field:
Luckily, one of mybatis developers helped me a while ago, suggesting that the easiest way to get it to work was to add a #Param annotation in the UserMapper to do this thing:
public User selectUserByUUID(#Param("uuid") UUID uuid);
<select id="selectUserByUUID" parameterType="uuid" resultMap="userResultMap">
SELECT * FROM user WHERE uuid = #{uuid, typeHandler=com.collective.persistence.typehandlers.UuidTypeHandler, javaType=uuid, jdbcType=VARCHAR}
</select>
I was stuck in watching the typehandler configuration, but,
if I understood it right, this has not much to do with typehandlers,
since they are used to "transport and translate" data between the sql
table and the java objects (through setter and getters), while my
issue was in the handling of methods' parameters. Hope this helps someone.
Also such error could be fixed by adding in mapper file new method with corresponding queue
Smth like this:
#Select("SELECT * FROM Users u WHERE u.userUUID = '${uuid}'")
User selectByUserUuid(#Param("uuid") UUID uuid);
I'm trying to internationalize a test application with GWT following the instruction and I have:
com.example.client.MyConstants.java
com.example.client.MyConstants_en.properties
com.example.client.MyConstants_fr.properties
com.example.client.MyAppEntryPoint.java
In this code I have:
public interface MyConstants extends Constants
{
#DefaultStringValue("HelloWorld")
String hellowWorld();
}
And
public class MyAppEntryPoint implements EntryPoint
{
public void onModuleLoad()
{
MyConstants constants = GWT.create(MyConstants.class);
VerticalPanel mainPanel = new VerticalPanel();
mainPanel.add(new Label(constants.hellowWorld()));
RootPanel.get("myContainer").add(mainPanel);
}
}
For MyApp.gwt.xml I have:
<module rename-to="myModule">
<inherits name="com.google.gwt.xml.XML" />
<inherits name="com.google.gwt.i18n.I18N"/>
<inherits name='com.google.gwt.user.theme.standard.Standard'/>
<!-- Specify the app entry point class. -->
<entry-point class='com.example.client.MyAppEntryPoint'/>
<extend-property name="locale" values="en,fr"/>
</module>
In the html I have:
...
It all seems to work as long as I don't include in the xml file. As soon as I do, I get the following exception:
[ERROR] Generator 'com.google.gwt.i18n.rebind.LocalizableGenerator' threw threw an exception while rebinding 'com.example.client.myConstants'
java.lang.NullPointerException: null
...
Any help would be greatly appreciated on why it's throwing the exception.
-
The answer is that the module name has to be the same as the properties name. So if I use:
<module rename-to="MyApp">
Then the properties files need to be:
com.example.client.MyAppConstants.java
com.example.client.MyApp_en.properties
com.example.client.MyApp_fr.properties
In other words, the module name has to be the same as the properties files.
It could be the extend-property doesn't accept multiple values. I think you should write:
<extend-property name="locale" values="en"/>
<extend-property name="locale" values="fr"/>
After trying different options I figured, you don't need "en" option, I guess because that is the default hence you need:
com.example.client.MyAppConstants.java (Interface)
com.example.client.MyAppConstants.properties (Default)
com.example.client.MyAppConstants_fr.properties (Other language)
Hopefully this helps someone else's time.
I have an action with an empty string for name defined in the root namespace, and I want to redirect to that action from another action if a certain result is found, but it doesn't seem to work.
Here's the default action
<action name="" class="com.example.actions.HomeAction">
<result name="success" type="freemarker">freemarker/home.ftl</result>
</action>
And I'm defining the redirect in the global-results for the package:
<global-results>
<result name="sendToRouting" type="redirectAction">
<param name="actionName"></param>
<param name="namespace">/</param>
</result>
</global-results>
I've tried taking out the actionName parameter, but that doesn't work. If I put a name in for the HomeAction and reference it by name in the global-results it works, so I'm assuming the problem is lack of action name for the redirect.
Any thoughts?
I think that what you want to do is use <default-action-ref />:
<package name="home" namespace="/" extends="struts-default">
<default-action-ref name="home" />
<action name="home" class="com.example.actions.HomeAction">
<result name="success" type="freemarker">freemarker/home.ftl</result>
</action>
</package>
Sorry...misread the question:
Try changing type="redirectAction" to type="redirect", I'm fairly sure that redirectAction is now redirect.
Were you getting a NullPointerException because the actionName parameter is blank? I hacked around this by providing a string which evaluates to an empty string:
<param name="actionName">${}</param>
Still looking for a more "correct" solution though...