Web App (Spring, Angular, Grunt, Maven, Tomcat) running both grunt and tomcat servers

 ̄綄美尐妖づ 提交于 2019-12-02 21:14:57
oak

from your question i digged up only two questions

  1. How to do continuous development with grunt/bower + tomcat
  2. How to deploy for production

1 - Continuous development

The solution i have chosen for that - since you already use api to communicate client <-> server- is to separate completely the two projects.

So what does it mean? for me is to have two different repositories. One for client, One for Server one this way you get few benefits:

  • Split the work on the projects (front-end/server-side)
  • Easier to maintain
  • In case you want to support "API only" For an example, mobile application, and etc..

But - How do they communicate while developing?

This is a good question: One solution is to run two server on localhost in parallel, i.e mvn clean tomcat:run -P yourprofile; grunt server

But - I'll get cross domain if I try to access server-side from client-side from different port? You are right. And here you get the help of grunt and its plugin. grab a copy of grunt-connect-proxy

What is nice about this plugin that it acts as a middleware between grunt server and tomcat server so you ask grunt server for the API but actually grunt is asking tomcat server to answer on that (behind the scenes of course)

2 - Deploy for production

I guess this is a matter of personal preference question. I find the war file extremely big to do upload again and again (even if are able to share lib between all of your tomcat app). The solution I came up with is to do deploy over git.

OK, but I have one big war file. How can I do that?

For me I use a deploy script I wrote in bash. This is what it does:

  1. Tag the current source
  2. Run mvn clean package war:exploded -P your-prod-profile (this will run test and integration test as well)
  3. With the above command you get all your complied project's file in one place, instead of one big war file.
  4. Copy all those files (and inner paths) into outside folder (I use another repository for maintenance deployment over git. So basically I have 3 repositories. One for server source, one for client source, and one for server binaries.)
  5. Before doing 4 make sure to delete all files and folders (besides .git stuff) from it
  6. After 4 do "git add -A"
  7. "git commit -a -m 'new production version X"
  8. You can mark some tags before and after for allowing easy way of recovering the last code if there was a big bug in the new production
  9. Run remote command on server to a.) stop server, b.) pull the last changes from the binary repository, c.) run server again.
  10. For me what I did is a symbolic link between tomcat app to an outside folder (the binary repository) so

Hope this will give you some directions,

Bests, Oak

OK so i wanted to post a solution that works well for my local development, and has allowed me to use the desired approach...is to do as Oak stated (sorry I'm not sure how to link his username) and have two separate builds/projects. However, my front-end project uses grunt to serve up my code on a specific port, with some middleware that directs requests on the port to either the front-end code, or to the server running spring-boot. This allows me to act as if the code is really running on the same project and avoid any CORS and other issues from running them on different domains/servers. here's the section of code in my grunt build that allows me to do that:

livereload: {
    options: {
      debug: true,
      middleware: function (connect, options) {
        var middlewares = [];

        middlewares.push(rewriteModule.getMiddleware([
          //Load App under context-root of 'myappcontext/secured'
          {from: '^/napiweb/(.*)$', to: '/$1'},

          //Redirect slash to myappcontext/secured as convenience
          {from: '^/$', to: '/napiweb', redirect: 'permanent'}

          //Send a 404 for anything else
          //{from: '^/.+$', to: '/404'}
        ]));

        if (!Array.isArray(options.base)) {
          options.base = [options.base];
        }

        options.base.forEach(function () {
          // Serve static files.
          middlewares.push(connect.static('.tmp'),
            connect().use(
              '/bower_components',
              connect.static('./bower_components')
            ),
            connect.static(appConfig.app));
        });

        // Make directory browse-able.
        //middlewares.push(connect.directory(directory));

        return middlewares;
      }
    }
  },

Then I configured my spring-boot to have a proxy for local development that forwards specific requests to the front-end. It is set up as follows: in my config.xml file

<config proxy-port="{{http-port}}" console-port="1776">
  <console-recording sso="true" rest="true" max-entries="100" enable-debug- logging='true'/>
  <sso-cookie name="wamulator" domain=".somedomain.com" session-timeout-seconds="1800"/>
  <port-access local-traffic-only="false"/>
  <sso-traffic strip-empty-headers="true">
     <by-site host="localhost.somedomain.com" port="{{http-port}}">

       <cctx-mapping thost="127.0.0.1" tport="8081">
         <policy-source>xml={{policy-src-xml}}</policy-source>
       </cctx-mapping>
       <cctx-mapping thost="127.0.0.1" tport="9000">
         <policy-source>xml={{static-src-xml}}</policy-source>
       </cctx-mapping>
       <cctx-mapping thost="127.0.0.1" tport="8180">
         <policy-source>xml={{napi-src-xml}}</policy-source>
       </cctx-mapping>
     </by-site>
  </sso-traffic>

 <user-source type='xml'>xml={{usr-src-xml}}</user-source>
 <proxy-timeout inboundMillis="0" outboundMillis="0" />
</config> 

As you can see the cctx mapping will direct some request to the front end being served up on port 9000 and some to the backend serving up the APIs.This is based off the policy-config.xml and static-config.xml files. They are almost exactly the same, and the only difference is in the authHost and cctx setting here's one for an example:

<deployment at='2013-01-31_16:25:12.205-0700'>
   <environment id='dev' host='dev.somedomain.com (exposee)'/>
   <application id='napi-rest' authHost='localhost.somedomain.com/napiweb/api' cctx='/napiweb/api'>
   <authentication scheme='anonymous' name='Anonymous Authentication'> </authentication>
   <authorization failure-redirect-url='/denied.html'>
   <default format='exposee' value='Allow Authenticated Users'>
     <headers>
       <success>
         ...profile-att specific for my organization
       </success>
       <failure>
         <redirect value='/denied.html'/>
       </failure>
     </headers>
  </default>
  <rule name='Allow Authenticated Users' enabled='true' allow-takes-precedence='false'>
    <allow>
      <condition type='role' value='Anyone'/>
    </allow>
  </rule>
</authorization>

The only other difference is the other file has authHost='localhost.somedomain.com/napiweb/' cctx='/napiweb/' This causes the APIs to be called and the front-end to be called as if they are served up from the same project. Then when we push the projects up to our repositories, we have two build cycles. One takes the front end and creates static assets using grunt build and then copies those files over to the rest server so it can serve them up. This allows us to have separate projects for development but a single server serving up our site. Not ideal...as ultimately I think we should have separate servers/instances for the front and back, but since we weren't allowed to do so, this allowed us to act as if we did during development. I hope this helps someone.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!