Skip to content
This repository has been archived by the owner on Jul 14, 2022. It is now read-only.
/ Gplume Public archive

Light-weight multiparadigmatic Java web framework

License

Notifications You must be signed in to change notification settings

xkommando/Gplume

Repository files navigation

Gplume

Light-weight multiparadigmatic Java web framework.

Features:

  • easy to learn and use
  • mixed Spring styled and Struts2 styled request handling
  • high performance.
  • cross cloud.

Quick Start: #####Part Zero: maven dependency

<dependency>
	<groupId>com.caibowen.gplumeframwork</groupId>
	<!-- web developement extensions-->
	<artifactId>gplume-webex</artifactId>
	<version>1.0</version>
</dependency>

#####Part One: Configurate the web.xml:

<context-param>
	<param-name>manifest</param-name>
	<param-val>classpath:app-manifest.xml</param-val><!-- in your class path -->
</context-param>
<listener>
	<listener-class>com.caibowen.gplume.web.WebAppBooter</listener-class>
</listener>

<filter>
	<filter-name>dispatcher</filter-name>
	<filter-class>com.caibowen.gplume.web.GFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>dispatcher</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

#####Part Two: The IoC Container. Spring-like configuration:

<beans namespace="MyApp::internal::storage" using="internal::api">

  <config>some_other_config.xml</config>
  <config>so_many_configs.xml</config>

<!--define global properties-->
  <define scope="global">
    <gplumeVersion>1.0.0.nightly</gplumeVersion>
    <webErrorHandler>com.caibowen.web.misc.ErrorHandler</webErrorHandler>
  </define>

<!-- default scope: current file -->
  <define import="kv_pairs_of_xml_format.xml" scope="file"/>
<!-- all properties is accessible at run time -->

  <bean class="com.caibowen.gplume.web.WebConfig" aftercall="afterPropertySet">
    <construct> <!--constructor injection -->
        <list>
            <bean class="some.class"/>
            <ref>MyApp::OtherNS::someOtherBean</ref>
            <val>literal</val>
        </list>
    </construct>
    <prop name="preProcessor" ref="headPrePrcessor"/>
    <prop name="errorHandler" instance="${webErrorHandler}"/>
    <prop name="pkgs">
        <list>
            <val>com.caibowen.web.controller</val>
        </list>
    </prop>
  </bean>
<!-- support dynamic proxy-->
  <bean class="com.sample.MyInterface" proxy="com.sample.MyInvokeHanlder">
    <construct name="what is this" value="invokeHanlderConstructor"/>
  </bean>
<beans>

#####Part Three: Internationalization. add language packages en.properties where there is gplumeIsRunning=Gplume is Running! and zh_CN.properties where there is gplumeIsRunning=Gplume \u8DD1\u8D77\u6765\u4E86\uFF01(ascii for Gplume跑起来了)

specify them in the manifest.xml

<!-- hot swap service. in production mode, use WebI18nService-->
<bean id="i18nService" class="com.caibowen.gplume.web.i18n.HotSwapWebI18n">
	<construct>
		<map>
			<en type="String">${lang_cn}</en> <!--tag name as key -->
			<zh_CN>/${i18base}/${i18_cn}</zh_CN>
			<key name="third key" type="Date">${date}</key> <!-- specify key name and value type -->
		</map>
	</construct>
	<prop name="defaultLang" value="SimplifiedChinese"/>
	<prop name="${time_zone_name}" value="ETC/GMT-8"/>
</bean>

use message tag in JSP

<label>
	<gp:msg k="gplumeIsRunning" />
</label>

#####Part Four: Gplume JDBC Operations

jdbcSupport.setDataSource(dataSource);
List<String> ids = jdbcSupport.batchInsert(new StatementCreator() {
    @Override
    public PreparedStatement createStatement(@Nonnull Connection con) throws SQLException {
        PreparedStatement ps = con.prepareStatement(
                "INSERT INTO `model_talbe`(`id, `name`)VALUES (?,?)");
        for (Model model : models) {
            ps.setString(1, model.getId());
            ps.setString(2, model.getName());
            ps.addBatch();
        }
        return ps;
    }
}, new String[]{"id"}, RowMapping.STR_ROW_MAPPING) ;

List<Model> ls = jdbcSupport.queryForList(new StatementCreator() {
        @Override
        public PreparedStatement createStatement(@Nonnull Connection con) throws SQLException {
            PreparedStatement ps = con.prepareStatement("SELECT * FROM `table_model`");
            return ps;
        }
    }, new RowMapping<Model>() {
        @Override
        public Model extract(@Nonnull ResultSet rs) throws SQLException {
            Model model = new Model();
            model.setId(rs.getString(1))
            model.setName(rs.getString(2));
            return model;
        }
    });

jdbcSupport.execute(new TransactionCallback<Object>() {
    @Override
    public Object withTransaction(@Nonnull Transaction tnx) throws Exception {
        tnx.setRollbackOnly(true);
        // operation 1
        // operation 2
        jdbcSupport.execute(new TransactionCallback<Object>() {
            @Override // nested transaction is isolated from the outer one
            public Object withTransaction(@Nonnull Transaction tnx) throws Exception {
                tnx.setRollbackOnly(true);
                throw new Exception();
            }
            });
            // fail on any of the operations will trigger rollback automatically
        return null;
    }
});

#####Part Five: ORM using Hibernate. Spring and Hibernate can be integrated to Gplume with just a few lines configuration:

<!-- will set sessionFactory bean with id ${sessionFactoryID} in afterPropertiesSet() -->
<bean class="com.caibowen.gplume.sample.test.SessionFactoryBuilder">
	<prop name="sessionFactoryID" value="sessionFactory"/>
	<prop name="dataSource" ref="dataSource" />
	<prop name="hibernateProperties">
		<map>
			<hibernate.dialect>org.hibernate.dialect.MySQL5Dialect</hibernate.dialect>
		</map>
	</prop>
	<prop name="annotatedClasses">
		<list> <!-- set prop "Class[] annotatedClasses" -->
			<val>com.caibowen.gplume.sample.model.Chapter</val>
		</list>
	</prop>
</bean>
<!-- use HibernateDaoSupport-->
<bean id="chapterDao" class="com.caibowen.gplume.sample.dao.ChapterDAO">
	<prop name="sessionFactory" ref="sessionFactory" />
</bean>

and the ChaperDAO

public class ChapterDAO extends HibernateDaoSupport {
	public List<Chapter> getAll() {
		return getHibernateTemplate().find("from Chapter");
	}
}

#####Part Six. Handling Event. register listeners and publish events as:

AppContext.broadcaster.register(new IEventHook() {
@Override
public void catches(AppEvent event) {
		LOG.info("cought event {0} from source {1}"
				, event.getClass().getSimpleName()
				, event.getSource());
	}
}, false);//set false to keep a weak reference to this listener
AppContext.broadcaster.register(new IAppListener<TimeChangedEvent>() {
@Override
public void onEvent(TimeChangedEvent event) {
		LOG.info("time changed {0}", event.getTime());
	}
});// default is true
TimeChangedEvent event = new TimeChangedEvent(this);
event.setTime(new Date());
AppContext.broadcaster.broadcast(event);

#####Part Seven. Test with Junit

@RunWith(JunitPal.class)
@ManifestPath("file:src/manifest.xml") // read as file
public class TestDemo {
	@Named("controlCenter")
	public AbstractControlCenter controlCenter;
	@Test
	public void test() {
		System.out.println(controlCenter);
		AppContext.broadcaster.broadcast(new WebAppStartedEvent(this));
	}
}

#####Part Eight: Gplume Web MVC Pre-process and after-process requests

public class SampleProcessor implements IRequestProcessor {
	@Override // all requests go through this
	public void process(RequestContext context) {
		before(context);
		getNext().process(context);
		after(context);
	}

Chaining processors in XML

<bean id="headPrePrcessor" class="com.caibowen.gplume.web.i18n.NativePkgInjector">
	<construct ref="i18nService"/>
	<prop name="next">
		<bean class="com.caibowen.web.plugin.ui.BackgroundImgPreprocessor">
			<prop name="next" instance="com.caibowen.web.plugin.statistic.LogRequestPreProcessor"/>
		</bean>
	</prop>
</bean>
<!--config view resolver-->
<bean class="com.caibowen.gplume.web.WebConfig">
	<prop name="preProcessor" ref="headPrePrcessor"/>
	<prop name="pkgs">
		<list>
			<val>com.caibowen.gplume.sample</val>
		</list>
	</prop>
	<!--by default using Jsp view resolver-->
	<prop name="viewPrefix" value="/"/>
	<prop name="viewSuffix" value=".jsp" />
	<!-- IView Resolver -->
	<prop name="ViewResolver"/>
		<bean class="com.caibowen.gplume.webex.json.JsonViewResolver">
			<prop name="doPrettyPrint" value="true"/>
		</bean>
	</prop>
</bean>

Handle request with method

// Support arbitrary return type. request handle is built and bind to custom view resolvers at server set-up, no overhead at runtime
class MyStrViewResolver implements IViewResolver;
class FreeMarkerResolver implements IViewResolver;
// controller
@Handle(value={"/", "/index",
	"/index.html", "/index.jsp"}})
public String index(RequestContext context) {
	context.putAttr("msg", nativeStr("gplumeIsRunning", context));
	return "index"; 
}
	//number of concurrent requests is limited by this semapher
@Semaphored(permit=100, fair=false, timeout=1000)// when time is out, send http code 503 
@Handle(value={"/your-birthday/{date formate like 1992-6-14::Date}"}
	, httpMethods={HttpMethod.GET, HttpMethod.POST})
public FreeMarkerView happyBirthday(Date date) {
	process(date);
	return new FreeMarkerView(date);
}

Handle request with an object storing current state

//Warn: insecure login controller, for demo only
@Controller("/async/")// the base path
public class MyAction {

	@ReqAttr(value="alias", defaultVal="1992-6-14")
	Date testdata1;
	@CookieVal(defaultVal=" 2.457  ", nullable=false)
	double testdata2;
	@ReqParam("psw_cipher")
	String passwordCipher;
	@ReqParam(value="email_address", nullable = false)
	String email;
	@SessionAttr(value="this_pubkey",nullable = false)
	PublicKey key;

	@Inject // injected after instanciation 
	@Named("myUserService")
	UserService userService;
	@Inject Validator validator;
	@Inject PublicKeyService keyService;

	User user;
	boolean ok() {
		String psw = keyService.decrypt(key, passwordCipher);
		if (!Str.Utils.notBlank(psw)
				||!validator.matches(email, psw))
			return false;
		else {
			user = userService.getByEmail(email);
			return true;
		}
	}
	
	@Semaphored(permit=300, timeout=2200)
	@Handle(value={"login"}, httpMethods={HttpMethod.POST})
	public SomePoJo login(MyAction reqScope, RequestContext req) {
		if (reqScope == null) //non-null requirements are not met.
			return IView.get.textView("no public key in session");
		else if (!reqScope.ok())
			return Views.textView("password and email mismatch");
		else {
			req.session(true).setAttribute("this-user", reqScope.user);
			return Jump.to("/user/" + reqScope.user.getNameURL());
		}
	}
}

Intercept requests

@Intercept(value = { "/user*" }) // intercept request by URL
public void demo(RequestContext context, IAction action) throws Throwable {
	if (hasLoggedIn(context)) {
		action.perform(context);
		after(context);
	} else {
		context.jumpTo("/login");
	}
}

===========


Gplume Overview alt text


About

Light-weight multiparadigmatic Java web framework

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published